Jumping in Targeting State

As you should know by now, I am working on a bigger project using this as a base and integrating the RPG course stuff. I had already made heavy modifications as I was doing this course to allow a LOT more freedom. Doing things in FreeLook that were only shown in Targeting and vice versa. I had done pretty well adjusting things to do this. So well, that I hadn’t even full tested something and here I am over a month later realizing I have a nasty bug.

I have allowed for jumping in TargetState. I really don’t like prohibiting things you can do in either state from the other. Jumping forward or straight up is perfectly fine. However, if you are strafing and jumping or jumping backward it gets ugly:


Powered by TinyTake Screen Capture

My first thought was how we have to handle movement in general in Targeting state is different than in FreeLook. I played around with momentum and transform.right and .forward but nothing seems to be working with that.

It’s weird because dodging works beautifully in both FreeLook and Targeting (with some modifications for FreeLook as that was prohibited there). They both use nearly the same concept but it doesn’t translate.

As far as the code, it is untouched essentially for jumping/falling. I did try the tweaks mentioned above for if the player has a target but haven’t been successful. Any insight is appreciated. Ask me for anything to help out with bug squashing.

Hmmm… before we go too far, are you getting this camera jerkiness when there isn’t a wall so close? (It looks like the wall’s triggering a camera jump)

You may need a custom PlayerJumpState designed for working with TargetingState… paste in your current TargetingState and we’ll take a look.

Nah… Doesn’t matter about walls it does that always.

public class PlayerTargetingState : PlayerBaseState
{
    public PlayerTargetingState(PlayerStateMachine stateMachine, bool shouldBlend = true) : base(stateMachine) 
    {
        this.shouldBlend = shouldBlend;
    }

    private readonly int TARGETINGBLENDTREEHASH = Animator.StringToHash("TargetingBlendTree");
    private readonly int TARGETINGFORWARDHASH = Animator.StringToHash("TargetingForward");
    private readonly int TARGETINGRIGHTHASH = Animator.StringToHash("TargetingRight");
    private const float ANIMATORDAMPTIME = 0.1f;
    private const float ANIMATORBLENDTIME = 0.2f;

    private bool shouldBlend;

    private bool isAttacking = false;

    public override void Enter()
    {
        stateMachine.InputReader.TargetEvent += OnTarget; //Subscribe to cancel target event.
        //stateMachine.InputReader.TargetEvent += AttemptTargetChange;
        stateMachine.InputReader.AttackDown += OnAttack;

        isAttacking = false;

        // Check to see if a smooth blend should happen or an abrupt play for special transitions such as pulling up
        if (shouldBlend) stateMachine.Animator.CrossFadeInFixedTime(TARGETINGBLENDTREEHASH, ANIMATORBLENDTIME); // Enter normal targeting blend tree to trigger Cinemachine camera.
        else stateMachine.Animator.Play(TARGETINGRIGHTHASH);

    }

    public void AttemptTargetChange()
    {
        stateMachine.Targeter.AttemptChangeTarget();
    }

    public override void Tick(float deltaTime)
    {
        // Check for ability animation. Disable when done and return to natural state.
        if (stateMachine.IsAnimatingAbility())
        {
            if (GetNormalizedTime(stateMachine.Animator, "Ability") < 1) return;
            else
            {
                stateMachine.SetAnimatingAbility(false);
                ReturnToLocomotion();
            }
        }

        if (isAttacking)
        {
            stateMachine.SwitchState(new PlayerAttackingState(stateMachine, 0)); //Switch to attack with first attack.
            return;
        }

        if (stateMachine.Targeter.currentTarget == null)
        {
            stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
            return;
        }

        Vector3 movement = CalculateMovement(deltaTime);
        Move(movement * stateMachine.GetCurrentSpeed(false) * DistanceSpeedModifier(), deltaTime);

        UpdateAnimator(deltaTime);
        FaceTarget(deltaTime);
    }

    public override void Exit()
    {
        stateMachine.InputReader.TargetEvent -= OnTarget;
        stateMachine.InputReader.AttackDown -= OnAttack;
    }

    private void OnTarget()
    {
        if (stateMachine.IsInUI()) return; //Ignore target change while in UI
        stateMachine.Targeter.Cancel();
        stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
    }

    private void OnAttack()
    {
        if (stateMachine.IsInUI()) return; //Ignore attack requests while in UI
        if (stateMachine.IsMouseTargeting()) return; //Ignore attack requests while targeting abilities.

        isAttacking = true;
    }

