Search Unity

Potential issues/improvements in my network physics method

Discussion in 'Multiplayer' started by kaiber, Mar 6, 2013.

  1. kaiber

    kaiber

    Joined:
    Mar 4, 2013
    Posts:
    15
    So, I've been trying to crack physics over a network for a few days now, and have got a method which is working pretty well. I've tested it quite a bit, and seems pretty good, so I'd like people with more knowledge to pick some holes in it :)

    Pretty much :

    Server has full collision with all rigidbodys, so any collisions he makes are shown to the clients via a network view(smoothed via network rigid body ofc).

    When a client collides with an object, rather than apply his force directly, this is sent via an RPC to the server, which then applies the force to the body. Clients see changes as they go from the server via networkview.

    Any major problems/obvious improvements?
     
  2. SneakySly

    SneakySly

    Joined:
    Mar 5, 2013
    Posts:
    3
    Where can I get networkrigidbody?
     
  3. kaiber

    kaiber

    Joined:
    Mar 4, 2013
    Posts:
    15
    Heres what I'm using

    Code (csharp):
    1.  
    2.     using UnityEngine;
    3.     //using System.Collections;
    4.    
    5.     public class NetworkRigidbody : MonoBehaviour {
    6.     public double m_InterpolationBackTime = 0.1;
    7.     public double m_ExtrapolationLimit = 0.5;
    8.     internal struct State
    9.     {
    10.         internal double timestamp;
    11.         internal Vector3 pos;
    12.         internal Vector3 velocity;
    13.         internal Quaternion rot;
    14.         internal Vector3 angularVelocity;
    15.     }
    16.    
    17.     // We store twenty states with "playback" information
    18.     State[] m_BufferedState = new State[20];
    19.     // Keep track of what slots are used
    20.     int m_TimestampCount;
    21.     void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
    22.     {
    23.         // Send data to server
    24.         if (stream.isWriting)
    25.         {
    26.             Vector3 pos = rigidbody.position;
    27.             Quaternion rot = rigidbody.rotation;
    28.             Vector3 velocity = rigidbody.velocity;
    29.             Vector3 angularVelocity = rigidbody.angularVelocity;
    30.             stream.Serialize(ref pos);
    31.             stream.Serialize(ref velocity);
    32.             stream.Serialize(ref rot);
    33.             stream.Serialize(ref angularVelocity);
    34.         }
    35.         // Read data from remote client
    36.         else
    37.         {
    38.             Vector3 pos = Vector3.zero;
    39.             Vector3 velocity = Vector3.zero;
    40.             Quaternion rot = Quaternion.identity;
    41.             Vector3 angularVelocity = Vector3.zero;
    42.             stream.Serialize(ref pos);
    43.             stream.Serialize(ref velocity);
    44.             stream.Serialize(ref rot);
    45.             stream.Serialize(ref angularVelocity);
    46.             // Shift the buffer sideways, deleting state 20
    47.             for (int i=m_BufferedState.Length-1;i>=1;i--)
    48.             {
    49.                 m_BufferedState[i] = m_BufferedState[i-1];
    50.             }
    51.             // Record current state in slot 0
    52.             State state;
    53.             state.timestamp = info.timestamp;
    54.             state.pos = pos;
    55.             state.velocity = velocity;
    56.             state.rot = rot;
    57.             state.angularVelocity = angularVelocity;
    58.             m_BufferedState[0] = state;
    59.             // Update used slot count, however never exceed the buffer size
    60.             // Slots aren't actually freed so this just makes sure the buffer is
    61.             // filled up and that uninitalized slots aren't used.
    62.             m_TimestampCount = Mathf.Min(m_TimestampCount + 1,
    63.             m_BufferedState.Length);
    64.             // Check if states are in order, if it is inconsistent you could reshuffel or
    65.             // drop the out-of-order state. Nothing is done here
    66.             for (int i=0;i<m_TimestampCount-1;i++)
    67.             {
    68.                 if (m_BufferedState[i].timestamp < m_BufferedState[i+1].timestamp)
    69.                 Debug.Log("State inconsistent");
    70.             }
    71.         }
    72.     }
    73.    
    74.     // We have a window of interpolationBackTime where we basically play
    75.     // By having interpolationBackTime the average ping, you will usually use interpolation.
    76.     // And only if no more data arrives we will use extra polation
    77.     void Update () {
    78.         // This is the target playback time of the rigid body
    79.         double interpolationTime = Network.time - m_InterpolationBackTime;
    80.        
    81.         // Smoothing
    82.         // Use interpolation if the target playback time is present in the buffer
    83.         if (m_BufferedState[0].timestamp > interpolationTime)
    84.         {
    85.             // Go through buffer and find correct state to play back
    86.             for (int i=0;i<m_TimestampCount;i++)
    87.             {
    88.                 if (m_BufferedState[i].timestamp <= interpolationTime || i ==
    89.                     m_TimestampCount-1)
    90.                 {
    91.                     // The state one slot newer (<100ms) than the best playback state
    92.                     State rhs = m_BufferedState[Mathf.Max(i-1, 0)];
    93.                     // The best playback state (closest to 100 ms old (default time))
    94.                     State lhs = m_BufferedState[i];
    95.                     // Use the time between the two slots to determine if interpolation is necessary
    96.                     double length = rhs.timestamp - lhs.timestamp;
    97.                     float t = 0.0F;
    98.                     // As the time difference gets closer to 100 ms t gets closer to 1 in
    99.                     // which case rhs is only used
    100.                     // Example:
    101.                     // Time is 10.000, so sampleTime is 9.900
    102.                     // lhs.time is 9.910 rhs.time is 9.980 length is 0.070
    103.                     // t is 9.900 - 9.910 / 0.070 = 0.14. So it uses 14% of rhs, 86% of lhs
    104.                     if (length > 0.0001)
    105.                         t = (float)((interpolationTime - lhs.timestamp) / length);
    106.                     // if t=0 => lhs is used directly
    107.                     transform.localPosition = Vector3.Lerp(lhs.pos, rhs.pos, t);
    108.                     transform.localRotation = Quaternion.Slerp(lhs.rot, rhs.rot, t);
    109.                     return;
    110.                 }
    111.             }
    112.         }
    113.         // Use extrapolation (Prediction)
    114.         else
    115.         {
    116.             State latest = m_BufferedState[0];
    117.             float extrapolationLength = (float)(interpolationTime - latest.timestamp);
    118.             // Don't extrapolation for more than 500 ms, you would need to do that carefully
    119.             if (extrapolationLength < m_ExtrapolationLimit )
    120.             {
    121.                 float axisLength = extrapolationLength * latest.angularVelocity.magnitude
    122.                                 * Mathf.Rad2Deg;
    123.                 Quaternion angularRotation = Quaternion.AngleAxis(axisLength, latest.angularVelocity);
    124.                 rigidbody.position = latest.pos + latest.velocity * extrapolationLength;
    125.                 rigidbody.rotation = angularRotation * latest.rot;
    126.                 rigidbody.velocity = latest.velocity;
    127.                 rigidbody.angularVelocity = latest.angularVelocity;
    128.             }
    129.         }
    130.     }
    131. }
    132.  
     
  4. Uncasid

    Uncasid

    Joined:
    Oct 5, 2012
    Posts:
    193
    What model are you trying to follow? Do you have a link to the method you are trying to implement? We need a bit more information about what your intended game is supposed to play like, how many players in a room, and how many updatable network objects on average you expect.
     
    Last edited: Mar 7, 2013
  5. kaiber

    kaiber

    Joined:
    Mar 4, 2013
    Posts:
    15
    I built my system based on this (fantastic) tutorial : http://www.3dmuve.com/3dmblog/?p=201

    Its a LAN focused fps. Internet play is very optional, so I don't have to worry about internet latency (which is pretty big bonus).

    The game is similar to battlefield (fps with vehicles etc) but with a focus on fun rather than realism (exadurated physics, interesting weaponry etc). More like Codename Eagle, if you ever played that. So physics are important, as they generate most hilarious moments in games (think garrys mod).

    Player limit will be fairly low also (8 max at its current state) as the intention is for it to be played at small gatherings.
     
  6. wolodo

    wolodo

    Joined:
    Nov 22, 2012
    Posts:
    55
    Could you please make some example project and upload it somewhere? Like two player-controllable objects colliding with each other or something like that?
     
  7. fholm

    fholm

    Joined:
    Aug 20, 2011
    Posts:
    2,052
    Other then the obvious delay between action and result, you can get pretty hefty inconsistencies between client and server if a client keeps colliding into an object which in reality should have moved.