implementation of drecon in unity 2022 lts forked from: https://github.com/joanllobera/marathon-envs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

357 lines
13 KiB

9 months ago
using UnityEngine;
public class AnimationController : MonoBehaviour, IAnimationController
{
public float MaxForwardVelocity = 1f; // Max run speed.
public float MinTurnVelocity = 400f; // Turn velocity when moving at maximum speed.
public float MaxTurnVelocity = 1400f; // Turn velocity when stationary.
public float JumpSpeed = 5f; //
public bool debugForceJump;
[SerializeField]
Animator _anim;
[SerializeField]
CharacterController _characterController;
[SerializeField]
InputController _inputController;
bool _isGrounded;
bool _previouslyGrounded;
const float kAirborneTurnSpeedProportion = 5.4f;
const float kGroundTurnSpeedProportion = 200f / 2;
const float kGroundedRayDistance = 1f;
const float kJumpAbortSpeed = 10f;
const float kInverseOneEighty = 1f / 180f;
const float kStickingGravityProportion = 0.3f;
float _forwardVelocity;
Vector3 _lastGroundForwardVelocity;
float _desiredForwardSpeed;
float _verticalVelocity = -1f;
Quaternion _targetDirection; // direction we want to move towards
float _angleDiff; // delta between targetRotation and current roataion
Quaternion _targetRotation;
bool _readyToJump;
bool _inCombo;
[SerializeField]
Material materialUnderFoot;
public LayerMask IgnoreLayers;
[Tooltip("for debugging, we disable this when setTpose in MarathonTestBedController is on")]
public bool doFixedUpdate = true;
protected bool IsMoveInput
{
get { return !Mathf.Approximately(_inputController.MovementVector.sqrMagnitude, 0f); }
}
public void OnEnable()
{
OnAgentInitialize();
}
//TODO: this is also called from RagdollAgent004. Dependency should be removed, somehow.
public void OnAgentInitialize()
{
if (!_anim)
_anim = GetComponent<Animator>();
if (!_characterController)
_characterController = GetComponent<CharacterController>();
if (!_inputController) //if it is used without a ragdoll agent (for example, for ROM extraction), we still need to initialize it
_inputController = GetComponent<InputController>();
_targetDirection = Quaternion.Euler(0, 90, 0);
//Moved to Training Environment Generator
/*
var ragDoll = _spawnableEnv.GetComponentInChildren<RagDollAgent>( true);//we include inactive childs
if (ragDoll)//in the ROM extraction case we do not have any ragdoll agent
{
_layerMask = 1 << ragDoll.gameObject.layer;
_layerMask |= 1 << this.gameObject.layer;
_layerMask = ~(_layerMask);
}
*/
}
public Vector3 GetDesiredVelocity()
{
Vector3 desiredVelocity = new Vector3(
_inputController.DesiredHorizontalVelocity.x,
0f,
_inputController.DesiredHorizontalVelocity.y);
return desiredVelocity;
}
void FixedUpdate()
{
OnFixedUpdate();
}
void OnFixedUpdate()
{
// RotateTarget(Time.fixedDeltaTime);
SetTargetFromMoveInput();
CalculateForwardMovement(Time.fixedDeltaTime);
CalculateVerticalMovement(Time.fixedDeltaTime);
if (this.IsMoveInput)
SetTargetRotation();
UpdateOrientation(Time.fixedDeltaTime);
// PlayAudio();
// TimeoutToIdle();
_previouslyGrounded = _isGrounded;
}
public void OnReset()
{
}
void OldOnReset()
{
_isGrounded = true;
_previouslyGrounded = true;
_inCombo = false;
_readyToJump = false;
_forwardVelocity = 0f;
_lastGroundForwardVelocity = Vector3.zero;
_desiredForwardSpeed = 0f;
_verticalVelocity = 0f;
_angleDiff = 0f;
if (!doFixedUpdate)
return;
_anim.SetBool("onGround", _isGrounded);
// _anim.SetFloat("verticalVelocity", _verticalVelocity);
_anim.SetFloat("angleDeltaRad", _angleDiff * Mathf.Deg2Rad);
_anim.SetFloat("forwardVelocity", _forwardVelocity);
_anim.SetBool("backflip", false);
_anim.Rebind();
_anim.SetBool("onGround", _isGrounded);
// _anim.SetFloat("verticalVelocity", _verticalVelocity);
_anim.SetFloat("angleDeltaRad", _angleDiff * Mathf.Deg2Rad);
_anim.SetFloat("forwardVelocity", _forwardVelocity);
_anim.SetBool("backflip", false);
OnFixedUpdate();
_anim.Update(0f);
}
// Called each physics step (so long as the Animator component is set to Animate Physics) after FixedUpdate to override root motion.
void OnAnimatorMove()
{
if (_anim == null)
return;
Vector3 movement;
float verticalVelocity = _verticalVelocity;
if (_isGrounded)
{
// find ground
RaycastHit hit;
var rayStart = transform.position + ((Vector3.up * kGroundedRayDistance) * 0.5f);
Ray ray = new Ray(rayStart, -Vector3.up);
if (Physics.Raycast(ray, out hit, kGroundedRayDistance, ~IgnoreLayers, QueryTriggerInteraction.Ignore))
{
// project velocity on plane
movement = _anim.deltaPosition;
movement.y = 0f;
movement = Vector3.ProjectOnPlane(_anim.deltaPosition, hit.normal);
// store material under foot
Renderer groundRenderer = hit.collider.GetComponentInChildren<Renderer>();
materialUnderFoot = groundRenderer ? groundRenderer.sharedMaterial : null;
}
else
{
// fail safe incase ray does not collide
movement = _anim.deltaPosition;
materialUnderFoot = null;
}
_lastGroundForwardVelocity = movement / Time.fixedDeltaTime;
}
else
{
movement = _lastGroundForwardVelocity * Time.fixedDeltaTime;
}
// Rotate the transform of the character controller by the animation's root rotation.
_characterController.transform.rotation *= _anim.deltaRotation;
// print ($"delta:{_anim.deltaPosition.magnitude} movement:{movement.magnitude} delta:{_anim.deltaPosition} movement:{movement}");
// Add to the movement with the calculated vertical speed.
movement += verticalVelocity * Vector3.up * Time.fixedDeltaTime;
// Move the character controller.
_characterController.Move(movement);
// After the movement store whether or not the character controller is grounded.
_isGrounded = _characterController.isGrounded;
// If Ellen is not on the ground then send the vertical speed to the animator.
// This is so the vertical speed is kept when landing so the correct landing animation is played.
if (!_isGrounded)
_anim.SetFloat("verticalVelocity", verticalVelocity);
// Send whether or not Ellen is on the ground to the animator.
_anim.SetBool("onGround", _isGrounded);
}
void RotateTarget(float deltaTime)
{
if (!Mathf.Approximately(_inputController.CameraRotation.x * _inputController.CameraRotation.x, 0f))
{
float roation = _targetDirection.eulerAngles.y;
float delta = _inputController.CameraRotation.x * kGroundTurnSpeedProportion * deltaTime;
roation += delta;
// print($"{_targetDirection.eulerAngles.y} delta:{delta}, {roation}");
_targetDirection = Quaternion.Euler(0f, roation, 0f);
}
}
void SetTargetFromMoveInput()
{
if (!_inputController) //if it is used without a ragdoll agent (for example, for ROM extraction), we still need to initialize it
OnAgentInitialize();
Vector2 moveInput = _inputController.MovementVector;
Vector3 localMovementDirection = new Vector3(moveInput.x, 0f, moveInput.y).normalized;
_targetDirection = Quaternion.Euler(localMovementDirection);
}
void SetTargetRotation()
{
// Create three variables, move input local to the player, flattened forward direction of the camera and a local target rotation.
Vector2 moveInput = _inputController.MovementVector;
Vector3 localMovementDirection = new Vector3(moveInput.x, 0f, moveInput.y).normalized;
Vector3 forward = _targetDirection * Vector3.forward;
forward.y = 0f;
forward.Normalize();
Quaternion targetRotation;
// // If the local movement direction is the opposite of forward then the target rotation should be towards the camera.
// if (Mathf.Approximately(Vector3.Dot(localMovementDirection, Vector3.forward), -1.0f))
// {
// targetRotation = Quaternion.LookRotation(-forward);
// }
// else
// {
// // Otherwise the rotation should be the offset of the input from the camera's forward.
// Quaternion cameraToInputOffset = Quaternion.FromToRotation(Vector3.forward, localMovementDirection);
// targetRotation = Quaternion.LookRotation(cameraToInputOffset * forward);
// }
// targetRotation = Quaternion.LookRotation(-forward);
Quaternion cameraToInputOffset = Quaternion.FromToRotation(Vector3.forward, localMovementDirection);
targetRotation = Quaternion.LookRotation(cameraToInputOffset * forward);
// The desired forward direction.
Vector3 resultingForward = targetRotation * Vector3.forward;
// Find the difference between the current rotation of the player and the desired rotation of the player in radians.
float angleCurrent = Mathf.Atan2(transform.forward.x, transform.forward.z) * Mathf.Rad2Deg;
float targetAngle = Mathf.Atan2(resultingForward.x, resultingForward.z) * Mathf.Rad2Deg;
_angleDiff = Mathf.DeltaAngle(angleCurrent, targetAngle);
_targetRotation = targetRotation;
}
void UpdateOrientation(float deltaTime)
{
_anim.SetFloat("angleDeltaRad", _angleDiff * Mathf.Deg2Rad);
Vector3 localInput = new Vector3(_inputController.MovementVector.x, 0f, _inputController.MovementVector.y);
float groundedTurnSpeed = Mathf.Lerp(MaxTurnVelocity, MinTurnVelocity, _forwardVelocity / _desiredForwardSpeed);
float actualTurnSpeed = _isGrounded ? groundedTurnSpeed : Vector3.Angle(transform.forward, localInput) * kInverseOneEighty * kAirborneTurnSpeedProportion * groundedTurnSpeed;
_targetRotation = Quaternion.RotateTowards(transform.rotation, _targetRotation, actualTurnSpeed * deltaTime);
bool hasNan = float.IsNaN(_targetRotation.x) || float.IsNaN(_targetRotation.y) || float.IsNaN(_targetRotation.z);
if (!hasNan)
transform.rotation = _targetRotation;
}
void CalculateForwardMovement(float deltaTime)
{
// Cache the move input and cap it's magnitude at 1.
Vector2 moveInput = _inputController.MovementVector;
if (moveInput.sqrMagnitude > 1f)
moveInput.Normalize();
// Calculate the speed intended by input.
_desiredForwardSpeed = moveInput.magnitude * MaxForwardVelocity;
// Note: acceleration is handle in InputController
_forwardVelocity = _desiredForwardSpeed;
// Set the animator parameter to control what animation is being played.
_anim.SetFloat("forwardVelocity", _forwardVelocity);
}
void CalculateVerticalMovement(float deltaTime)
{
// If jump is not currently held and is on the ground then ready to jump.
if (!_inputController.Jump && _isGrounded)
_readyToJump = true;
_anim.SetBool("backflip", _inputController.Backflip);
if (_isGrounded)
{
// When grounded we apply a slight negative vertical speed to make Ellen "stick" to the ground.
_verticalVelocity = Physics.gravity.y * kStickingGravityProportion;
// If jump is held, Ellen is ready to jump and not currently in the middle of a melee combo...
if (_inputController.Jump && _readyToJump && !_inCombo)
{
// ... then override the previously set vertical speed and make sure she cannot jump again.
_verticalVelocity = JumpSpeed;
_isGrounded = false;
_readyToJump = false;
_anim.SetBool("onGround", false);
}
}
else
{
// If Ellen is airborne, the jump button is not held and Ellen is currently moving upwards...
if (!_inputController.Jump && _verticalVelocity > 0.0f)
{
// ... decrease Ellen's vertical speed.
// This is what causes holding jump to jump higher that tapping jump.
_verticalVelocity -= kJumpAbortSpeed * deltaTime;
}
// If a jump is approximately peaking, make it absolute.
if (Mathf.Approximately(_verticalVelocity, 0f))
{
_verticalVelocity = 0f;
}
// If Ellen is airborne, apply gravity.
_verticalVelocity += Physics.gravity.y * deltaTime;
}
}
}