Search Unity

Interpolation implementation guidance

Discussion in 'Multiplayer' started by Chom1czek, Sep 21, 2016.

  1. Chom1czek

    Chom1czek

    Joined:
    Sep 19, 2015
    Posts:
    66
    Hello there,

    Prolog:
    I am trying to implement click to move system with smooth movement with UNET but not using NetworkTransform component because I want more freedom with movement and learn more what's going on underneath it and I need a little bit guidance.

    Before doing research about lerp I used it incorrectly (Time.deltaTime * speed thing) but it still confuses me when trying to implement it in my network project.

    What I want to achieve:
    - Smooth movement on remote clients (host already has smooth movement)

    Movement implementation:
    Code (CSharp):
    1.     public MovementState StepTowards(MovementState previous, Vector2 destination, float stepSize)
    2.     {
    3.         Vector2 nextStep = Vector2.MoveTowards(previous.position, destination, stepSize);
    4.  
    5.         return new MovementState
    6.         {
    7.             movementNumber = previous.movementNumber,
    8.             position = nextStep,
    9.         };
    10.     }
    and I use this IEnumerator to apply movement:
    Code (CSharp):
    1.     public IEnumerator MoveTowards(Vector2 destination)
    2.     {
    3.         while(true)
    4.         {
    5.             ServerMovementState = StepTowards(serverMovementState, destination, movementSpeed * Time.deltaTime);
    6.  
    7.             if (serverMovementState.position == destination)
    8.                 yield break;
    9.  
    10.             yield return null;
    11.         }
    12.     }
    ServerMovementState is a [SyncVar] and I call MoveTowards inside [Command] so it's synced to all clients.

    Problem:
    Remote clients have jitter movement

    My lerping:
    I'm using my lerping in SyncHook method and looks like this:

    Code (CSharp):
    1.     private IEnumerator Lerping(Transform transform, Vector2 syncedPos, float duration)
    2.     {
    3.         float startTime = Time.time;
    4.         var syncedPosition = new Vector3(syncedPos.x, transform.position.y, syncedPos.y);
    5.         while(Time.time < startTime + duration)
    6.         {
    7.             transform.position = Vector3.Lerp(transform.position, syncedPosition, (Time.time - startTime) / duration);
    8.             yield return null;
    9.         }
    10.         transform.position = syncedPosition;
    11.     }
    Confusion part / Guidance part:
    1. With what float duration should I call the lerp method? What's correlation between MoveTowards results and lerping?
    2. Should I MoveTowards every frame or every fixed frame or any other fixed time amount? And how to blend it with lerp?
    3. StepSize & Lerp duration correlation and best ratio,
    4. How big StepSize should be or rather what are average?
    5. Trivial one I think: How can I control how fast I sent updates as server, if anyone says that they sent X packages per second (if I call [Cmd] in Update and I've got 100fps, does it mean I sent 100pps?)
    Thanks for any help and guidance.

    PS Should I even use MoveTowards? Wouldn't it be better to sent my destination over network and just lerp everyone towards it?
     
  2. Whippets

    Whippets

    Joined:
    Feb 28, 2013
    Posts:
    1,775
    I don't think there are right or wrong answers with networking, everyone seems to have different methods.

    1. 2. 3. 4. Pick your time-slices (see 5) - During each timeslice you want to lerp from the previous position to the new position, then update the previous position with the new position before getting a new new position. Personally, I subtract the new position from the previous position, and use that as a velocity for the current time-slice. I give the player-controller/character-controller/rigidbody that velocity.

    5. I wouldn't send Cmd in Update(), that's very frame rate dependent, and overkill. Create your own event that runs 5/10/20 times a second and use that for sending messages.
     
  3. Chom1czek

    Chom1czek

    Joined:
    Sep 19, 2015
    Posts:
    66
    I was thinking whole night about it, if my physics time step is 0.2f this means physics runs in 50fps right?
    So if I want to send for example 25 updates per second then in my coroutine I should yield for 0.04f and send update. Character after a second should move one unit so my stepSize should be 0.04f? (0.04f x 25 pps = 1unit)

    Someone also suggested to "store" or wait delay movement (is this latency?) Which I think is my missing puzzle.
    If I get pos 1 at t0 and wait to t1 and from t1 I start to lerp my pos 0 (actual) to pos 1 I will get smooth movement?

    Thanks for advice about velocity, I was thinking about it!
     
  4. Whippets

    Whippets

    Joined:
    Feb 28, 2013
    Posts:
    1,775
    0.02 = 50 times a second. 0.2 = 5 times a second - think it was probably a typo :)
    You can gather a few incoming messages and delay movement, allowing you 2 or 3 steps of history to smooth (interpolate) the positions. Bearing in mind the quantity of late or lost data packets over the network - there will be plenty unless you are using a reliable channel for movement (not recommended).
     
  5. Chom1czek

    Chom1czek

    Joined:
    Sep 19, 2015
    Posts:
    66
    Yes it was a typo, thanks :) That's what I will try to do, thanks!