Jumping sets back momentum

Hi,
I am using forces to control velocity for my player character in a 2D game.
As it is, whenever I jump, there is a brief jolt as my velocity is suddenly reduced, as if I was being held back.
I want to preserve my ground velocity going into the air, or at least make the transition between ground and air speed smoother.

The following function is where it all happens:

private void ApplyMovement()
    {
        if (isGrounded && !isOnSlope && !isJumping) //if not on slope
        {
            //Debug.Log("This one");
            newVelocity.Set(movementSpeed * xInput, 0.0f);
            speedDif = newVelocity.x - rb.velocity.x;
            accelRate = (Mathf.Abs(newVelocity.x) > 0.01f) ? runAccel : runDeccel;
            if (((rb.velocity.x > newVelocity.x && newVelocity.x > 0.01f) || (rb.velocity.x < newVelocity.x && newVelocity.x < -0.01f)))
            {
                accelRate = 0; //prevent any deceleration from happening, or in other words conserve are current momentum
            }

            if (Mathf.Abs(newVelocity.x) < 0.01f)
            {
                velPower = stopPower;
            }
            else if (Mathf.Abs(rb.velocity.x) > 0 && (Mathf.Sign(newVelocity.x) != Mathf.Sign(rb.velocity.x)))
            {
                velPower = turnPower;
            }
            else
            {
                velPower = accelPower;
            }

            float movement = Mathf.Pow(Mathf.Abs(speedDif) * accelRate, velPower) * Mathf.Sign(speedDif);

            rb.AddForce(movement * Vector2.right);
            //rb.velocity = newVelocity;
        }
        else if (isGrounded && isOnSlope && canWalkOnSlope && !isJumping) //If on slope
        {
            newVelocity.Set(movementSpeed * slopeNormalPerp.x * -xInput, movementSpeed * slopeNormalPerp.y * -xInput);
            //speedDif = newVelocity.x - rb.velocity.x;
            speedDif = newVelocity.x - rb.velocity.x;
            accelRate = (Mathf.Abs(newVelocity.x) > 0.01f) ? runAccel : runDeccel;
            if (((rb.velocity.x > newVelocity.x && newVelocity.x > 0.01f) || (rb.velocity.x < newVelocity.x && newVelocity.x < -0.01f)))
            {
                accelRate = 0; //prevent any deceleration from happening, or in other words conserve are current momentum
            }

            if (Mathf.Abs(newVelocity.x) < 0.01f)
            {
                velPower = stopPower;
            }
            else if (Mathf.Abs(rb.velocity.x) > 0 && (Mathf.Sign(newVelocity.x) != Mathf.Sign(rb.velocity.x)))
            {
                velPower = turnPower;
            }
            else
            {
                velPower = accelPower;
            }

            float movement = Mathf.Pow(Mathf.Abs(speedDif) * accelRate, velPower) * Mathf.Sign(speedDif);

            rb.AddForce(movement * Vector2.right);
            //rb.velocity = newVelocity;
        }
        else if (!isGrounded) //If in air
        {
            newVelocity.Set(movementSpeed * xInput, rb.velocity.y);
            speedDif = newVelocity.x - rb.velocity.x;
            accelRate = (Mathf.Abs(newVelocity.x) > 0.01f) ? runAccel * accelInAir : runDeccel * deccelInAir;
            if (((rb.velocity.x > newVelocity.x && newVelocity.x > 0.01f) || (rb.velocity.x < newVelocity.x && newVelocity.x < -0.01f)))
            {
                accelRate = 0; //prevent any deceleration from happening, or in other words conserve are current momentum
            }

            if (Mathf.Abs(newVelocity.x) < 0.01f)
            {
                velPower = stopPower;
            }
            else if (Mathf.Abs(rb.velocity.x) > 0 && (Mathf.Sign(newVelocity.x) != Mathf.Sign(rb.velocity.x)))
            {
                velPower = turnPower;
            }
            else
            {
                velPower = accelPower;
            }

            float movement = Mathf.Pow(Mathf.Abs(speedDif) * accelRate, velPower) * Mathf.Sign(speedDif);

            rb.AddForce(movement * Vector2.right);
            //rb.velocity = newVelocity;
        }

    }

