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 :
using Unity.Netcode;
using UnityEngine;
public class PlayerMovementPredictive : NetworkBehaviour
{
[SerializeField] private int defaultSpeed;
[SerializeField] private Transform turret;
[SerializeField] private Mesh debugMesh;
private Rigidbody m_Rigidbody;
private PlayerInputs m_PlayerInputs;
private Camera m_MainCamera;
private bool m_IsMousePresent;
private Vector3 m_LookTarget;
private float m_Speed;
private int m_Tick;
private const float TickRate = 1f / 60f;
private float m_TickDeltaTime;
private const int BufferSize = 1024;
private readonly InputState[] m_InputStates = new InputState[BufferSize];
private readonly TransformState[] m_TransformStates = new TransformState[BufferSize];
private readonly NetworkVariable<TransformState> m_ServerTransformState = new();
private TransformState m_PreviousTransformState;
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
m_Rigidbody = GetComponent<Rigidbody>();
m_PlayerInputs = GetComponent<PlayerInputs>();
m_MainCamera = Camera.main;
m_IsMousePresent = Input.mousePresent;
m_Speed = defaultSpeed;
m_ServerTransformState.OnValueChanged += OnServerStateChanged;
}
private void OnServerStateChanged(TransformState previousValue, TransformState newValue)
{
m_PreviousTransformState = previousValue;
}
private void Update()
{
var moveInput = m_PlayerInputs.PlayerControls.Player.Move.ReadValue<Vector2>();
UpdateLookTarget();
if (IsClient && IsLocalPlayer)
{
ProcessLocalPlayerMovement(moveInput, m_LookTarget);
}
else
{
ProcessSimulatedPlayerMovement();
}
}
private void UpdateLookTarget()
{
if (m_IsMousePresent)
{
var ray = m_MainCamera.ScreenPointToRay(m_PlayerInputs.PlayerControls.Player.Look.ReadValue<Vector2>());
if (Physics.Raycast(ray, out var hit, 100000, 1 << 9))
{
m_LookTarget = new Vector3(hit.point.x, 0, hit.point.z);
}
}
else
{
var tmpLook = m_PlayerInputs.PlayerControls.Player.Look.ReadValue<Vector2>();
if (tmpLook.x is not 0 || tmpLook.y is not 0)
{
m_LookTarget = new Vector3(tmpLook.x, 0, tmpLook.y);
}
}
}
private void ProcessLocalPlayerMovement(Vector2 moveInput, Vector3 lookTarget)
{
m_TickDeltaTime += Time.deltaTime;
if (m_TickDeltaTime > TickRate)
{
var bufferIndex = m_Tick % BufferSize;
if (!IsServer)
{
MoveWithTickServerRpc(m_Tick, moveInput, lookTarget);
Move(moveInput);
Look(lookTarget);
}
else
{
Move(moveInput);
Look(lookTarget);
var state = new TransformState()
{
Tick = m_Tick,
Position = transform.position,
Rotation = transform.rotation,
TurretRotation = turret.rotation,
IsMoving = true
};
m_ServerTransformState.Value = state;
}
var inputState = new InputState()
{
Tick = m_Tick,
MoveInput = moveInput,
LookTarget = lookTarget,
};
var transformState = new TransformState()
{
Tick = m_Tick,
Position = transform.position,
Rotation = transform.rotation,
TurretRotation = turret.rotation,
IsMoving = true
};
m_InputStates[bufferIndex] = inputState;
m_TransformStates[bufferIndex] = transformState;
m_TickDeltaTime -= TickRate;
if (m_Tick is BufferSize)
{
m_Tick = 0;
}
else
{
m_Tick += 1;
}
}
}
[ServerRpc]
private void MoveWithTickServerRpc(int tick, Vector2 moveInput, Vector3 lookTarget)
{
Move(moveInput);
Look(lookTarget);
var transformState = new TransformState()
{
Tick = tick,
Position = transform.position,
Rotation = transform.rotation,
TurretRotation = turret.rotation,
IsMoving = true
};
m_ServerTransformState.Value = transformState;
}
private void ProcessSimulatedPlayerMovement()
{
m_TickDeltaTime += Time.deltaTime;
if (m_TickDeltaTime > TickRate)
{
if (m_ServerTransformState.Value is not null && m_ServerTransformState.Value.IsMoving)
{
transform.position = m_ServerTransformState.Value.Position;
transform.rotation = m_ServerTransformState.Value.Rotation;
turret.rotation = m_ServerTransformState.Value.TurretRotation;
}
m_TickDeltaTime -= TickRate;
if (m_Tick is BufferSize)
{
m_Tick = 0;
}
else
{
m_Tick += 1;
}
}
}
private void Move(Vector2 moveInput)
{
moveInput = Vector2.ClampMagnitude(moveInput, 1);
var moveVector = new Vector3(moveInput.x, 0, moveInput.y);
if (moveVector != Vector3.zero)
{
m_Rigidbody.velocity = moveVector * m_Speed;
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(moveVector), 0.1f);
}
else
{
m_Rigidbody.velocity = Vector3.Slerp(m_Rigidbody.velocity, Vector3.zero, 0.1f);
}
}
private void Look(Vector3 lookTarget)
{
if (lookTarget == Vector3.zero)
return;
if (m_IsMousePresent)
{
lookTarget -= transform.position;
}
lookTarget.y = 0;
turret.rotation = Quaternion.LookRotation(lookTarget);
}
private void OnDrawGizmos()
{
if (m_ServerTransformState.Value is not null)
{
Gizmos.color = Color.magenta;
Gizmos.DrawMesh(debugMesh, m_ServerTransformState.Value.Position, m_ServerTransformState.Value.Rotation);
}
}
}
Also my player’s components :
I am using Unity 2022.3.1f1 and NGO 1.4.0
Thank you !