- Home /
physics randomness problem
Hello i have a problem. When i press W the object jumps up but each time with a different height. I tried to do it through fixedupdate, but then he either jumps every time with a different height or does not jump at all.
void Update()
{
Jump();
}
private void Jump()
{
if (Input.GetKeyDown(KeyCode.W))
{
var jumpVel = new Vector2(0, jumpForce * Time.deltaTime);
rb.velocity += jumpVel;
}
}
Answer by Eno-Khaon · Oct 27, 2021 at 06:45 PM
You're basically scaling your jumping force erratically.
Constant forces/motions (i.e. velocity) never need to be scaled by the framerate. That's what you do when you're making that change yourself.
// When you first change this, expect it to be ~60+ times
// stronger than you intend, depending on your average framerate
Vector2 jumpVel = new Vector2(0, jumpForce);
Answer by rh_galaxy · Oct 27, 2021 at 04:30 PM
Something like this should do it.
private bool bJumping = false;
void FixedUpdate()
{
if (Input.GetKey(KeyCode.W) && !bJumping)
{
bJumping = true;
Jump();
} else bJumping = false; //now jumping is possible again
//issue: you need to keep it true until you land (or jumping in the air is possible)
}
private void Jump()
{
var jumpVel = new Vector2(0, jumpForce * Time.fixedDeltaTime);
rb.velocity += jumpVel;
}
Whenever possible (pretty much always), it's better to *NOT* check input in FixedUpdate() (where it's unreliable) and ensure it remains in Update() (where it is).
That's not true when using Input.GetKey (or similar) instead of Input.GetKeyDown... I have based my entire game at getting input in FixedUpdate, not unreliable at all... Input.GetKey is true at least one "frame", never changes during the FixedUpdate since it's sampled once and valid for at least one FixedUpdate, but it's true that you should not scale force by time.
//this for example is using the gamepad in FixedUpdate with new input system
Gamepad gamepad = Gamepad.current;
if (gamepad != null)
{
Vector2 stickG1 = gamepad.rightStick.ReadValue();
Vector2 stickG2 = gamepad.leftStick.ReadValue();
float trgG1 = gamepad.rightTrigger.ReadValue();
float trgG2 = gamepad.leftTrigger.ReadValue();
//... use input here
}
The randomness comes from using Time.deltaTime and Update() instead of Time.fixedDeltaTime with FixedUpdate(). (I need to do it in FixedUpdate where the timestep is not changing from frame to frame, since I have a replay function that is based on fixed timesteps) In my case I have set fixed time step to 100Hz (instead of the default 50Hz). It works great as long as I don't rely on unsafe functions like Input.GetKeyDown which is true only for one frame and could be missed from FixedUpdate(). Imagine doing the replay within Update and at the recording machine you have 90 fps, and at the replaying machine you have 72 fps... while FixedUpdate() runs at 100Hz timesteps for both. If you steer left for 2 timesteps during recording the replay may play for only one timestep if I use Update(), which is not good.
Well, it's worth clarifying that there's a distinct separation between Input and InputSystem. Your use of "Gamepad" and its associated variables/functions is tied to InputSystem (as you mention in the script sample), while the original question is based around Input.GetKeyDown(), an element of Input.
InputSystem has mechanisms in place to specifically and *directly* support reading input during FixedUpdate() cycles.
The older system, Input (handled via the Input Manager, mainly in the editor), does not support that ti$$anonymous$$g choice and always runs during the main Update() cycle.
Just because player input *CAN* work by avoiding the most ti$$anonymous$$g-sensitive calls doesn't mean that it's the ideal solution and can easily lead to bad habits and practices that would show up again in subsequent projects/scripts with different goals in $$anonymous$$d. Finding workarounds involving Input.GetKey() as a stand-in for Input.GetKeyDown() doesn't change the fact that you *CAN'T* rely on Input.GetKeyDown() during FixedUpdate(), and that any behavioral consistencies become framerate-dependent.
As an example of reading Input separately from its use with a Physics-driven character:
enum ButtonState { NONE, DOWN, HELD, UP }
ButtonState jumpInput;
public KeyCode jumpKey = KeyCode.Space;
void Update()
{
// Only looks for KeyDown and KeyUp variants,
// then holds the result until FixedUpdate()
if(Input.GetKeyDown(jumpKey))
{
jumpInput = ButtonState.DOWN;
}
else if(Input.GetKeyUp(jumpKey))
{
jumpInput = ButtonState.UP;
}
}
void FixedUpdate()
{
// Uses the current state if it was set to UP or
// DOWN during the last Update(), then cycles
// the state to the next in sequence
if(jumpInput == ButtonState.DOWN)
{
rb.AddForce(-Physics.gravity.normalized * jumpForce, ForceMode.VelocityChange);
jumpInput = ButtonState.HELD;
}
else if(jumpInput == ButtonState.UP
{
jumpInput = ButtonState.NONE;
}
}
Your answer
Follow this Question
Related Questions
How do I convert this into Fixed Update for physics and Update for inputs? 0 Answers
Fixed Update Problems C# - Please help! 1 Answer
Should player input be caught in Update, and then raycasting in FixedUpdate for shooting mechanics? 1 Answer
How often does the internal physics calculation get called? 1 Answer