Physics for elevator in multiplayer (NetCode)

I had some problems in creating an elevator that would carry the player in a multiplayer game.
Being multiplayer and most of the game being base on physics, it was hard to figure a solution that would sync smoothly in multiplayer and also work on the server side.

I look into a lot of topics on this and couldn’t find something satifying, but finally, I have my own solution that I want to share in case someone has problems in creating it.

Basically, the problems I encountered:

  • I needed the elevator to go down or up. For that, simply applying rb.MovePosition was enough, at least, it looked good. The problem?! When it snaps from current position to the new one, the player, having gravity, will be falling but with some delay, which resulted in some shaking of the player (not good)
  • Found some posts where people suggested to simply set the elevator as parent for the player that enters it. Didn’t work for me as I use physics.
  • Another solution would be to simply apply acceleration to the elevator (just like in real life) but that means that the rigidbody cannot be kinematic and when a player gets on the elevator, the elevator will go down, pushed by the gravity of the player. You cannot set kinematic or freeze the position.
  • So I thought that I would keep tracking of all forces that would act on my elevator and create opposing forces to cancel them (the idea would be that a player entering on the platform would create a down force with the gravity, and I would apply the same force but upwards to cancel the gravity of the player pushing the platform). Sounds good, doesn’t work. Syncronization is a nightmare for this approach and a lot of edge cases.

So finally, found the perfect combination. What if I use MovePosition for the rb, keeping the rb kinematic and simulate acceleration on the distances applied using the MovePosition?? Well, it works like a charm. Elevator starts going down and the player will stay on the platform and will decrease in altitude smoothly along with the platform. There are some things to consider, but I attached my code for the elevator.

I just hope that at least one person will use this and save some time :slight_smile:

using Unity.Netcode;
using UnityEngine;

public class ElevatorController : NetworkBehaviour
{
    private float maxSpeed = 3f;
    private float acceleration = 0.05f;
    private float stopDistance = 1f;
    private bool upDirection = false;
    private float currentSpeed = 0f;
    private Rigidbody rb;

    public static bool activated = false;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void FixedUpdate()
    {
        if (!IsServer) return;

        if (activated)
        {
            MoveElevator();
        }
        rb.isKinematic = true;
    }

    private void MoveElevator()
    {
        Vector3 currentPosition = rb.position;
        float targetY = upDirection ? -3f : -50f;
        Vector3 targetPosition = new Vector3(currentPosition.x, targetY, currentPosition.z);
        Vector3 direction = (targetPosition - currentPosition).normalized;
        float distanceToTarget = Mathf.Abs(currentPosition.y - targetY);

        if (distanceToTarget <= stopDistance)
        {
            currentSpeed = Mathf.Max(0f, currentSpeed - acceleration);
        }
        else
        {
            currentSpeed = Mathf.Min(maxSpeed, currentSpeed + acceleration);
        }

        Vector3 movement = direction * currentSpeed * Time.fixedDeltaTime;
        rb.MovePosition(currentPosition + movement);

        if (distanceToTarget <= 0.01f)
        {
            rb.position = targetPosition;
            currentSpeed = 0f;
            activated = false;
            upDirection = !upDirection;
        }
    }
}