Search Unity

Network Prediction with Built-In Physics

Discussion in 'Scripting' started by AxisRob, Jul 30, 2014.

  1. AxisRob

    AxisRob

    Joined:
    Aug 6, 2013
    Posts:
    33
    Hi. I'm making a game. It's like pong, but it uses Unity's built in physics engine.

    As I've come to understand it, the engine isn't managed, so prediction for it won't work. This is proving to be quite the headache. I've spent days looking at different ideas, but I'm having severe difficulty implementing any of them.

    To put it succinctly, the ball and paddles are phsyics controlled, and while I have the ball moving fairly smoothly, the paddle that isn't host misses the ball often because I can't figure out how to make it predict where the ball is going to be. Any advice, at all, would be really appreciated.

    If you could point me to the right place for a solution that you know would work, or anything, it would be very helpful. I'm concerned that what I'm trying to do isn't possible in Unity, or maybe isn't possible without a dedicated, third-party server.

    Thank you for your time.
     
  2. hammil

    hammil

    Joined:
    Jun 5, 2013
    Posts:
    56
    If the server sending the ball's position and velocity to the client, the client (with appropriate code) should be able to predict with reasonable accuracy where the ball will end up using normal newtonian physics, as long as all the information is correct. You might need to take things like friction into account, depending on the physics materials you're using. Lag might also be a problem; the client will need to take this into account and add a small offset to the time.
     
  3. lrlelaldl

    lrlelaldl

    Joined:
    Jul 27, 2014
    Posts:
    75
    The most simple way would be to just have the paddle move up if th eball is over it, or down if it is under it, which works for fairly "normal" angles
     
  4. AxisRob

    AxisRob

    Joined:
    Aug 6, 2013
    Posts:
    33
    See, this is awesome, and what I would like to do. Let me show you what I've got so far, and where I get stuck.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class WatchBallMovementScript : MonoBehaviour
    5. {
    6.     //Area for tracking network states
    7.     #region
    8.     networkState[] stateBuffer = new networkState[20];
    9.     int stateCount = 0; //Number of recorded states
    10.     public float InterpolationBackTime = 0.2f;
    11.  
    12.     #endregion
    13.  
    14.     // Update is called once per frame
    15.     void FixedUpdate()
    16.     {
    17.         if (networkView.isMine || stateCount == 0)
    18.             return;
    19.         if (!networkView.isMine)
    20.         {
    21.             SyncedMovement();
    22.         }
    23.     }
    24.  
    25.     void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
    26.     {
    27.  
    28.         Vector3 position = Vector3.zero;
    29.         //dothis?
    30.  
    31.         if (stream.isWriting)
    32.         {
    33.             position = transform.position;
    34.             stream.Serialize(ref position);
    35.         }
    36.         else
    37.         {
    38.             bufferState(new networkState(position, info.timestamp));
    39.         }
    40.     }
    41.  
    42.     void SyncedMovement()
    43.     {
    44.         double currentTime = Network.time;
    45.         double interpolationTime = currentTime - InterpolationBackTime;
    46.  
    47.         //If the latest packet is newer than our interpolation time, we can interpolate
    48.         if (stateBuffer[0].Timestamp > interpolationTime)
    49.         {
    50.             for (int i = 0; i < stateCount; i++)
    51.             {
    52.                 //find the closest state that matches network time, or use the oldest state
    53.                 if (stateBuffer[i].Timestamp <= interpolationTime || i == stateCount - 1)
    54.                 {
    55.                     //the state closest to the network time
    56.                     networkState lhs = stateBuffer[i];
    57.  
    58.                     //the state one slot newer
    59.                     networkState rhs = stateBuffer[Mathf.Max(i - 1, 0)];
    60.  
    61.                     //use the time between lhs and rhs to interpolate
    62.                     double length = rhs.Timestamp - lhs.Timestamp;
    63.  
    64.                     float t = 0f;
    65.                     if (length > 0.0001)
    66.                     {
    67.                         t = (float)((interpolationTime - lhs.Timestamp) / length);
    68.                     }
    69.                     transform.position = Vector3.Lerp(lhs.Position, rhs.Position, t);
    70.                     break;
    71.                 }
    72.             }
    73.         }
    74.     }
    75.  
    76.     //save new state to buffer
    77.     void bufferState(networkState state)
    78.     {
    79.         //shift buffer contents to accomodate new state
    80.         for (int i = stateBuffer.Length - 1; i > 0; i--)
    81.         {
    82.             stateBuffer[i] = stateBuffer[i - 1];
    83.         }
    84.  
    85.         //save new state
    86.         stateBuffer[0] = state;
    87.  
    88.         //increment state count
    89.         stateCount = Mathf.Min(stateCount + 1, stateBuffer.Length);
    90.     }
    91.  
    92.     //The struct to be used for a history of networked states
    93.     private struct networkState
    94.     {
    95.         public Vector3 Position;
    96.         public double Timestamp;
    97.  
    98.         public networkState(Vector3 pos, double time)
    99.         {
    100.             this.Position = pos;
    101.             this.Timestamp = time;
    102.         }
    103.     }
    104. }
    I realize that is a huge, huge chunk of code, so basically it breaks down to:

    save states of the game object, put the ball in an interpolated position between two chosen states.

    The ball moves buttery smooth, but what I would like to do is take those states and do something like
    "This state's position = current position + (distance determined by velocity * lagtime).

    If I don't, I have an issue where the player is playing 200 ms in the past, which isn't so bad, but the game moves VERY fast, so 200ms often makes a difference between a miss and a hit

    The frustrating part is I don't know how to do this. Where would I start? I imagine with rigidbody2d.velocity attached to the ball, but then what?
     
    Last edited: Aug 7, 2014