MythicalWar/Assets/Invector-3rdPersonController/Basic Locomotion/Scripts/CharacterController/vThirdPersonAnimator.cs

414 lines
15 KiB
C#

using UnityEngine;
namespace Invector.vCharacterController
{
using vEventSystems;
public class vThirdPersonAnimator : vThirdPersonMotor
{
#region Variables
public float randomIdleCount;
public const float walkSpeed = 0.5f;
public const float runningSpeed = 1f;
public const float sprintSpeed = 1.5f;
public Vector3 lastCharacterPosition { get; protected set; }
#endregion
#if UNITY_EDITOR
[ContextMenu("Enable AnimatorStateInfos Debug")]
void EnableAnimatorStateInfosDebug()
{
if (animatorStateInfos != null)
{
animatorStateInfos.debug = true;
}
}
[ContextMenu("Disable AnimatorStateInfos Debug")]
void DisableAnimatorStateInfosDebug()
{
if (animatorStateInfos != null)
{
animatorStateInfos.debug = false;
}
}
#endif
protected override void Start()
{
base.Start();
RegisterAnimatorStateInfos();
}
protected virtual void RegisterAnimatorStateInfos()
{
animatorStateInfos = new vAnimatorStateInfos(GetComponent<Animator>());
animatorStateInfos.RegisterListener();
}
protected override void OnEnable()
{
base.OnEnable();
if (animatorStateInfos.animator != null)
{
animatorStateInfos.RegisterListener();
}
}
protected override void OnDisable()
{
base.OnDisable();
animatorStateInfos.RemoveListener();
}
public virtual void UpdateAnimator()
{
if (animator == null || !animator.enabled)
{
return;
}
AnimatorLayerControl();
ActionsControl();
TriggerRandomIdle();
CalculateRotationMagnitude();
UpdateAnimatorParameters();
DeadAnimation();
}
public virtual void AnimatorLayerControl()
{
baseLayerInfo = animator.GetCurrentAnimatorStateInfo(baseLayer);
underBodyInfo = animator.GetCurrentAnimatorStateInfo(underBodyLayer);
rightArmInfo = animator.GetCurrentAnimatorStateInfo(rightArmLayer);
leftArmInfo = animator.GetCurrentAnimatorStateInfo(leftArmLayer);
upperBodyInfo = animator.GetCurrentAnimatorStateInfo(upperBodyLayer);
fullBodyInfo = animator.GetCurrentAnimatorStateInfo(fullbodyLayer);
}
public virtual void ActionsControl()
{
// to have better control of your actions, you can assign bools to know if an animation is playing or not
// this way you can use this bool to create custom behavior for the controller
// identify if the rolling animations is playing
isRolling = IsAnimatorTag("IsRolling");
// identify if a turn on spot animation is playing
isTurningOnSpot = IsAnimatorTag("TurnOnSpot");
// locks player movement while a animation with tag 'LockMovement' is playing
lockAnimMovement = IsAnimatorTag("LockMovement");
// locks player rotation while a animation with tag 'LockRotation' is playing
lockAnimRotation = IsAnimatorTag("LockRotation");
// ! -- you can add the Tag "CustomAction" into a AnimationState and the character will not perform any Melee action -- !
customAction = IsAnimatorTag("CustomAction");
// identify if the controller is airborne
isInAirborne = IsAnimatorTag("Airborne");
}
public virtual void UpdateAnimatorParameters()
{
if (disableAnimations)
{
return;
}
animator.SetBool(vAnimatorParameters.IsStrafing, isStrafing);
animator.SetBool(vAnimatorParameters.IsSprinting, isSprinting);
animator.SetBool(vAnimatorParameters.IsSliding, isSliding && !isRolling);
animator.SetBool(vAnimatorParameters.IsCrouching, isCrouching);
animator.SetBool(vAnimatorParameters.IsGrounded, isGrounded);
animator.SetBool(vAnimatorParameters.IsDead, isDead);
animator.SetFloat(vAnimatorParameters.GroundDistance, groundDistance);
animator.SetFloat(vAnimatorParameters.GroundAngle, GroundAngleFromDirection());
if (!isGrounded)
{
animator.SetFloat(vAnimatorParameters.VerticalVelocity, verticalVelocity);
}
//if (!lockAnimMovement)
{
if (isStrafing)
{
animator.SetFloat(vAnimatorParameters.InputHorizontal, horizontalSpeed, strafeSpeed.animationSmooth, Time.fixedDeltaTime);
animator.SetFloat(vAnimatorParameters.InputVertical, verticalSpeed, strafeSpeed.animationSmooth, Time.fixedDeltaTime);
}
else
{
animator.SetFloat(vAnimatorParameters.InputVertical, verticalSpeed, freeSpeed.animationSmooth, Time.fixedDeltaTime);
animator.SetFloat(vAnimatorParameters.InputHorizontal, 0, freeSpeed.animationSmooth, Time.fixedDeltaTime);
}
inputMagnitude = Mathf.LerpUnclamped(inputMagnitude, 0f, stopMoveWeight);
animator.SetFloat(vAnimatorParameters.InputMagnitude, inputMagnitude, isStrafing ? strafeSpeed.animationSmooth : freeSpeed.animationSmooth, Time.fixedDeltaTime);
animator.SetFloat(vAnimatorParameters.RotationMagnitude, rotationMagnitude);
}
}
protected virtual void UpdateRotationMagnitude(float deltaTime)
{
bool moving = inputMagnitude >= 0.1f;
if (!(moving ? useLeanMovementAnim : useTurnOnSpotAnim))
{
rotationMagnitudeSmoothVelocity = 0f;
rotationMagnitude = 0f;
return;
}
float target = rawRotationMagnitude;
float dampTime = moving ? leanSmooth : turnOnSpotSmooth;
if (dampTime <= 0f || deltaTime <= 0f)
{
rotationMagnitudeSmoothVelocity = 0f;
rotationMagnitude = target;
return;
}
rotationMagnitude = Mathf.SmoothDamp(rotationMagnitude, target, ref rotationMagnitudeSmoothVelocity,
dampTime, Mathf.Infinity, deltaTime);
}
/// <summary>
/// Advances the rotationMagnitude damping for one rendered frame. Called from
/// <see cref="vThirdPersonInput.Update"/>, before the animation pose for the frame is sampled.
/// </summary>
public virtual void UpdateRotationMagnitudePerFrame()
{
if (animator == null || !animator.enabled)
{
return;
}
UpdateRotationMagnitude(Time.deltaTime);
}
public virtual void SetAnimatorMoveSpeed(vMovementSpeed speed)
{
Vector3 relativeInput = transform.InverseTransformDirection(moveDirection);
verticalSpeed = relativeInput.z;
horizontalSpeed = relativeInput.x;
var newInput = new Vector2(verticalSpeed, horizontalSpeed);
if (speed.walkByDefault || alwaysWalkByDefault)
{
inputMagnitude = Mathf.Clamp(newInput.magnitude, 0, isSprinting ? runningSpeed : walkSpeed);
}
else
{
var mag = newInput.magnitude;
sprintWeight = Mathf.Lerp(sprintWeight, isSprinting ? 1f : 0f, (isStrafing ? strafeSpeed.movementSmooth : freeSpeed.movementSmooth) * Time.fixedDeltaTime);
inputMagnitude = Mathf.Clamp(Mathf.Lerp(mag, mag + (newInput.magnitude > 0.1f ? 0.5f : 0), sprintWeight), 0, isSprinting ? sprintSpeed : runningSpeed);
}
}
public virtual void SetInputDirection(Vector3 direction)
{
float movementDirection = direction.magnitude >= .1 ? transform.forward.AngleFormOtherDirection(direction).y : 0;
if (vAnimatorParameters.InputDirection != -1) animator.SetFloat(vAnimatorParameters.InputDirection, movementDirection);
}
public virtual void ResetInputAnimatorParameters()
{
animator.SetBool(vAnimatorParameters.IsSprinting, false);
animator.SetBool(vAnimatorParameters.IsSliding, false);
animator.SetBool(vAnimatorParameters.IsCrouching, false);
animator.SetBool(vAnimatorParameters.IsGrounded, true);
animator.SetFloat(vAnimatorParameters.GroundDistance, 0f);
animator.SetFloat("InputHorizontal", 0);
animator.SetFloat("InputVertical", 0);
animator.SetFloat("InputMagnitude", 0);
}
protected virtual void TriggerRandomIdle()
{
if (input != Vector3.zero || customAction)
{
return;
}
if (randomIdleTime > 0)
{
if (input.sqrMagnitude == 0 && !isCrouching && _capsuleCollider.enabled && isGrounded)
{
randomIdleCount += Time.fixedDeltaTime;
if (randomIdleCount > 6)
{
randomIdleCount = 0;
animator.SetTrigger(vAnimatorParameters.IdleRandomTrigger);
animator.SetInteger(vAnimatorParameters.IdleRandom, Random.Range(1, 4));
}
}
else
{
randomIdleCount = 0;
animator.SetInteger(vAnimatorParameters.IdleRandom, 0);
}
}
}
protected virtual void DeadAnimation()
{
if (!isDead)
{
return;
}
// death by animation
if (deathBy == DeathBy.Animation)
{
if (TryGetDeadStateProgress(out float normalizedTime) && normalizedTime >= 0.99f && groundDistance <= 0.15f)
{
RemoveComponents();
}
}
// death by animation & ragdoll after a time
else if (deathBy == DeathBy.AnimationWithRagdoll)
{
if (TryGetDeadStateProgress(out float normalizedTime) && normalizedTime >= 0.8f)
{
onActiveRagdoll.Invoke(null);
}
}
// death by ragdoll
else if (deathBy == DeathBy.Ragdoll)
{
onActiveRagdoll.Invoke(null);
}
}
/// <summary>
/// Normalized progress of the death animation on whichever backend is driving the character,
/// excluding transitions. False while no death state is playing.
/// </summary>
protected virtual bool TryGetDeadStateProgress(out float normalizedTime)
{
normalizedTime = 0f;
#if INVECTOR_PLAYABLESANIMATOR
if (UsingPlayablesBackend)
{
return !playablesAnimator.IsInTransition(0)
&& playablesAnimator.IsPlayingState("Dead", out normalizedTime, 0);
}
#endif
var info = animatorStateInfos.GetStateInfoUsingTag("Dead");
if (info == null || animator.IsInTransition(baseLayer))
{
return false;
}
normalizedTime = info.normalizedTime;
return true;
}
#region Generic Animations Methods
public virtual void SetActionState(int value)
{
animator.SetInteger(vAnimatorParameters.ActionState, value);
}
public virtual bool IsAnimatorTag(string tag)
{
#if INVECTOR_PLAYABLESANIMATOR
if (playablesAnimator != null && playablesAnimator.isActiveAndEnabled)
{
return playablesAnimator.HasTag(tag);
}
#endif
if (animator == null)
{
return false;
}
if (animatorStateInfos != null)
{
if (animatorStateInfos.HasTag(tag))
{
return true;
}
}
if (baseLayerInfo.IsTag(tag))
{
return true;
}
if (underBodyInfo.IsTag(tag))
{
return true;
}
if (rightArmInfo.IsTag(tag))
{
return true;
}
if (leftArmInfo.IsTag(tag))
{
return true;
}
if (upperBodyInfo.IsTag(tag))
{
return true;
}
if (fullBodyInfo.IsTag(tag))
{
return true;
}
return false;
}
public virtual void MatchTarget(Vector3 matchPosition, Quaternion matchRotation, AvatarTarget target, MatchTargetWeightMask weightMask, float normalisedStartTime, float normalisedEndTime)
{
if (animator.isMatchingTarget || animator.IsInTransition(0))
{
return;
}
float normalizeTime = Mathf.Repeat(animator.GetCurrentAnimatorStateInfo(0).normalizedTime, 1f);
if (normalizeTime > normalisedEndTime)
{
return;
}
animator.MatchTarget(matchPosition, matchRotation, target, weightMask, normalisedStartTime, normalisedEndTime);
}
#endregion
}
public static partial class vAnimatorParameters
{
public static int InputHorizontal = Animator.StringToHash("InputHorizontal");
public static int InputVertical = Animator.StringToHash("InputVertical");
public static int InputMagnitude = Animator.StringToHash("InputMagnitude");
public static int RotationMagnitude = Animator.StringToHash("RotationMagnitude");
public static int TurnOnSpotDirection = Animator.StringToHash("TurnOnSpotDirection");
public static int ActionState = Animator.StringToHash("ActionState");
public static int ResetState = Animator.StringToHash("ResetState");
public static int IsDead = Animator.StringToHash("isDead");
public static int IsGrounded = Animator.StringToHash("IsGrounded");
public static int IsCrouching = Animator.StringToHash("IsCrouching");
public static int IsStrafing = Animator.StringToHash("IsStrafing");
public static int IsSprinting = Animator.StringToHash("IsSprinting");
public static int IsSliding = Animator.StringToHash("IsSliding");
public static int GroundDistance = Animator.StringToHash("GroundDistance");
public static int GroundAngle = Animator.StringToHash("GroundAngle");
public static int VerticalVelocity = Animator.StringToHash("VerticalVelocity");
public static int IdleRandom = Animator.StringToHash("IdleRandom");
public static int IdleRandomTrigger = Animator.StringToHash("IdleRandomTrigger");
public static int InputDirection = Animator.StringToHash("InputDirection");
}
}