Movement Jitter in FixedUpdate(), Inconsistent on Update()

Hello! I’m working on a 3D endless runner and I’m having some issues getting consistent and smooth character movement with simple custom physics.

Currently, the main character’s movement is jittery and I assume is because the character movement is being applied through FixedUpdate():

(I’m new to Unity and C#, so this code is a mix of code from a tutorial video (that never addresses these obvious issues in their code) and improvisations I have come up with to fix other minor issues I stumbled upon, so I’m sorry if it is messy and confusing.)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


[System.Serializable]
public enum SIDE { Left, Mid, Right }

public class Character : MonoBehaviour
{
    public SIDE m_Side = SIDE.Mid;
    [HideInInspector]
    public bool SwipeLeft, SwipeRight, SwipeUp;

    public int JumpState = 0;
    //JumpState = 0, false
    //JumpState = 1, true
    //JumpState = 2, interrupted mid-air

    float NewXPos = 0f;
    float verticalMovement = 0f;

    //Speed at which char travels forward
    public float ForwardSpeed = 6;
    //Distance char travels to the sides
    public float SideStepWidth;
    //Speed at which char travels to the sides
    public float SideStepSpeed;
    //Distance char travels up when jumping
    public float JumpForce;

    private float SideStepMovement;
    public float gravity;
    private CharacterController m_char;

    // Start is called before the first frame update
    void Start()
    {
        m_char = GetComponent<CharacterController>();

        transform.position = Vector3.zero;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A) | Input.GetKeyDown(KeyCode.LeftArrow))
        {
            SwipeLeft = true;
        }
        if (Input.GetKeyDown(KeyCode.D) | Input.GetKeyDown(KeyCode.RightArrow))
        {
            SwipeRight = true;
        }
        if (Input.GetKeyDown(KeyCode.W) | Input.GetKeyDown(KeyCode.UpArrow))
        {
            SwipeUp = true;
        }
    }

    private void FixedUpdate()
    {
        if (SwipeLeft)
        {
            NewXPos = NewXPos - SideStepWidth;

            if (m_Side == SIDE.Right)
            {
                m_Side = SIDE.Mid;
            }
            else
            {
                m_Side = SIDE.Left;
            }

            SwipeLeft = false;
        }
        else if (SwipeRight)
        {
            NewXPos = NewXPos + SideStepWidth;
            if (m_Side == SIDE.Left)
            {
                m_Side = SIDE.Mid;
            }
            else
            {
                m_Side = SIDE.Right;
            }
            SwipeRight = false;
        }
        SideStepMovement = Mathf.Lerp(SideStepMovement, NewXPos, Time.deltaTime * SideStepSpeed);

        Jump();

        Vector3 moveVector = new Vector3(SideStepMovement - transform.position.x, verticalMovement, ForwardSpeed * Time.deltaTime);
        m_char.Move(moveVector); 
    }

    void Jump()
    {
        if (m_char.isGrounded)
        {
            JumpState = 0;
            verticalMovement = -gravity * Time.deltaTime;
 
            if (SwipeUp)
            {
                SwipeUp = false;
                verticalMovement = JumpForce;
                JumpState = 1;
            }
        }
        else
        {
            verticalMovement -= gravity * Time.deltaTime;
        }
    }

I have tried to put this whole code into Update(), however, that causes a different issue where the character’s jump height becomes inconsistent - specifically, it jumps much higher when testing the game in “Maximize on Play” mode, compared to when the game is running in a small window.

I’ve read that the solution to this inconsistent jump is moving the physics calculation to FixedUpdate(), which brings me back to the start with jittery movement.


Is there a way I can calculate the physics in FixedUpdate() and apply the movement on Update() (I’ve tried it myself with no success), or maybe some other solution for this problem?

I believe all of that code should be in the update method, only using a fixed update for rigid body physics.

When doing FixedUpdate physics, try using Time.fixedDeltaTime instead of Time.deltaTime.