Help with players Sync (camera, movement, etc)

Hi everyone, I’m developing a simple project for college, and I’m having some problems with the synchronization of the players, I followed the 5-minute video that teaches it, but some things didn’t work.

This is the link with a video of the problem: https://www.youtube.com/watch?v=FBsmmioCAzQ

Basically, when a player is alone, the camera, flashlight and movement work normally, but when another player enters, everything bugs out.
I’m a beginner in game development, so this is my first experience with multiplayer.

here are some of the codes i’m using:

PlayerSpawnHandler.cs (with some different adjustments to the video code)

using UnityEngine;
using Coherence.Toolkit;
using Coherence.Connection;

public class PlayerSpawnHandler : MonoBehaviour
{
    [SerializeField] private GameObject playerPrefab;

    private CoherenceBridge _coherenceBridge;
    private GameObject _playerReference;

    private void OnEnable()
    {
        _coherenceBridge = FindObjectOfType<CoherenceBridge>();
        _coherenceBridge.onConnected.AddListener(OnConnected);
        _coherenceBridge.onDisconnected.AddListener(OnDisconnected);
    }

    private void OnDisable()
    {
        if (_coherenceBridge != null)
        {
            _coherenceBridge.onConnected.RemoveListener(OnConnected);
            _coherenceBridge.onDisconnected.RemoveListener(OnDisconnected);
        }
    }

    private void OnDisconnected(CoherenceBridge arg0, ConnectionCloseReason arg1)
    {
        if (_playerReference != null)
            Destroy(_playerReference);
    }

    private void OnConnected(CoherenceBridge arg0)
    {
        _playerReference = Instantiate(playerPrefab, transform.position, transform.rotation);
        
        // Você já trava o cursor dentro do CameraController, mas não faz mal reforçar aqui:
        Cursor.lockState = CursorLockMode.Locked;
    }
}

CameraController.cs:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public float mouseSensitivity = 400.0f;
    public float verticalClamp = 80.0f;
    public float cameraHeight = 2.5f;
    public float smoothFollow = 5f; // Suavização do movimento
    
    private Transform playerBody;
    private CharacterController playerCC;
    private float xRotation = 0f;
    private Vector3 targetPosition;

    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;
        playerBody = GameObject.FindGameObjectWithTag("Player").transform;
        playerCC = playerBody.GetComponent<CharacterController>();
        
        transform.localPosition = new Vector3(0, cameraHeight, 0);
        transform.localRotation = Quaternion.identity;
    }

    void Update()
    {
        HandleCameraRotation();
    }

    void LateUpdate()
    {
        // Suaviza o acompanhamento do pulo
        targetPosition = playerBody.position + Vector3.up * cameraHeight;
        transform.position = Vector3.Lerp(transform.position, targetPosition, smoothFollow * Time.deltaTime);
    }

    void HandleCameraRotation()
    {
        float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
        float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;

        xRotation -= mouseY;
        xRotation = Mathf.Clamp(xRotation, -verticalClamp, verticalClamp);

        transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
        playerBody.Rotate(Vector3.up * mouseX);
    }
}

PlayerCharacterController.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerCharacterController : MonoBehaviour
{
    public float speed = 5f;
    public float runSpeed = 8f; // Velocidade ao correr
    public float gravity = -45.0f;
    public float jumpHeight = 2f;
    private float yVelocity;
    private bool isGrounded;

    public float interactionRadius = 2f;
    public Transform cameraTransform;
    private CharacterController cc;
    public Animator animator;

    void Start()
    {
        cc = GetComponent<CharacterController>();
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }

    void Update()
    {
        HandleMovement();
        HandleJump();
        HandleCameraRotation();
        UpdateAnimations();
    }

    void HandleMovement()
    {
        isGrounded = cc.isGrounded;

        if (isGrounded && yVelocity < 0)
        {
            yVelocity = -2f;
        }

        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        // Verifica se o Shift está pressionado para correr
        float currentSpeed = Input.GetKey(KeyCode.LeftShift) ? runSpeed : speed;

        Vector3 move = transform.right * horizontal + transform.forward * vertical;
        cc.Move(move * currentSpeed * Time.deltaTime);
    }

    void HandleJump()
    {
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            yVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity);
            if (animator != null)
            {
                animator.SetTrigger("Jump");
            }
        }

        yVelocity += gravity * Time.deltaTime;
        cc.Move(Vector3.up * yVelocity * Time.deltaTime);
    }

    void HandleCameraRotation()
    {
        if (cameraTransform != null)
        {
            cameraTransform.localRotation = Quaternion.Euler(
                cameraTransform.localEulerAngles.x,
                0f,
                0f
            );
        }
    }

    void UpdateAnimations()
    {
        if (animator == null) return;

        Vector2 moveInput = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
        bool isMoving = moveInput.magnitude > 0.1f;
        animator.SetBool("IsWalking", isMoving);
        animator.SetBool("IsGrounded", isGrounded);
    }

    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.cyan;
        Gizmos.DrawWireSphere(transform.position, interactionRadius);
    }
}

Hey! It’s very likely that you have the camera and things as part of the prefab that you’re replicating so when they’re created on other clients the camera and other behaviours that you only want on the client with authority are running. See: 2. Prefab setup | coherence Documentation

You’ll probably want to disable movement controller and camera things on replicated characters

But then the camera and other things need to be outside the player’s prefab? How would these things be spawned when the game starts?

here are the things i disabled

I’m really a little lost

So you have two options:

  1. create the prefab with all the parts and then disable the parts that only should run on the owner of the prefab when that prefab is replicated on a client (like that doc suggests)
  2. create the prefab without the other things and then whatever system is instantiating the prefab would also then instantiate the other things and connect them.

If your plan is to just save your character in the scene then you’ll want to do #1 option, but you’ll find in the longer term that you probably don’t want to save the character in the scene, that you’ll want some kind of character spawn manager so you can control things like where the character spawns. In this case you’ll probably want to switch to option #2.

For option #1 if you want to also disable things not at the root of the prefab hierarchy, you can open the prefab in the editing context and then select the camera and you’ll see the configuration can make changes to that as well: