Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

How to fix Jitter in 2D Movement?

Discussion in '2D' started by MrDoodleFace, Feb 10, 2019.

  1. MrDoodleFace

    MrDoodleFace

    Joined:
    Aug 18, 2018
    Posts:
    3
    I have been working on a physics-based 2D platformer. I have been trying for roughly a week to get my movement feeling good, but I can't seem to get rid of this 'jitter' (I think its jitter, but it might be something else). It is very subtle but has an effect on how responsive the movement feels. This is what it currently looks like:



    I am using my own physics (trigger + circlecast for collision detection, kinematic rigidbody2D, my own collision resolution), I use a force accumulator and update velocity in fixed update (I use Time.fixedDeltaTime as the duration of the forces), and update position in fixed update. I read update in Update() (not in FixedUpdate() to avoid dropped inputs).

    I have the following tried the following the fix it, with none working:
    • Moving position update to Update() and using Time.deltaTime
    • Moving force, velocity and position updates to Update() and using Time.deltaTime
    • Calling all updates in both Update() and FixedUpdate() and using Time.(fixed)deltaTime, as well as using the time since Update/FixedUpate was called (calculated using Time.time).
    • Using GetAxisRaw and GetAxis to get input
    • The scripts in the package referenced by this article this article.
    Here is the relevant code (this version is using time since last frame (deltaTime) for updates0:

    Velocity, Force and Position Update Code:

    Code (CSharp):
    1.    
    2.     // Code in RigidPlayerPhysics.cs
    3.     float lastUpdateTime;
    4.     float deltaTime;
    5.  
    6.     private void Update()
    7.     {
    8.         deltaTime = Time.time - lastUpdateTime;
    9.         PositionUpdate();
    10.         lastUpdateTime = Time.time;
    11.     }
    12.  
    13.     private void FixedUpdate()
    14.     {
    15.         deltaTime = Time.time - lastUpdateTime;
    16.         ForceUpdate();
    17.         VelocityUpdate();
    18.         PositionUpdate();
    19.         ApplyGravity();
    20.  
    21.         lastUpdateTime = Time.time;
    22.     }
    23.  
    24.     private void ApplyGravity()
    25.     {
    26.         AddForce(Vector2.down * baseGravity * gravityScale);
    27.     }
    28.  
    29.     private void ForceUpdate()
    30.     {
    31.         if (deltaTime != 0)
    32.         {
    33.             f += impulse * mass / (Time.time - lastUpdateTime);
    34.             impulse = Vector2.zero;
    35.         }
    36.     }
    37.  
    38.     private void VelocityUpdate()
    39.     {
    40.         if (deltaTime != 0)
    41.         {
    42.             v += f * (Time.time - lastUpdateTime) / mass;
    43.  
    44.             v.x = StickyMath.MinAbs(v.x, Mathf.Sign(v.x) * maxHorizSpeed);
    45.             v.x = (Mathf.Abs(v.x) < moveThreshold) ? 0 : v.x;
    46.  
    47.             f = Vector2.zero;
    48.         }
    49.     }
    50.  
    51.     private void PositionUpdate()
    52.     {
    53.         transform.position += new Vector3(v.x, v.y) * (Time.time - lastUpdateTime);
    54.     }
    55.  
    56.     public void AddForce( Vector2 addedF )
    57.     {
    58.         f += addedF;
    59.     }
    60.  
    61.     public void ApplyImpulse(Vector2 velToAdd)
    62.     {
    63.         impulse += velToAdd;
    64.     }

    Input and Impulse Update Code:

    Code (CSharp):
    1. //Input Reading and Impulse application to physics script
    2. void Update()
    3.     {
    4.         float xIn = Input.GetAxis("Horizontal");
    5.         ImpulseMoveOnInput(xIn);
    6.     }
    7.  
    8.     void ImpulseMoveOnInput(float xIn)
    9.     {
    10.         Vector2 vCurr = rpp.CurrentVel(); //rpp is a refernce to RigidPlayerPhysics (my physics script)
    11.         xIn = Mathf.Sign(xIn)*Mathf.CeilToInt(Mathf.Abs(xIn));
    12.         Vector2 vNew = xIn * speed * Vector2.right;
    13.         rpp.ApplyImpulse(vNew - vCurr);
    14.     }
    15.  
    NB: I am using Unity version 2018.3.2f1

    Is there anything else I could try that might fix this?

    Thanks!
    Milan
     
  2. herb_nice

    herb_nice

    Joined:
    May 4, 2017
    Posts:
    170
    There are a bunch of threads on the inherent jitter problem in unity. The real big demons are Time.deltaTime and fixed update.

    almost all displays strobe an image at a fixed rate. let's say 60hz, but it does vary on every device slightly. nothing you do will change this.

    if you simulate a duration more or less than 1/60 seconds, things will move a little too far or not far enough... this is visible as jitter.

    unity DOES NOT provide a stable delta time even when operating under vsync. Time.unscaledDeltaTime is the duration since the last time Update() was called, pretty much. This fluctuates and is NOT the same as the duration a frame is displayed- and therefore causes jitter.

    fixedUpdate, interpolation and extrapolation all compound the problem.

    I've done a bunch of crazy workarounds to get around these problems in my game. There is a thread here with a bunch of info: https://forum.unity.com/threads/time-deltatime-not-constant-vsync-camerafollow-and-jitter.430339/

    summary:

    for me, i use vsync and calculate my own deltaTime that is quantized to the display's refresh rate. Physics2D.autoSimulate is off, as are interpolation and extrapolation. I do not use unity's animators, particles, etc., either as they all use unity's jittery deltaTime internally. I do not use fixed update. I do call Physics2d.Simulate once a frame with my quantized deltatime from my own Update loop, and use my own procedural animation, camera and particle systems that use my deltaTime. This is what you have to do to fix jitter in unity currently.
     
    Last edited: Feb 10, 2019
  3. herb_nice

    herb_nice

    Joined:
    May 4, 2017
    Posts:
    170
    I should add that Time.captureFrameRate can be used to force Time.deltaTime to a constant value, but it is somewhat brute-force and will slowdown instead of dropping frames when under load, etc. Might be worth experimenting with.
     
  4. MrDoodleFace

    MrDoodleFace

    Joined:
    Aug 18, 2018
    Posts:
    3
    Hey, thanks for your reply!

    I understand that there are some underlying jitter issues as you have said, but I have had projects in the past, and have looked at friends' projects that have very little jitter compared to this project, all of which used RigidBody2D, and thought I might have something in my physics code causing additional jitter.

    Regardless, I will look into doing as you suggested as I would like to have as little jitter as is possible with Unity.
    Thanks again
     
  5. herb_nice

    herb_nice

    Joined:
    May 4, 2017
    Posts:
    170
    well, the way you are using deltatime in your fixed update looks like it could be trouble, but the nature of fixed update itself is weird and bad if you are trying to avoid jitter.

    Fixed update is an old idea from days when simulating a frame was so expensive that you could not afford to do it once every frame- and leads to lumpy performance, laggy feeling input and inaccurate, interpolated visuals.

    simplifying synchronization problems by disabling Physics2D.autoSimulate, disabling all rigidbody interpolation and extrapolation and completely ignoring the concept of fixed update will actually make your life a lot easier. and make your game feel a lot tighter.

    You can do this by managing your own update loop that includes a call to Physics2D.Simulate() exactly when you want it to happen in relation to input, camera and everything else. Just like we used to in any other engine...

    Once you have that done, you could try using Time.captureFrameRate to quantize your deltaTime, especially if you are using unity animations and/or particle systems. You should probably try to detect when to drop frames and modulate it.

    Or, you can get mad and just do everything that involves deltaTime yourself like i do.
     
    Last edited: Feb 11, 2019
  6. herb_nice

    herb_nice

    Joined:
    May 4, 2017
    Posts:
    170
    also why are you updating the position in both fixed update and normal update? that could be trouble...