Unity 3rd Person Combat & Traversal Ledge Climbing

I am following along with the tutorial for the climbing feature but my Pull Up state is transitioning to free look as soon as I hit the key despite using an if statement checking normalized time <1. If I comment out the line to switch states to free look then it completes the animation. Any ideas?

public class PlayerPullUpState : PlayerBaseState
{
    private readonly int PullUpHash = Animator.StringToHash("BasicLedgePullUp");
    private const float CrossFadeDuration = 0.1f;

    //Offset to move character controller to new position after pull up animation
    private readonly Vector3 Offset = new(0f, 2f, 1f);


    public PlayerPullUpState(PlayerStateMachine stateMachine) : base(stateMachine)
    {
    }

    public override void Entry()
    {
        stateMachine.Animator.CrossFadeInFixedTime(PullUpHash, CrossFadeDuration);

    }


    public override void Tick(float deltaTime)
    {
        if (stateMachine.Animator.GetCurrentAnimatorStateInfo(0).normalizedTime < 1f) { return; }

        Debug.Log("Transition to FreeLook");

        //In order to handle the root motion of the pullup animations, turn off controller to allow translate(), then renable
        stateMachine.Controller.enabled = false;
        stateMachine.transform.Translate(Offset, Space.Self);
        stateMachine.Controller.enabled = true;

        stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
    }

    public override void Exit()
    {
        stateMachine.Controller.Move(Vector3.zero);
        stateMachine.ForceReceiver.Reset();
    }
}

Here is a screen shot of my animation

After a few debug.logs it seems it was using the normalized time from the previous state (hanging). If I change the crossfade duration to zero for playing the animation, the if statement in the tick seems work correctly. Any suggestions how I could keep crossfade and get the normalizedTime to update before the first Tick?

Sorry about the delay on this one, I’ve added the unity-3rd-person tag to the post so I’ll see it going forward.

This is handled the same way that we handle the GetNormalizedTime() in Attack. Just calling Animator.GetCurrentAnimatorStateInfo(0).normalizedTime will always return the initial state’s normalized time if the animator is in transition.

To help solve this, Nathan promoted the GetNormalizedTime() method to the State class, so we can check it in other states against any given tag and use it in both Player and Enemy states

    protected float GetNormalizedTime(Animator animator, string tag)
    {
        AnimatorStateInfo currentInfo = animator.GetCurrentAnimatorStateInfo(0);
        AnimatorStateInfo nextInfo = animator.GetNextAnimatorStateInfo(0);
        
        if (animator.IsInTransition(0) && nextInfo.IsTag(tag))
        {
            return nextInfo.normalizedTime;
        }
        else if (!animator.IsInTransition(0) && currentInfo.IsTag(tag))
        {
            return currentInfo.normalizedTime;
        }
        else
        {
            return 0f;
        }
    }

Instead of calling stateMachine.anamator.GetCurrentAnimatorStateInfo(0).normalizedTime, instead use GetNormalizedTime("climbing) (and be sure to set the climbing state’s tag to “Climbing”)

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms