Closed
Jumping with slight air control while retaining velocity
Hi all,
Hopefully I can find a solution here as this has been driving me mad. I'm creating an FPS Controller, movement has been going great however I've hit a problem with jumping and maintaining velocity.
When my character jumps, it behaves as expected. Moving forward and jumping moves at a reasonable speed compared to the characters walking speed. However if I stop moving forward whilst mid air, the character stops on a dime mid air. I want the character to continue moving in the direction they were jumping in regardless of the player stopping movement input. This itself is straightforward but I also want the player to have some slight air control, similar in the sense of games like Overwatch or Halo.
I've gotten a temporary solution, however jumping seems to take the player velocity and THEN add the movement on top, making the player seem to leap further and faster than i'd expect.
Here is my code currently:
public float walkingSpeed = 6.0f;
public float jumpHeight = 5.0f;
public float gravity = 20.0f;
private Vector3 movement;
private Vector3 jumpVelocity = Vector3.zero;
private float airSpeed = 4.0f;
private float airFriction = 0.65f;
private void Update()
{
GetPlayerInput();
movement = new Vector3(xInput, 0, zInput);
movement = transform.TransformDirection(movement);
if (controller.isGrounded)
{
controller.slopeLimit = 45.0f;
movement *= walkingSpeed;
if (Input.GetButton("Jump"))
{
jumpVelocity = movement * airFriction;
jumpVelocity.y = jumpHeight;
controller.slopeLimit = 90.0f;
}
else
{
jumpVelocity = Vector3.zero;
}
}
else
{
movement *= airSpeed;
}
jumpVelocity.y -= gravity * Time.deltaTime;
controller.Move((movement + jumpVelocity) * Time.deltaTime);
}
I've left out the movement, but it is a simple Input.GetAxisRaw(Horizontal) etc etc.
Is there anyway I can achieve this effect without the player rocketing ahead whilst moving in mid air?
Thanks!
Answer by lgarczyn · Nov 21, 2019 at 11:09 PM
There's no two ways about it, you need to store the previous velocity.
You already do so with the jumping velocity, simply rename that velocity. If your player is on the ground, simply change the x and z value to your input. If the player is not on the ground, simply add the x and z value of the input to the velocity, multiplied by Time.deltaTime and by a small airSpeedMultiplier.
To prevent the player from skyrocketing during flight, simply check if their lateral velocity is already greater than their maximum ground velocity before adding an input that would increase it.
Oh I see!, that makes perfect sense!
So just to confirm, have I done this correctly? I'm not able to test currently (Work).
private void Update()
{
GetPlayerInput();
if (controller.isGrounded)
{
velocity.x = xInput;
velocity.z = zInput;
controller.slopeLimit = 45.0f;
if (Input.GetButton("Jump"))
{
velocity.y = jumpHeight;
controller.slopeLimit = 90.0f;
}
}
else
{
if( velocity.velocity < 6.0f){
velocity.x += xInput * airSpeed * Time.deltaTime;
velocity.z += zInput * airSpeed * Time.deltaTime;
}
}
velocity.y -= gravity * Time.deltaTime;
velocity = (velocity * walkingSpeed * Time.deltaTime);
velocity = transform.TransformDirection(velocity);
controller.$$anonymous$$ove( velocity );
}
Thanks again!
Just make sure to add drag to your physics material to prevent infinite acceleration in the air, but yes, pretty nice code.
Just a note, code-prettyness-wise, it might be better to make your GetPlayerInput function return the result directly ins$$anonymous$$d of relying on side-effects.
So I've tested the code, the movement still works but when I jump the character seems to just suddenly appear in the air, then quickly drops back to the ground as if gravity is too high, though changing the gravity has no effect and the player can't be controlled mid-air.
I temporarily took out the check for max velocity and have this currently:
private void Update()
{
GetPlayerInput();
if (controller.isGrounded)
{
velocity.x = xInput;
velocity.z = zInput;
controller.slopeLimit = 45.0f;
if (Input.GetButton("Jump"))
{
velocity.y = jumpHeight;
controller.slopeLimit = 90.0f;
}
}
else
{
velocity.x += xInput * airSpeed * Time.deltaTime;
velocity.z += zInput * airSpeed * Time.deltaTime;
}
velocity.y -= gravity * Time.deltaTime;
velocity = (velocity * walkingSpeed * Time.deltaTime);
velocity = transform.TransformDirection(velocity);
controller.$$anonymous$$ove(velocity);
}
Have I mistyped something to cause this?