Is it perhaps just friction on the ground? Test that theory by making all colliders frictionless (zero friction) and see.

Otherwise, debug what is actually happening. Here’s how:

You must find a way to get the information you need in order to reason about what the problem is.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is
  • you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as Debug.Log("Problem!",this);

If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer or iOS: How To - Capturing Device Logs on iOS or this answer for Android: How To - Capturing Device Logs on Android

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

I believe the issue is that I’m setting newVelocity separately within each if statement

It sounds like you’re overriding the existing horizontal velocity rather than simply adding a vertical component to it. If you’re using forces to jump, just take the existing velocity, add to the y component, and use that.

Can’t say much more as it’s hard to read your mess of code.

1 Like

I feel like it might be a bit disappointing for them to hear, but i have to agree – even when you want to help the person, the moment you see code that is not easily readible/explained, you kinda lose your enthusiasm.

@Shreddednerd , have you considered breaking your ApplyMovement method into smaller methods? Sometimes doing so allows you to see the potential issues much more clearly.

Sounds like an issue with transitioning between your “non jumpy” state and your “in the air” state. Perhaps add a new state, such as !isJumpingFromStandingStill or something?

Yes I think this is the issue. I’ll get to clean up the code in the future but for now I’m just trying to get it working.

Here’s an updated code segment. Certainly works better and is much cleaner but the issue still occurs to some degree. I’ll see if I can make some adjustments to the values to fix.

float targetSpeed = xInput * movementSpeed;
        speedDif = targetSpeed - rb.velocity.x;

       
        if (!isGrounded)
        {
            accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? runAccel * accelInAir : runDeccel * deccelInAir;
        }
        else
        {
            accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? runAccel : runDeccel;
        }

        if (((rb.velocity.x > targetSpeed && targetSpeed > 0.01f) || (rb.velocity.x < targetSpeed && targetSpeed < -0.01f)))
        {
           accelRate = 0; //prevent any deceleration from happening, or in other words conserve are current momentum
        }
       
        if (Mathf.Abs(targetSpeed) < 0.01f)
        {
            velPower = stopPower;
        }
        else if (Mathf.Abs(rb.velocity.x) > 0 && (Mathf.Sign(targetSpeed) != Mathf.Sign(rb.velocity.x)))
        {
            velPower = turnPower;
        }
        else
        {
            velPower = accelPower;
        }
        Debug.Log(rb.velocity);
        float movement = Mathf.Pow(Mathf.Abs(speedDif) * accelRate, velPower) * Mathf.Sign(speedDif);
        rb.AddForce(movement * Vector2.right);

Do you know you can also just set the velocity rather than trying to attain a velocity with forces?

A pattern I like is to read the velocity out into a Vector3, do all my input and adjust the velocity, then write it back to the Rigidbody at the end of the function.

ALSO: you are doing all this from FixedUpdate(), correct? As in that’s where ApplyMovement() is called from?

I was directly setting velocity in earlier, simpler code. I wanted to change it to forces to give some movement acceleration. And yes ApplyMovement is being called from FixedUpdate.

You can also gradually change the velocity so it has pickup and slowdown.

This pattern always works:

Smoothing movement between any two particular values:

https://discussions.unity.com/t/812925/5

You have currentQuantity and desiredQuantity.

  • only set desiredQuantity
  • the code always moves currentQuantity towards desiredQuantity
  • read currentQuantity for the smoothed value

Works for floats, Vectors, Colors, Quaternions, anything continuous or lerp-able.

The code: https://gist.github.com/kurtdekker/fb3c33ec6911a1d9bfcb23e9f62adac4

In your case the currentQuantity would simply be what is actually IN the Rigidbody right now, so you only need to move that towards the desiredQuantity by the amount of acceleration you want.