Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Resolved Child gameobject lagging behind parent network object

Discussion in 'Netcode for GameObjects' started by Complex77, Nov 30, 2023.

  1. Complex77

    Complex77

    Joined:
    Feb 28, 2013
    Posts:
    32
    Hi, I have a network object that calls a client rpc on spawn so clients can instanciate the corresponding model (gameobject) for that network object.

    This works fine but when I move the parent (the network object) the child is lagging behind the parent position and stutters

    This is the hierarchy:
    >Base object (network object that moves with physics)
    >>Model game object (just a game object with a 3d model)


    Does anybody know why the child lags behind the parent and how to solve this?
    Thanks
     
  2. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    244
  3. Complex77

    Complex77

    Joined:
    Feb 28, 2013
    Posts:
    32
    Hi and thanks for answering
    the parent has NetworkTransform and NetworkRigidbody, yes
    and the motion is server authoritative. i add some forces and torques to the parent for motion
     
  4. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    244
    Do you have interpolation enabled?
    Is there a way you could share your script that handles applying the forces/torque to the rigid body?
     
  5. Complex77

    Complex77

    Joined:
    Feb 28, 2013
    Posts:
    32
    No, im not using interpolation, ill try that tho

    in our model, server updates rigidbodys information, while client shows particle effects based on ShipCommandData, which is a network variable of a class with the user input

    Code (CSharp):
    1. using Unity.Collections;
    2. using Unity.Netcode;
    3. using UnityEngine;
    4.  
    5. [RequireComponent(typeof(Ship))]
    6. public class ShipEngineController : NetworkBehaviour
    7. {
    8.     private Ship Ship;
    9.  
    10.     [SerializeField]
    11.     ParticleSystem m_Thrust;
    12.     ParticleSystem.MainModule m_ThrustMain;
    13.     private NetworkVariable<float> m_Thrusting = new NetworkVariable<float>();
    14.     Rigidbody m_Rigidbody;
    15.  
    16.     void Awake()
    17.     {
    18.         Ship = GetComponent<Ship>();
    19.         m_Rigidbody = GetComponent<Rigidbody>();
    20.  
    21.         m_ThrustMain = m_Thrust.main;
    22.     }
    23.  
    24.     void Update()
    25.     {
    26.         if (IsServer) UpdateServer();
    27.         if (IsClient) UpdateClient();
    28.     }
    29.  
    30.     void UpdateServer()
    31.     {
    32.         // update rotation
    33.         float rotate = Ship.ShipCommandData.Value.ShipHorizontalThruster * Ship.data.RotateSpeed;
    34.  
    35.         m_Rigidbody.angularVelocity = new Vector3(0, -rotate, 0);
    36.         //m_Rigidbody.AddTorque(new Vector3(0, -rotate, 0));
    37.  
    38.         // update thrust
    39.         if (Ship.ShipCommandData.Value.ShipVerticalThruster != 0)
    40.         {
    41.             float accel = Ship.data.Acceleration;
    42.  
    43.             Vector3 thrustVec = transform.forward * (Ship.ShipCommandData.Value.ShipVerticalThruster * accel);
    44.             m_Rigidbody.AddForce(thrustVec);
    45.  
    46.             // restrict max speed
    47.             float top = Ship.data.TopSpeed;
    48.  
    49.             if (m_Rigidbody.velocity.magnitude > top)
    50.             {
    51.                 m_Rigidbody.velocity = m_Rigidbody.velocity.normalized * top;
    52.             }
    53.         }
    54.     }
    55.  
    56.     void UpdateClient()
    57.     {
    58.  
    59.         if (!IsLocalPlayer) return;
    60.  
    61.         // control thrust particles
    62.         if (Ship.ShipCommandData.Value.ShipVerticalThruster == 0.0f)
    63.         {
    64.             m_ThrustMain.startLifetime = 0.1f;
    65.             m_ThrustMain.startSize = 1f;
    66.             GetComponent<AudioSource>().Pause();
    67.         }
    68.         else
    69.         {
    70.             m_ThrustMain.startLifetime = 0.4f;
    71.             m_ThrustMain.startSize = 1.2f;
    72.             GetComponent<AudioSource>().Play();
    73.         }
    74.     }
    75. }
    76.  
     
    Last edited: Dec 1, 2023
  6. Complex77

    Complex77

    Joined:
    Feb 28, 2013
    Posts:
    32


    this is whats happening, the GUI and camera follow the network object correctly, but the model (a child of the network object) does not
     
  7. Complex77

    Complex77

    Joined:
    Feb 28, 2013
    Posts:
    32
    ok, solved, it seems it was a camera issue using a network variable to set position instead of the network object reference
     
    NoelStephens_Unity likes this.
  8. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    244
    Glad to hear you resolved your issue!
    Here is an alternate version of your script in the event you are interested:

    Code (CSharp):
    1.    
    2. #if UNITY_EDITOR
    3. using UnityEditor;
    4. // This bypases the default custom editor for NetworkTransform
    5. // and lets you modify your custom NetworkTransform's properties
    6. // within the inspector view
    7. [CustomEditor(typeof(ShipEngineController), true)]
    8. public class ShipEngineControllerEditor : Editor
    9. {
    10. }
    11. #endif
    12. [RequireComponent(typeof(Ship))]
    13.     public class ShipEngineController : NetworkTransform
    14.     {
    15.         public enum AuthorityModes
    16.         {
    17.             Server,
    18.             Owner
    19.         }
    20.  
    21.         [SerializeField]
    22.         private AuthorityModes m_AuthorityMode = AuthorityModes.Server;
    23.         [SerializeField]
    24.         private ParticleSystem m_Thrust;
    25.  
    26.         private Ship Ship;
    27.         private ParticleSystem.MainModule m_ThrustMain;
    28.         private NetworkVariable<float> m_Thrusting = new NetworkVariable<float>();
    29.         private Rigidbody m_Rigidbody;
    30.  
    31.         /// <summary>
    32.         /// Determines the authority mode.
    33.         /// </summary>
    34.         protected override bool OnIsServerAuthoritative()
    35.         {
    36.             return m_AuthorityMode == AuthorityModes.Server;
    37.         }
    38.  
    39.         protected override void Awake()
    40.         {
    41.             // Always invoke the base Awake method when deriving from NetworkTransform
    42.             base.Awake();
    43.  
    44.             Ship = GetComponent<Ship>();
    45.             m_Rigidbody = GetComponent<Rigidbody>();
    46.             m_ThrustMain = m_Thrust.main;
    47.         }
    48.  
    49.         protected override void Update()
    50.         {
    51.             // Don't do anything if we are not spawned
    52.             if (!IsSpawned)
    53.             {
    54.                 return;
    55.             }
    56.  
    57.             if (CanCommitToTransform)
    58.             {
    59.                 AuthorityUpdate();
    60.             }
    61.  
    62.             // I think you want this to be invoked on all instances to reflect the thruster FX for all instances
    63.             // (Used to be UpdateClient)
    64.             UpdateFx();
    65.  
    66.             // Invokong the base Update applies the authority's state
    67.             // If you want to take control over the object locally (only locally) you can
    68.             // add a condition arond this to skip updating the authority state updates and interpolation
    69.             base.Update();
    70.         }
    71.  
    72.         private void AuthorityUpdate()
    73.         {
    74.             // update rotation
    75.             float rotate = Ship.ShipCommandData.Value.ShipHorizontalThruster * Ship.data.RotateSpeed;
    76.  
    77.             m_Rigidbody.angularVelocity = new Vector3(0, -rotate, 0);
    78.             //m_Rigidbody.AddTorque(new Vector3(0, -rotate, 0));
    79.  
    80.             // update thrust
    81.             if (Ship.ShipCommandData.Value.ShipVerticalThruster != 0)
    82.             {
    83.                 float accel = Ship.data.Acceleration;
    84.  
    85.                 Vector3 thrustVec = transform.forward * (Ship.ShipCommandData.Value.ShipVerticalThruster * accel);
    86.                 m_Rigidbody.AddForce(thrustVec);
    87.  
    88.                 // restrict max speed
    89.                 float top = Ship.data.TopSpeed;
    90.  
    91.                 if (m_Rigidbody.velocity.magnitude > top)
    92.                 {
    93.                     m_Rigidbody.velocity = m_Rigidbody.velocity.normalized * top;
    94.                 }
    95.             }
    96.         }
    97.  
    98.         /// <summary>
    99.         /// Update the FX for all ships
    100.         /// Formerly UpdateClient
    101.         /// </summary>
    102.         private void UpdateFx()
    103.         {
    104.             // control thrust particles on all non-
    105.             if (Ship.ShipCommandData.Value.ShipVerticalThruster == 0.0f)
    106.             {
    107.                 m_ThrustMain.startLifetime = 0.1f;
    108.                 m_ThrustMain.startSize = 1f;
    109.                 GetComponent<AudioSource>().Pause();
    110.             }
    111.             else
    112.             {
    113.                 m_ThrustMain.startLifetime = 0.4f;
    114.                 m_ThrustMain.startSize = 1.2f;
    115.                 GetComponent<AudioSource>().Play();
    116.             }
    117.         }
    118.  
    119.         /// <summary>
    120.         /// Just making you aware of this, right after the authority pushes a state update this is invoked.
    121.         /// It can be useful to know what was most recently updated on the transform.
    122.         /// </summary>
    123.         protected override void OnAuthorityPushTransformState(ref NetworkTransformState networkTransformState)
    124.         {
    125.             base.OnAuthorityPushTransformState(ref networkTransformState);
    126.         }
    127.  
    128.         /// <summary>
    129.         /// Just making you aware of this, right after the non-authority receives an authority state update this is invoked.
    130.         /// It can be useful to know what was the most recent authoritative state update is and what the previous one was.
    131.         /// </summary>
    132.         protected override void OnNetworkTransformStateUpdated(ref NetworkTransformState oldState, ref NetworkTransformState newState)
    133.         {
    134.             base.OnNetworkTransformStateUpdated(ref oldState, ref newState);
    135.         }
    136.     }
     
  9. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    244
    You would replace your NetworkTransform and ShipEngineController with this... not sure if it makes sense but as an alternative.
     
  10. Complex77

    Complex77

    Joined:
    Feb 28, 2013
    Posts:
    32
    Oh that's a very interesting approach! Thanks for the feedback!!
     
  11. Complex77

    Complex77

    Joined:
    Feb 28, 2013
    Posts:
    32
    Very useful! Thanks again!