There are a few problems here. First, let's look at a typical series of frames in Unity:
- Frame 1
- Read Input
- FixedUpdate
- FixedUpdate
- Update
- Draw
- Frame 2
- Read Input
- FixedUpdate
- Update
- Draw
So, the first thing to note is that you get different numbers of FixedUpdate calls from one frame to the next. This can create a visible judder, where the character appears to be moving at one speed, then you get a frame with fewer FixedUpdates and they appear to slow or stutter for a frame before continuing.
The simplest solution here is to enable Rigidbody Interpolation, and move the object using velocity. This will offset the rendered position of the object by whatever fractional timestep is needed to keep up with the frame time, without upsetting the predictable fixed timestep of the underlying simulation.
(Some folks don't like controlling characters via velocity, especially player characters, although I've generally found it quite workable. If it's not your style, you might prefer to remove the Rigidbody and move the object directly using its transform in Update, or use a character controller)
The second issue here is that you're capturing input in Update, but not actually using it until FixedUpdate. Since FixedUpdate runs before Update, that means you don't get to act on this information until the next frame, which adds latency to your game. For continuous values like Input.GetAxis
, this data is already available and usable in FixedUpdate - so you can just grab it fresh when you want to use it. (Special care needs to be given to capturing instantaneous actions like Input.GetButtonDown
though, to avoid missing or double-counting the events)
So, a fixed version might look like this - and it's simpler too! :)
public class Player : MonoBehaviour
{
public float speed = 4f;
public Rigidbody2D rb;
void FixedUpdate ()
{
rb.velocity = Vector3.right * Input.GetAxis("Horizontal") * speed;
}
}