- Home /
Game skips input on 9999 fps but doesn't on 60 fps?
Hello! I have a simple scene where the code kinda depends on Time.deltaTime. I tried to build the scene with frameratelimit on 9999 fps and on 60 fps, when on 60 fps it work quite right, but on 9999 fps it got extremely bad with the input.
I have no abnormalities in my code, just normal use of Time.deltaTime, so I don't know what the problem is.
I have also tried not limiting the frame rate, in which case it got really high and caused the same problem as with 9999 fps.
What are you doing with the input (e.g. script example)? In what manner is it "extremely bad" for you?
Reliance on Time.deltaTime is normal for a framerate-independent game. There's nothing weird about using it. However, we need a little bit more context (again, script example) to know whether your "normal use of Time.deltaTime" is a potential/significant factor in your problem(s) or not.
This is my code:
void Movement()
{
float xInput = Input.GetAxis("Horizontal") * movementSpeed;
float zInput = Input.GetAxis("Vertical") * movementSpeed;
if (!slideOrCrouch) //if the player is NOT sliding
{
moveVector = Vector3.ClampMagnitude(transform.forward * zInput + transform.right * xInput, movementSpeed); //mV2
}
isGrounded = Physics.CheckSphere(transform.position - new Vector3(0f, 0.56f, 0f), 0.5f, groundedLayers); //CheckSphere to know when player is and isn't Grounded.
if (isGrounded)
{
if (Input.GetButtonDown("Jump") && canCrouchUp)
{
Jumping();
}
else
{
gravityVector.y = gravityForce;
}
hasDoubleJumped = false;
cc.slopeLimit = 45f;
}
else
{
gravityVector.y -= gravityForce * gravityForce * Time.deltaTime;
cc.slopeLimit = 90f;
}
cc.Move(moveVector * Time.deltaTime);
cc.Move(gravityVector * Time.deltaTime);
if (moveVector.magnitude >= (movementSpeed / 2)) { isWalking = true; } else { isWalking = false; }
}
void Jumping()
{
if (moveVector.sqrMagnitude == 0f)
{
jumpForceMultiplier = Mathf.Sqrt(jumpForce * -gravityForce);
}
if (moveVector.sqrMagnitude > 0f)
{
jumpForceMultiplier = Mathf.Sqrt(jumpForce * -gravityForce) + (moveVector.magnitude * additionalMoveJumpMultiplier);
}
gravityVector.y = jumpForceMultiplier;
}
I turned Unity's "Sync V Blank" and it fixed it I guess. Though, I want to know what the problem in my code is so I can learn not to do it anymore. I like to make my code run with absolutly no errors, even if the framerate is extremely high or low.
And the problem is that the player doesn't jump sometimes. He would be grounded and still not jump even after spa$$anonymous$$g the jump button.
Answer by Eno-Khaon · Jun 25, 2021 at 02:34 AM
A CharacterController's physics are processed during FixedUpdate().
With that in mind, you're currently reliant on lucky timing based on the way you're processing those physics.
So, let's start with how you're handling Jumping in the first place:
if (Input.GetButtonDown("Jump") && canCrouchUp)
{
Jumping();
}
else
{
gravityVector.y = gravityForce;
}
// ... and...
void Jumping()
{
// ...
gravityVector.y = jumpForceMultiplier;
}
First, let's compare Update() to FixedUpdate(). Update() runs as rapidly as possible/is permitted (i.e. limited by Vertical Sync), where FixedUpdate() only runs at a fixed pace (by default, 50 times per second).
Based on your script, this means that it's a race. After the Update() cycle where you provide input, will FixedUpdate() take place and process the instructions given to the CharacterController, or will the next Update() run and reset "gravityVector.y" to the "gravityForce" value again?
With a lower framerate (60, for instance), Update() can run twice in a row without a FixedUpdate(), but it's a 1/6 chance to occur. By contrast, with a framerate of 500, you would have only a 1/10 chance of your "Jumping()" function's impact on gravityVector being reflected by the physics interactions.
(As another element of this...
isGrounded = Physics.CheckSphere(transform.position - new Vector3(0f, 0.56f, 0f), 0.5f, groundedLayers); //CheckSphere to know when player is and isn't Grounded.
... would be run again before you have a chance to move, resulting in the assumption you're on the ground and, therefore, negating the change to "gravityVector")
Finally, in addition to all this, you're mixing up your [position-by-gravity] and [acceleration-by-gravity] formulas, resulting in a somewhat irregular gravitational formula:
// After 1 second, vertical speed would be (gravityForce^2)
// After 2 seconds, vertical speed would be (2 * gravityForce^2)
// After 3 seconds, vertical speed would be (3 * gravityForce^2)
// And so on...
gravityVector.y -= gravityForce * gravityForce * Time.deltaTime;
// After 1 second, vertical speed would be (gravityForce)
// After 2 seconds, vertical speed would be (2 * gravityForce)
// After 3 seconds, vertical speed would be (3 * gravityForce)
// And so on...
gravityVector.y -= gravityForce * Time.deltaTime;
I don't understand, what should be done otherwise? This is how most youtube videos (all that I've seen) say it should be, or at least basically that way.
Should all my code that has to do with movement be in FixedUpdate() instead???? If not, what can I do if it shouldn't be in FixedUpdate() and is bad to have in Update() ???
Oh and this is the code I'm using now, it's the same but "cleaner":
void Movement()
{
float xInput = Input.GetAxis("Horizontal") * movementSpeed;
float zInput = Input.GetAxis("Vertical") * movementSpeed;
moveVector = Vector3.ClampMagnitude(transform.forward * zInput + transform.right * xInput, movementSpeed);
isGrounded = Physics.CheckSphere(transform.position - new Vector3(0f, 0.56f, 0f), 0.5f, groundedLayers); //CheckSphere to know when player is and isn't Grounded.
if (isGrounded)
{
if (Input.GetButtonDown("Jump"))
{
float jumpForceMultiplier = (moveVector.sqrMagnitude > 0f) ? ((jumpForce * -gravityForce) + (moveVector.magnitude * movementJumpMultiplier)) : (jumpForce * -gravityForce);
gravityVector.y = jumpForceMultiplier;
}
else
{
gravityVector.y = gravityForce;
}
hasDoubleJumped = false;
cc.slopeLimit = 45f;
}
else
{
if (Input.GetButtonDown("Jump") && !hasDoubleJumped)
{
gravityVector.y = doubleJumpForce * -gravityForce;
hasDoubleJumped = true;
}
else
{
gravityVector.y -= Mathf.Pow(gravityForce, 2) * Time.deltaTime;
}
cc.slopeLimit = 90f;
}
cc.Move(moveVector * Time.deltaTime);
cc.Move(gravityVector * Time.deltaTime);
isWalking = moveVector.magnitude >= (movementSpeed / 2);
}
And for the gravity part (last part), I don't get what is wrong. When you're not grounded, the gravity force would increase over time wouldn't it? Shouldn't it be *2, *3, *4 over time?
Ah, whoops. I got distracted when typing out that last part. I meant to emphasize that removing the second "gravityForce" multiplication would result in those speed examples at given times. The position after a given time follows a different formula than the current speed as a result of acceleration over time. You don't need to give gravity a strange relationship with jumping speed, since it means that changing gravity changes the relationship between those speeds.
To address the *2, *3, and *4 elements specifically, that example was made to simply illustrate that squaring it would result in odd values.
The key element with regard to input and velocity is more closely related to the fact that you have race conditions between when input is provided ( Update() cycle) and when that input is acted on ( FixedUpdate() cycle).
// If this runs again before you have a chance to leave the ground,
// the loop sequence will lead to (isGrounded->else), resulting in
// gravityVector.y = gravityForce;
// running before you ever start to move
isGrounded = Physics.CheckSphere(transform.position - new Vector3(0f, 0.56f, 0f), 0.5f, groundedLayers);
if(isGrounded)
{
if (Input.GetButtonDown("Jump"))
{
float jumpForceMultiplier = etc;
gravityVector.y = jumpForceMultiplier;
}
else
{
gravityVector.y = gravityForce;
}
}
else
{
// Hard to reach with high framerate, basically
// ...
// (Technically, Mathf.Pow(x, 2) is less efficient than x*x),
// but that's not really the point here, as mentioned above
gravityVector.y -= Mathf.Pow(gravityForce, 2) * Time.deltaTime;
}
As far as a CharacterController goes, I haven't personally worked with them much. I get what they're intended for, but my personal preference leads me to work with carefully a constrained Rigidbody instead, since that allows for much simpler/cleaner external force application (i.e. getting knocked around). I also know that a CharacterController *IS* more usable through Update() than a standard Rigidbody, but it still has to interact with the physics system, which fundamentally occurs during FixedUpdate().
With all of that in $$anonymous$$d, the most direct cause of your problem would simply be your CheckSphere(). With a higher framerate, any amount of buffer on the size of the sphere (to account for the on-the-ground-or-close-enough element) will more easily catch you off guard. If you have only risen 1/10 as high on the first frame of your jump, for instance (based on Time.deltaTime value), will you still make it beyond that buffer, or will you still be too close to the ground that it counts as making contact again?
I tried putting the grounded CheckSphere() in FixedUpdate and it worked worse. I know you didn't say that's what I should do but I don't get what else there is to do? How will I check if the player is grounded otherwise? If the problem is CheckSphere() what else is there to use?
I don't think I'll be writing more code, but I think someone else who encounters the same problem will find this and also be helped by it.
Thank you so much for the help btw <3 <3 <3
Your answer
Follow this Question
Related Questions
Framerate counter 1 Answer
Frames Per Second 2 Answers
Too much FPS, is there a cutoff limit? 2 Answers
Patching glitches 1 Answer
Setting camera pitch - Never works? 0 Answers