    private Vector3 CalculateMovement(float deltaTime)
    {
        Vector3 movement = new Vector3();

        movement += stateMachine.transform.right * stateMachine.InputReader.MovementValue.x; //Calculate lateral movement.
        movement += stateMachine.transform.forward * stateMachine.InputReader.MovementValue.y; //Calculate forward/backward movement.

        return movement;
    }

    private void UpdateAnimator(float deltaTime)
    {
        if (stateMachine.InputReader.MovementValue.y == 0) //Check for no forward/back movement
        {
            stateMachine.Animator.SetFloat(TARGETINGFORWARDHASH, 0, ANIMATORDAMPTIME, deltaTime);
        }
        else
        {
            //Check to see if moving forward or back and then set Animator value to match
            float value = stateMachine.InputReader.MovementValue.y > 0 ? 1f : -1f;
            stateMachine.Animator.SetFloat(TARGETINGFORWARDHASH, value, ANIMATORDAMPTIME, deltaTime);
        }

        if (stateMachine.InputReader.MovementValue.x == 0) //Check for no forward/back movement
        {
            stateMachine.Animator.SetFloat(TARGETINGRIGHTHASH, 0, ANIMATORDAMPTIME, deltaTime);
        }
        else
        {
            //Check to see if moving right or left and then set Animator value to match
            float value = stateMachine.InputReader.MovementValue.x > 0 ? 1f : -1f;
            stateMachine.Animator.SetFloat(TARGETINGRIGHTHASH, value, ANIMATORDAMPTIME, deltaTime);
        }
    }
}

And the (false) for GetCurrentSpeed is just saying it isn’t FreeLook. I have the two separate speeds and use a MovementSpeed Stat that uses one or the other fore base. DistanceModifier is something you helped with for that illusion of how fast you can rotate around an enemy. It works like a charm, too.

But in any event, isn’t this sort of irrelevant since all of movement will be in PlayJumpingState and PlayerFallingState ?

public class PlayerJumpingState : PlayerBaseState
{
    private readonly int JUMPHASH = Animator.StringToHash("Jump");
    private const float ANIMATORBLENDTIME = 0.1f;
    private const float JUMPVOLUME = 0.7f;

    private Vector3 momentum;
    private bool queueJumpAttack = false;

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

    public override void Enter()
    {
        stateMachine.LedgeDetector.OnLedgeDetect += HandleLedgeDetect;
        stateMachine.InputReader.AttackDown += HandleJumpAttack;

        stateMachine.SetJumping(true);
        stateMachine.ForceReceiver.Jump(stateMachine.JumpForce);

        stateMachine.AudioSource.PlayOneShot(stateMachine.JumpSounds[Random.Range(0, stateMachine.JumpSounds.Length)], JUMPVOLUME);

        momentum = stateMachine.Controller.velocity;
        momentum.y = 0;

        stateMachine.Animator.CrossFadeInFixedTime(JUMPHASH, ANIMATORBLENDTIME);
        stateMachine.FaceMovementDirection();
    }

    public override void Tick(float deltaTime)
    {
        // Move with current momentum
        if (stateMachine.Targeter.currentTarget == null) Move(momentum, deltaTime);
        else Move(HandleLateralMovement(momentum), deltaTime);

        // Enter Falling state if y starts to head towards ground
        if (stateMachine.Controller.velocity.y <= 0 || stateMachine.Controller.isGrounded)
        {
            stateMachine.SwitchState(new PlayerFallingState(stateMachine, queueJumpAttack));
            return;
        }

        FaceTarget(deltaTime); 
    }

    public override void Exit()
    {
        stateMachine.LedgeDetector.OnLedgeDetect -= HandleLedgeDetect;
        stateMachine.InputReader.AttackDown -= HandleJumpAttack;

        stateMachine.SetJumping(false);
    }

    private Vector3 HandleLateralMovement(Vector3 momentum)
    {
        return momentum;
    }

    private void HandleLedgeDetect(Vector3 ledgeForward, Vector3 closestPoint)
    {
        if (!stateMachine.Sheathe.IsSheathed()) return; //Do not allow ledge grab while weapon is unsheathed.
        stateMachine.SwitchState(new PlayerHangingState(stateMachine, ledgeForward, closestPoint));
    }

    private void HandleJumpAttack()
    {
        queueJumpAttack = true;
    }
}

(You can see that I have the if check there and HandleLateralMovement from when I was trying different things. I assume we need to mess with momentum a bit here to fix this). I also have a jump attack that queues in JumpingState and actually calls over to attack from FallingState

How are you switching to PlayerJumpingState? I see it’s not handled at all in the TargetingState.

