Search Unity

Framerate independent jump

Discussion in 'Scripting' started by Khena_B, Jul 13, 2018.

  1. Khena_B

    Khena_B

    Joined:
    Aug 21, 2014
    Posts:
    273
    Hey,

    I'm trying to build a raycast based character controller and I can't make the Jump method framerate independent no matter what I try, here is a test project from one of the tutorials I've been following, which is also not framerate independent.

    Just press play, move with arrows or wasd and jump with spacebar, and try to clear the pillar. Now change the public variable "Target Framerate" on the Player script from 30 to 60+ and try again. Why is the jump affected by the framerate in this scenario?

    Thanks
     

    Attached Files:

  2. fire7side

    fire7side

    Joined:
    Oct 15, 2012
    Posts:
    1,819
    It's better to post your code rather than requiring a download.
    I noticed this line in the player:
    velocity.y += gravity * Time.deltaTime;

    A compiler will do multiplication before addition, so I think you want:
    (velocity.y += gravity) * Time.deltaTime;

    I didn't try it or anything.
     
  3. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,196
    Ditto on the posting of the relevant code, not a whole project.

    Anyway, so that others can assist, here's that code of yours:

    Code (CSharp):
    1. public class Player : MonoBehaviour {
    2.  
    3.     public int targetFramerate = 30;
    4.  
    5.     public float moveSpeed = 6;
    6.     public float jumpVelocity = 6;
    7.     public float gravity = -25;
    8.  
    9.     Vector3 velocity;
    10.     Controller2D controller;
    11.  
    12.     void Start()
    13.     {
    14.         controller = GetComponent<Controller2D> ();
    15.     }
    16.  
    17.     void Update()
    18.     {
    19.         Application.targetFrameRate = targetFramerate;
    20.  
    21.         if (controller.collisions.above || controller.collisions.below)
    22.             velocity.y = 0;
    23.  
    24.         Vector2 input = new Vector2 (Input.GetAxisRaw ("Horizontal"), Input.GetAxisRaw ("Vertical"));
    25.  
    26.         //JUMP
    27.         if (Input.GetKeyDown (KeyCode.Space) && controller.collisions.below)
    28.             velocity.y = jumpVelocity;
    29.  
    30.         //GRAVITY
    31.         velocity.y += gravity * Time.deltaTime;
    32.  
    33.         //MOVE
    34.         velocity.x = input.x * moveSpeed;
    35.        
    36.         controller.Move (velocity * Time.deltaTime);
    37.     }
    38. }
    Some reactions:

    It looks like you're applying gravity even when grounded. Maybe you shouldn't be doing that?

    As for the framerate issue, my hunch is that it's because you're multiplying by Time.deltaTime twice. I'd need to do some math on that to verify it, but it seems weird. You're reducing the velocity by a deltaTime-based amount, and then moving a deltaTime-based amount. Perhaps its compounding the time in a weird way.

    Maybe it should be like this?

    Code (CSharp):
    1.         //JUMP
    2.         if (Input.GetKeyDown (KeyCode.Space) && controller.collisions.below)
    3.             velocity.y = jumpVelocity;
    4.  
    5.  
    6.         //MOVE
    7.         velocity.x = input.x * moveSpeed;
    8.  
    9.         velocity *= Time.deltaTime;
    10.  
    11.         //GRAVITY
    12.         velocity.y += gravity * Time.deltaTime;
    13.      
    14.         controller.Move (velocity);
     
  4. Khena_B

    Khena_B

    Joined:
    Aug 21, 2014
    Posts:
    273
    I've figured out why this is happening thanks to Eno-Khaon on Unity Answers

    It's easier to understand by looking at it visually so I suggest taking a look at what he posted, basically the more frames, the smaller the subtracted amount will be (because it's multiplied every frame by delta) so the higher it will jump.

    I'll keep looking for a solution

    @dgoyette thanks for posting the code
     
  5. Khena_B

    Khena_B

    Joined:
    Aug 21, 2014
    Posts:
    273
    So Bunny31's solution at my Unity Answers post works perfectly, the gravity has to be applied in two parts, example:

    Code (CSharp):
    1. //GRAVITY
    2. velocity.y += gravity * 0.5f * Time.deltaTime;
    3.  
    4. //MOVE
    5. velocity.x = input.x * moveSpeed;
    6.  
    7. controller.Move (velocity * Time.deltaTime);
    8.  
    9. //GRAVITY
    10. velocity.y += gravity * 0.5f * Time.deltaTime;