Search Unity

Resolved Clients Rigidbodies aren't moving on Server

Discussion in 'Netcode for GameObjects' started by Faysou, Jun 13, 2023.

  1. Faysou

    Faysou

    Joined:
    Feb 22, 2021
    Posts:
    12
    Hello everyone,
    I am making a ClientPrediction using this tutorial : Unity Netcode For Gameobject - Client Prediction - YouTube.
    Everything is working correctly, but my players aren't moving server side.
    What I can tell you is that my problem is specific to rigidbodies : replacing m_Rigidbody.velocity = ... by Transform.Translate(...) is working on both sides. So the server is correctly receiving infos, but it is blocked.

    Here is my class for moving :
    Code (CSharp):
    1. using Unity.Netcode;
    2. using UnityEngine;
    3.  
    4. public class PlayerMovementPredictive : NetworkBehaviour
    5. {
    6.     [SerializeField] private int defaultSpeed;
    7.  
    8.     [SerializeField] private Transform turret;
    9.  
    10.     [SerializeField] private Mesh debugMesh;
    11.  
    12.     private Rigidbody m_Rigidbody;
    13.     private PlayerInputs m_PlayerInputs;
    14.     private Camera m_MainCamera;
    15.  
    16.     private bool m_IsMousePresent;
    17.  
    18.     private Vector3 m_LookTarget;
    19.     private float m_Speed;
    20.  
    21.  
    22.  
    23.     private int m_Tick;
    24.     private const float TickRate = 1f / 60f;
    25.     private float m_TickDeltaTime;
    26.  
    27.     private const int BufferSize = 1024;
    28.  
    29.     private readonly InputState[] m_InputStates = new InputState[BufferSize];
    30.     private readonly TransformState[] m_TransformStates = new TransformState[BufferSize];
    31.  
    32.     private readonly NetworkVariable<TransformState> m_ServerTransformState = new();
    33.     private TransformState m_PreviousTransformState;
    34.  
    35.     public override void OnNetworkSpawn()
    36.     {
    37.         base.OnNetworkSpawn();
    38.         m_Rigidbody = GetComponent<Rigidbody>();
    39.         m_PlayerInputs = GetComponent<PlayerInputs>();
    40.         m_MainCamera = Camera.main;
    41.      
    42.         m_IsMousePresent = Input.mousePresent;
    43.         m_Speed = defaultSpeed;
    44.  
    45.         m_ServerTransformState.OnValueChanged += OnServerStateChanged;
    46.     }
    47.  
    48.     private void OnServerStateChanged(TransformState previousValue, TransformState newValue)
    49.     {
    50.         m_PreviousTransformState = previousValue;
    51.     }
    52.  
    53.     private void Update()
    54.     {
    55.         var moveInput = m_PlayerInputs.PlayerControls.Player.Move.ReadValue<Vector2>();
    56.         UpdateLookTarget();
    57.      
    58.         if (IsClient && IsLocalPlayer)
    59.         {
    60.             ProcessLocalPlayerMovement(moveInput, m_LookTarget);
    61.         }
    62.         else
    63.         {
    64.             ProcessSimulatedPlayerMovement();
    65.         }
    66.     }
    67.  
    68.     private void UpdateLookTarget()
    69.     {
    70.         if (m_IsMousePresent)
    71.         {
    72.             var ray = m_MainCamera.ScreenPointToRay(m_PlayerInputs.PlayerControls.Player.Look.ReadValue<Vector2>());
    73.          
    74.             if (Physics.Raycast(ray, out var hit, 100000, 1 <<  9))
    75.             {
    76.                 m_LookTarget = new Vector3(hit.point.x, 0, hit.point.z);
    77.             }
    78.         }
    79.         else
    80.         {
    81.             var tmpLook = m_PlayerInputs.PlayerControls.Player.Look.ReadValue<Vector2>();
    82.             if (tmpLook.x is not 0 || tmpLook.y is not 0)
    83.             {
    84.                 m_LookTarget = new Vector3(tmpLook.x, 0, tmpLook.y);
    85.             }
    86.         }
    87.     }
    88.  
    89.     private void ProcessLocalPlayerMovement(Vector2 moveInput, Vector3 lookTarget)
    90.     {
    91.         m_TickDeltaTime += Time.deltaTime;
    92.  
    93.         if (m_TickDeltaTime > TickRate)
    94.         {
    95.             var bufferIndex = m_Tick % BufferSize;
    96.  
    97.             if (!IsServer)
    98.             {
    99.                 MoveWithTickServerRpc(m_Tick, moveInput, lookTarget);
    100.                 Move(moveInput);
    101.                 Look(lookTarget);
    102.             }
    103.             else
    104.             {
    105.                 Move(moveInput);
    106.                 Look(lookTarget);
    107.              
    108.                 var state = new TransformState()
    109.                 {
    110.                     Tick = m_Tick,
    111.                     Position = transform.position,
    112.                     Rotation = transform.rotation,
    113.                     TurretRotation = turret.rotation,
    114.                     IsMoving = true
    115.                 };
    116.  
    117.                 m_ServerTransformState.Value = state;
    118.             }
    119.  
    120.             var inputState = new InputState()
    121.             {
    122.                 Tick = m_Tick,
    123.                 MoveInput = moveInput,
    124.                 LookTarget = lookTarget,
    125.             };
    126.          
    127.             var transformState = new TransformState()
    128.             {
    129.                 Tick = m_Tick,
    130.                 Position = transform.position,
    131.                 Rotation = transform.rotation,
    132.                 TurretRotation = turret.rotation,
    133.                 IsMoving = true
    134.             };
    135.          
    136.             m_InputStates[bufferIndex] = inputState;
    137.             m_TransformStates[bufferIndex] = transformState;
    138.          
    139.             m_TickDeltaTime -= TickRate;
    140.             if (m_Tick is BufferSize)
    141.             {
    142.                 m_Tick = 0;
    143.             }
    144.             else
    145.             {
    146.                 m_Tick += 1;
    147.             }
    148.         }
    149.     }
    150.  
    151.     [ServerRpc]
    152.     private void MoveWithTickServerRpc(int tick, Vector2 moveInput, Vector3 lookTarget)
    153.     {
    154.         Move(moveInput);
    155.         Look(lookTarget);
    156.  
    157.         var transformState = new TransformState()
    158.         {
    159.             Tick = tick,
    160.             Position = transform.position,
    161.             Rotation = transform.rotation,
    162.             TurretRotation = turret.rotation,
    163.             IsMoving = true
    164.         };
    165.  
    166.         m_ServerTransformState.Value = transformState;
    167.     }
    168.  
    169.     private void ProcessSimulatedPlayerMovement()
    170.     {
    171.         m_TickDeltaTime += Time.deltaTime;
    172.  
    173.         if (m_TickDeltaTime > TickRate)
    174.         {
    175.             if (m_ServerTransformState.Value is not null && m_ServerTransformState.Value.IsMoving)
    176.             {
    177.                 transform.position = m_ServerTransformState.Value.Position;
    178.                 transform.rotation = m_ServerTransformState.Value.Rotation;
    179.                 turret.rotation = m_ServerTransformState.Value.TurretRotation;
    180.             }
    181.          
    182.             m_TickDeltaTime -= TickRate;
    183.             if (m_Tick is BufferSize)
    184.             {
    185.                 m_Tick = 0;
    186.             }
    187.             else
    188.             {
    189.                 m_Tick += 1;
    190.             }
    191.         }
    192.     }
    193.  
    194.     private void Move(Vector2 moveInput)
    195.     {
    196.         moveInput = Vector2.ClampMagnitude(moveInput, 1);
    197.         var moveVector = new Vector3(moveInput.x, 0, moveInput.y);
    198.      
    199.         if (moveVector != Vector3.zero)
    200.         {
    201.             m_Rigidbody.velocity = moveVector * m_Speed;
    202.             transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(moveVector), 0.1f);
    203.         }
    204.         else
    205.         {
    206.             m_Rigidbody.velocity = Vector3.Slerp(m_Rigidbody.velocity, Vector3.zero, 0.1f);
    207.         }
    208.     }
    209.  
    210.     private void Look(Vector3 lookTarget)
    211.     {
    212.         if (lookTarget == Vector3.zero)
    213.             return;
    214.      
    215.         if (m_IsMousePresent)
    216.         {
    217.             lookTarget -= transform.position;
    218.         }
    219.      
    220.         lookTarget.y = 0;
    221.         turret.rotation = Quaternion.LookRotation(lookTarget);
    222.     }
    223.  
    224.     private void OnDrawGizmos()
    225.     {
    226.         if (m_ServerTransformState.Value is not null)
    227.         {
    228.             Gizmos.color = Color.magenta;
    229.             Gizmos.DrawMesh(debugMesh, m_ServerTransformState.Value.Position, m_ServerTransformState.Value.Rotation);
    230.         }
    231.     }
    232. }
    Also my player's components :
    upload_2023-6-13_4-41-0.png

    I am using Unity 2022.3.1f1 and NGO 1.4.0

    Thank you !
     
  2. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
  3. Faysou

    Faysou

    Joined:
    Feb 22, 2021
    Posts:
    12
    Ok now that my minds are more clear and it's not 4am I can say that disabling transform.position = m_ServerTransformState.Value.Position; (line 177) fixed the problem at the cost of... syncing.

    I believe I am doing something in the wrong order, I will try to find the issue but any help is appreciated ^^

    Also if you spot something that is not "good practice" I would love to hear about ;
     
  4. Faysou

    Faysou

    Joined:
    Feb 22, 2021
    Posts:
    12
    Hi Riku,
    I did try it, but with my setup I want to be the only master in syncing the transform so that is why I removed network transform and networking rigidbody, I still have them on my non-predictive player prefab.

    I found the issue atleast ! After some guessing I can say that ProcessSimulatedPlayerMovement() is interferring with the
    MoveWithTickServerRpc() and that is why my character happen to be "lock" on place on the server-side.

    Can you try to spot my mistake T_T ? It is in the "order" of the logic, I will try to find a fix aswell and keep you updated ;)

    Thank you for you time
     
  5. Faysou

    Faysou

    Joined:
    Feb 22, 2021
    Posts:
    12
    Ok I think I found out why, rigidbody.velocity is set over time compared to transform.translate, so I think my
    ProcessLocalPlayerMovement is stopping player velocity or something like that
     
  6. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
    Thanks for sharing! I see you're also writing to m_ServerTransformState.Value in both the RPC and the ProcessSimulatedPlayerMovement(), so you could have race conditions there
     
    Faysou likes this.
  7. Faysou

    Faysou

    Joined:
    Feb 22, 2021
    Posts:
    12
    OKAY,
    It's silly to do ProcessSimulatedPlayerMovement() in Update() if you're the server, it'll undo the move you made haha, it took me a while to figure out that was what you were saying Riku xD

    Thank you once again ^^
     
  8. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
    hehe, happy to see you found that helpful :D