In Nathan’s code, he actually does set things up for both the FreelookState and the TargetingState, by having each of those states subscribe to the Jump event. Are you using the StateMachine for this? Nathan actually has no difference in logic as far as whether the player started from FreeLook or Targeting. FaceTarget() actually deals with keeping the player straffing while jumping, eliminating the need to HandleLateralMovement()

After watching the video multiple times, it does look like the player is jumping correctly, but the camera is either switching to a different view or otherwise overreacting…

Yeah I do it in PlayerStateMachine. Once I decided to allow the player to do more things in FreeLook (like attacks and dodges) I started doing most actions from there. My bad if I confused the issue if he had jump set up in both states.

    private void OnJump()
    {
        if (IsInUI()) return;
        if (IsStunnedOrDead()) return;
        if (IsPerformingAction()) return;

        SwitchState(new PlayerJumpingState(this));
    }

I also came to the same conclusion. It has to be the Cinemachine camera doing something just nothing makes any sense. It shouldn’t make a difference which statemachine is passed in to my Jump call, right? Dodges work almost exactly the same and my dodge is I think exactly the same as my OnJump

Maybe someone with this working right could drop in some screenshots of the Targeting Camera settings. It’s possible I tinkered with something that is breaking it?

Tinker Updates:
I had it set to “Transposer” on Body. I changed that to “Framing Transposer” and it looked like it fixed it. That camera jerk doesn’t happen. But then if I run out of targeting range or get right up on the target the camera really freaks out jumping or not. Focuses on me and spins around like crazy for a second.

I have gone back and checked with having ALL settings just like Nathan to no avail. Most of my tweaks were superficial to get a better view or snappiness I like. Even an actual component add of a camera offset doesn’t affect it (I like it up and pointing down more in targeted… disabling that does nothing to help). The only thing that has helped is Yaw Damping. Putting that up to about 8 or so will mask that camera jerk but then the player is VERY loose moving laterally. The camera doesn’t rotate for until you move like 10 steps to either side. Still toying with it :confused:

More messing around: I removed FaceTarget from JumpingState and it makes it a little easier to see what is making the camera jerk. unless it is just an illusion. The camera tries to basically ZOOM to the target a little bit and then zooms back out as the player starts to fall… IDK. It’s really frustrating. Literally Yaw damping works and it seems only because I basically jump within the distance of the damped yaw so it doesn’t disturb the camera. Even having it at 2 makes it less noticeable. But about 10 for it to go away the whole way and that is far too loose. Meh.

It’s best practice with StateMachines for each state to handle selection of actions like Jump, Attack, etc… This gives more direct control over which states a Jump would work in. For example, I’m not sure that your OnJump() would fail to let the player jump multiple times while still in the air. Because each state is an encapsulated snapshot of what is going on, it’s best to let the states handle these logical elements. I’m not saying you -=have=- to, but just making a suggestion.

Do you have a CinemachineFollowZoom on your Camera by chance? Or a Cinemachine Collider?

Here are Nathan’s Camera settings for the Targeting camera


I’m quite thorough with protections and in my mind it is way easier to handle this all in the main PSM:

    public bool IsPerformingAction()
    {
        // Restricted actions to not allow animation cancel.
        if (IsJumping() || IsFalling() || IsDodging() || IsSheathing() || IsUnsheathing() || IsBlocking() || IsJumpAttacking() || IsMouseTargeting() || IsAnimatingAbility()) return true;

        return false;
    }

And this is only on TargetCam:
image

The collider is only on free look.

Just a few tweaks to offsets and such. I don’t see anything that would cause this behavior.

Still no luck so far. I had done part of the Third Person course by the book but apparently I had stopped before jump. I wanted to see if it would do it to me there. I may try to move the jump code into FreeLook and Target just to see what happens. Otherwise I am running out of ideas :confused:

Yeah… Handling Jump directly in TargetingState yielded no changes. Expected, but then again… this behavior is so bizarre who knows.

At this point, I think I’ll just need to take a closer look at your project. Zip up your project and upload to https::/gdev.tv/projectupload and I’ll take a look as soon as I can. Don’t forget to remove the Library folder to conserve space.

Sounds good. As an added bonus you can give me some feedback on my direction so far. Plus you’ll have a copy for my inevitable followup questions on stuff :joy:

It’s going to be pretty large even with Library removed as I do have a lot of paid assets already imported. I’ll try to leave out stuff that isn’t in this scene if at all possible

Alright… Got it down to 1.6GB. Had to throw it on my Google Drive for it to be accepted. Hopefully it worked.

