- Home /
How do I pass an input in Update() to FixedUpdate() for Physics movement?
First and foremost I realize this is a huge topic of discussion as I have read various previous posts and looked at Unity documentation and other sites for this answer. The overall response has been to have all inputs within the Update() method and have those inputs made known to the FixedUpdate() method for the actual physics movement. However, I've also read that if it is a single input event the input and physics can just be included in the Update() method. With all that said, I am trying to do a simple "jump" on the space bar key down, I have attached my entire script to avoid any confusion.
As I have it, when I test it, the Player game object only jumps every so often, not every single time that I press the space key. I am having the same issue with the second If-statement for the "bullet" firing on left mouse button click. So how can I restructure this to make it work? I am interested in learning how to handle the input (such as key down on space key to jump) in Update() and have the physics (adding the force to handle the jump) occur in FixedUpdate(). ANY HELP/THOUGHTS WOULD BE GREATLY APPRECIATED!!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerBehavior : MonoBehaviour
{
public LayerMask groundLayer;
private Rigidbody _rb;
private CapsuleCollider _col;
public GameObject bullet;
public float jumpVelocity = 5f;
public float moveSpeed = 10f;
public float rotateSpeed = 75f;
public float distanceToGround = 0.1f;
public float bulletSpeed = 100f;
private float vInput;
private float hInput;
// Start is called before the first frame update
void Start()
{
_rb = GetComponent<Rigidbody>();
_col = GetComponent<CapsuleCollider>();
}
// Update is called once per frame
void Update()
{
vInput = Input.GetAxis("Vertical") * moveSpeed;
hInput = Input.GetAxis("Horizontal") * rotateSpeed;
}
void FixedUpdate()
{
Vector3 rotation = Vector3.up * hInput;
Quaternion angleRot = Quaternion.Euler(rotation * Time.deltaTime);
_rb.MovePosition(this.transform.position + this.transform.forward * vInput * Time.fixedDeltaTime);
_rb.MoveRotation(_rb.rotation * angleRot);
if (IsGrounded() && Input.GetKeyDown(KeyCode.Space))
{
_rb.AddForce(Vector3.up * jumpVelocity, ForceMode.Impulse);
}
if (Input.GetMouseButtonDown(0))
{
GameObject newBullet = Instantiate(bullet, this.transform.position + new Vector3(1, 0, 0), this.transform.rotation) as GameObject;
Rigidbody bulletRB = newBullet.GetComponent<Rigidbody>();
bulletRB.velocity = this.transform.forward * bulletSpeed;
}
}
private bool IsGrounded()
{
Vector3 capsuleBottom = new Vector3(_col.bounds.center.x, _col.bounds.min.y, _col.bounds.center.z);
bool grounded = Physics.CheckCapsule(_col.bounds.center, capsuleBottom, distanceToGround, groundLayer, QueryTriggerInteraction.Ignore);
return grounded;
}
}
Answer by Eno-Khaon · Jul 25, 2021 at 01:48 AM
One way you could manage the state of your button presses would be to create your own enumerator to track a given key. You'd set active changes in Update() and passive changes in FixedUpdate(). For example:
public enum KeyState { Off, Down, Held, Up }
public KeyState ksSpace = KeyState.Off;
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
ksSpace = KeyState.Down;
}
else if(Input.GetKeyUp(KeyCode.Space))
{
ksSpace = KeyState.Up;
}
}
void FixedUpdate()
{
if(ksSpace == KeyState.Down)
{
// "Jump" Pressed
ksSpace = KeyState.Held;
}
else if(ksSpace == KeyState.Held)
{
// Holding "Jump", as desired
}
else if(ksSpace == KeyState.Up)
{
// "Jump" Released
ksSpace = KeyState.Off;
}
}
There are plenty of ways this can be expanded on, but should serve as a good starting point and reasonable implementation for what you're looking for here.
I've read about using an enumerator for these situation but never seen what it would look like, this is really helpful. One question, is there another way to pass input from Update to FixedUpdate without using an enumerator? Also, the code block inside the the if /else - if statements, is that were I would put the code to execute the actual physics. I get the keystates, just not sure where I would insert the jump physics using this process... I apologize, I'm new to all of it (Unity, C#, program$$anonymous$$g).
You can certainly find other approaches to it. I recommend basing it around an enumerator because it gives you better granularity and control over your inputs by using it as an extension of Input, than Input itself provides at face value.
Basically, my example is just there to fill with your own content, with some comments as examples of what would go where:
if(ksSpace == KeyState.Down)
{
// "Jump" Pressed
if(IsGrounded())
{
// Changed your call to ignore mass as an example
// by using ForceMode.VelocityChange
_rb.AddForce(Vector3.up * jumpVelocity, ForceMode.VelocityChange);
}
// Cycle KeyState to next state
ksSpace = KeyState.Held;
}
else if(ksSpace == KeyState.Held)
{
// Holding "Jump"
// If you want to influence the height of the jump
// by holding the key, you could add continuous
// force here.
// Example ignores mass using ForceMode.Acceleration
_rb.AddForce(Vector3.up * hoverStrength, ForceMode.Acceleration);
}
else if(ksSpace == KeyState.Up)
{
// "Jump" Released
// Any desired change in behavior could be handled
// here, such as triggering an animation change
// cycle KeyState to next state
ksSpace = KeyState.Off;
}
For reference, I used else if()
blocks specifically to ensure you won't hit multiple on the same frame with the same key input (notably, changing from "Down" to "Held" without also triggering the "Held" action simultaneously). However, they could just as easily be reordered (putting "Held" first), since "Held" would be the most common occurrence aside from "Off".
@Eno-Khaon, thank you for your time. You have been very helpful in understanding how to do this. I think I'll be using enumerators for this task from now on, gonna try and get this one to work in my script. Thank you!
This is not a correct solution and will miss inputs. Multiple Update calls typically happen between each FixedUpdate call. So it can easily happen that ksSpace receives Up and Down in two consecutive Update calls and the Up state never reaches FixedUpdate. Here's a correct solution: http://answers.unity.com/answers/1900333/view.html
That's a good point with respect to ensuring that the input goes through no matter what. My approach was more focused on isolating input phases themselves, especially for the context of held-input-means-higher-jump.
Guaranteeing the initial jump is certainly important though, so your approach does hold significant value. To that end, adding in a "jump" variable rather than tying it specifically to the keypress in my simple example above would just as easily allow jump = true;
to be viable in Update().
(Having said that, though, I also specifically stated that it wasn't meant to be a complete solution as-is...)
Answer by tyruji · Jul 24, 2021 at 10:53 PM
As of right now you are not checking for jump in the Update message - you are doing it in FixedUpdate. As you have already learned - you shouldn't check for input in FixedUpdate in short, that's the reason why your input sometimes is detected and sometimes not. You can just put the jump logic in the Update message and it will work fine.
Thank you @tyruji , that is what I was going to end up doing for the sake of getting this thing to work, just wasn't sure if this was the "best practice" since there's so much out there that says to keep physics interactions inside FixedUpdate().
Your answer
Follow this Question
Related Questions
Is it okay to use ForceMode.VelocityChange in Update()? 1 Answer
Supplying Input from Update To FixedUpdate 1 Answer
Mouse Input for RayCast - Update or FixedUpdate 1 Answer
Input and applying physics, Update or FixedUpdate? 3 Answers
Why Do Inputs Have to Been in Update (Rather than FixedUpdate)? 3 Answers