I have created a vertical moving platform which on a players personal screen is perfectly synced, no jittering and you keep your velocity. However for any other player when watching someone on the elevator, the player is sinking through the floor a little when going upwards and hovering when going downward. Any idea how to fix this? Ive tried the platform as a rigid body which made it 10x worse, ive used a client network transform as well as a network transform. My players use client network transforms and are controlled by character controllers.
using UnityEngine;
using Unity.Netcode;
using System.Collections.Generic;
public class VerticalPlatform : NetworkBehaviour
{
#region Variables
[Header("Settings")]
[SerializeField] private float m_speed;
[SerializeField] private Vector3 m_startPosition;
[SerializeField] private Vector3 m_endPosition;
private float m_direction;
private Dictionary<ulong, NetworkObject> m_playersOnPlatform = new Dictionary<ulong, NetworkObject>();
#endregion
#region Mono
private void Update()
{
if(Input.GetKeyDown(KeyCode.T))
{
m_direction = 1.0f;
}
if(Input.GetKeyDown(KeyCode.Y))
{
m_direction = -1.0f;
}
}
private void FixedUpdate()
{
if(m_direction == 0.0f) return;
if(IsServer)
{
// Move the platform
if (m_direction == 1.0f) MoveUp();
if (m_direction == -1.0f) MoveDown();
}
}
private void OnTriggerEnter(Collider other)
{
// Check if collider belongs to a player
var obj = other.GetComponent<NetworkObject>();
if(obj != null && obj.GetComponent<PlayerMovement>() != null)
{
// Try set parent
obj.TrySetParent(transform, true);
// Ensure player is snapped to the platform's position on parenting
var relativePosition = transform.InverseTransformPoint(obj.transform.position);
obj.transform.position = transform.TransformPoint(relativePosition);
// Add to dictionary
m_playersOnPlatform.Add(obj.OwnerClientId, obj);
}
}
private void OnTriggerExit(Collider other)
{
// Check if collider belongs to a player
var obj = other.GetComponent<NetworkObject>();
if(obj != null && obj.GetComponent<PlayerMovement>() != null)
{
// Check if players on platform
if(m_playersOnPlatform.ContainsKey(obj.OwnerClientId))
{
// Try remove parent
m_playersOnPlatform[obj.OwnerClientId].TryRemoveParent(true);
// Ensure player is snapped to the platform's position on parenting
var relativePosition = transform.InverseTransformPoint(obj.transform.position);
obj.transform.position = transform.TransformPoint(relativePosition);
// Remove from dictionary
m_playersOnPlatform.Remove(obj.OwnerClientId);
}
}
}
#endregion
#region Platform
private void MoveUp()
{
// Increase y value
var newPos = transform.position;
newPos.y += m_speed * Time.fixedDeltaTime;
// Update transform
transform.position = newPos;
// Check if y value has reached target
if(transform.position.y >= m_endPosition.y)
{
// Set to target
transform.position = m_endPosition;
// Clear direction
m_direction = 0.0f;
}
UpdatePositionClientRPC(transform.position);
}
private void MoveDown()
{
// Decrease y value
var newPos = transform.position;
newPos.y -= m_speed * Time.fixedDeltaTime;
// Update transform
transform.position = newPos;
// Check if y value has reached target
if(transform.position.y <= m_startPosition.y)
{
// Set to target
transform.position = m_startPosition;
// Clear direction
m_direction = 0.0f;
}
UpdatePositionClientRPC(transform.position);
}
[ClientRpc]
private void UpdatePositionClientRPC(Vector3 newPosition)
{
if(IsServer) return;
transform.position = newPosition;
}
#endregion
}
using Unity.Netcode;
using UnityEngine;
public class PlayerMovement : NetworkBehaviour
{
#region Variables
[Header("Camera")]
[SerializeField] private Transform m_camera;
[SerializeField] private float m_sensitivity;
[Header("Movement")]
[SerializeField] private float m_walkSpeed;
[SerializeField] private float m_jumpHeight;
private bool m_isGrounded;
private CharacterController m_controller;
#endregion
#region Mono
private void Start()
{
Cursor.lockState = CursorLockMode.Locked;
}
private void Update()
{
if(IsOwner)
{
m_isGrounded = m_controller.isGrounded;
HandleCameraInput();
HandleMoveInput();
}
}
#endregion
#region Network
public override void OnNetworkSpawn()
{
// Owner only
if(IsOwner)
{
m_controller = GetComponent<CharacterController>();
}
}
#endregion
#region Camera
private void HandleCameraInput()
{
// Get raw input
var input = PlayerInput.Instance.GetRawCameraInput() * m_sensitivity;
// Rotate camera (up and down)
m_camera.localRotation = Quaternion.Euler(new Vector3(input.y, 0.0f, 0.0f));;
// Rotate body (left and right)
transform.rotation *= Quaternion.Euler(new Vector3(0.0f, input.x, 0.0f));
}
#endregion
#region Motor
private void HandleMoveInput()
{
// Handle jump input
HandleJumpInput();
// Get raw input
var input = PlayerInput.Instance.GetRawMoveInput();
input.x *= m_walkSpeed;
input.y *= Time.deltaTime;
input.z *= m_walkSpeed;
// Apply movement
m_controller.Move(transform.TransformDirection(input));
}
private void HandleJumpInput()
{
// Get jump input
if(Input.GetKeyDown(KeyCode.Space) && m_isGrounded)
{
PlayerInput.Instance.SetVelocity(PlayerInput.Instance.GetVelocity() + Mathf.Sqrt(m_jumpHeight * -2.0f * Physics.gravity.y));
}
// Add gravity
if(!m_isGrounded)
{
PlayerInput.Instance.SetVelocity(PlayerInput.Instance.GetVelocity() + Physics.gravity.y * Time.deltaTime);
}
// Reset velocity
if(m_isGrounded && PlayerInput.Instance.GetVelocity() < 0.0f)
{
PlayerInput.Instance.SetVelocity(-2.0f);
}
}
#endregion
}
Netcode Moving Platform Desync