The entry for the file is blank… PM me the link to your project on Drive.

Done. Thanks for your patience, Brian. Much appreciated.

Well, that was an adventure… it didn’t like missing some of the things that were removed (and the scene was broken, the characters were falling falling falling…)
Anyways, after a little scene reconstruction, I was able to enter targetting state and try jumping left, right, and sdrawkcab, and duplicate the bug.

Interestingly enough, it didn’t seem to matter what I did to the individual settings in the Targetting camera, it still weirded out… until I change the Transposer to FramingTransposer…
And without changing a single setting in the Framing Transposer, it worked perfectly, and…
I have NO IDEA why!!!.
In our course project, we’re using the Transposer.
In my personal project, I’m using the Transposer…
But in this project, the Framing Transposer did the trick…

It’s possible that something in one of the added assets is causing a conflict, but I’m not sure how…

See if it’s working for you with the Framing Transposer

Yeah I tried that. I’ll try it again in a bit to see if it magically changes. But try running out of range and right up in enemy faces with Framing Transposer and see if you replicate that part (and if you do if you can think of a fix)

The first part, running up on the character, That’s best solved enforcing some distance between the player and the target. (This brings up another problem, though, which is if you’re not close enough, you don’t score a hit, but that was already a problem, which is best solved by extending the colliders on the weapon).
The second part is a transition problem between the Targeting camera and the freelook camera. I believe nathan addresses this in a later video.

Sigh… so just no resolution basically. I already have my nice workaround to stopping the speed from looking so ridiculous when you get close. And I have done the whole course so any sort of tweak on switching is already in place. It was the cylinder or whatever thing. And ease in. I just don’t get it.

I am playing more with it. Unchecking “Target Movement Only” for Framing Transposer seems to somewhat alleviate the out of range factor. But being right up on the enemy is kind of required for melee. And the distances that it starts acting up… even a buffer zone wouldn’t fix.

Yeah… the max range thing is fine really. It’s just that when you get close, the camera pulls up and out… Until it is directly over ONLY the enemy (which makes no sense because it should be framing the enemy AND YOU). Then it can’t tell which way is up or down and starts freaking out.

Back to regular transposer mode… Yaw adjustments entirely fix the jerking but then combat does not feel right at all… you strafe all the way around the enemy and the camera doesn’t go with you. None of this makes any sense considering dodge is doing nearly the same exact thing as jump.

As usual I’ll keep this space for running notes on more testing. What is SUPER interesting… If I change the Look At target for the TargetingCam to just the player or the normal Camera focus… rather than the targeting group… It STILL does the weird jerk(its just quicker about it). To me, that screams it really just hates the jump code or character controller or something when it is jumping

I’m sorry, I hate that this isn’t resolved as well. I’m not used to unsolved mysteries. Hopefull a solution will present itself.

I appreciate you taking the time. I’ll throw up what I come across for awhile and then get depressed about it.

I have a novel solution that looks slightly better. Since I already have a CameraHelper script for custom zoom levels (that change the orbit levels) I put this in update:

    private void TargetingCameraJumpStuff()
    {
        if (playerStateMachine.Targeter.currentTarget != null)
        {
            if (playerStateMachine.IsJumping() || playerStateMachine.IsFalling()) targetingCamera.GetCinemachineComponent<CinemachineTransposer>().m_YawDamping = 10;
            else targetingCamera.GetCinemachineComponent<CinemachineTransposer>().m_YawDamping = 0;
        }
    }

Updated Version that looks maybe a little better, but still irritating. This at least gives the impression “Oh, you are jumping? Then you will jump away from this nice centered view then we will get back to that:”

    private void TargetingCameraJumpStuff()
    {
        if (playerStateMachine.HasTarget())
        {
            if (playerStateMachine.IsJumping() || playerStateMachine.IsFalling())
            {
                defaultLookAt = targetingCamera.LookAt;
                targetingCamera.GetCinemachineComponent<CinemachineTransposer>().m_YawDamping = 10;
                targetingCamera.LookAt = targetingCamera.Follow;
            }
            else
            {
                targetingCamera.GetCinemachineComponent<CinemachineTransposer>().m_YawDamping = 0;
                targetingCamera.LookAt = defaultLookAt;
            }
        }
    }

Both still have a visible snap back after landing while it isn’t rotating normally.

But I find this most interesting of all…

I removed Move(momentum) from both PlayerJumping and PlayerFalling… It STILL does the camera jerk. How is that even possible?

Given that jumping worward doesn’t create teh jerk, I’m not sure at all!

Privacy & Terms