Search Unity

Bug Netcode Spawn Position Issue

Discussion in 'Multiplayer' started by Cyanox, Jan 27, 2024.

  1. Cyanox

    Cyanox

    Joined:
    Aug 16, 2021
    Posts:
    2
    Hi all! I'm new here :)

    I'm working on a coop multiplayer game where two players are on the map at once, each one spawns on one side of the room. It's a host / client game, so no dedicated server to worry about. In doing this, I need to set the spawn position. To my understanding (I'm fairly new to Netcode) I just need to remove the player prefab from the NetworkManager to prevent the server from creating players naturally.

    To spawn players, I made a "game manager" script that runs on the host only.
    Here's how I did that:
    Code (CSharp):
    1.  
    2. public class MainGameController {
    3.     private void Start()
    4.     {
    5.         if (!NetworkManager.Singleton.IsHost)
    6.         {
    7.             Destroy(gameObject);
    8.             return;
    9.         }
    10.  
    11.         // Spawn host
    12.         SpawnPlayerServerRpc(NetworkManager.Singleton.LocalClientId);
    13.  
    14.         NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected;
    15.     }
    16.  
    17.     private void OnClientConnected(ulong obj)
    18.     {
    19.         Debug.Log("Client connected, clientId=" + obj);
    20.  
    21.         // Spawn client
    22.         SpawnPlayerServerRpc(obj);
    23.  
    24.         // Start game
    25.         if (GetConnectedClientCount() == 2)
    26.         {
    27.             Debug.Log("Both players connected - game starting!");
    28.         }
    29.     }
    30.  
    31.     [ServerRpc]
    32.     private void SpawnPlayerServerRpc(ulong clientId)
    33.     {      
    34.         Vector3 pos = GetNextSpawnPosition(); // Alternates between the two gameobject spawnpoints
    35.         NetworkObject newNetworkObject = Instantiate(playerPrefab, pos, Quaternion.identity).GetComponent<NetworkObject>();
    36.  
    37.         // Spawn client
    38.         newNetworkObject.SpawnAsPlayerObject(clientId, true);
    39.     }
    40. }
    In SpawnPlayerServerRpc(), I set the position explicitly to the spawn point. For the host, this works every time. They always spawn in the proper location. For the client however, they always seem to spawn at the origin for a single frame, then teleport to either the correct spawn location that I set, or some random position nearby with no relevance. This random position is always the same though.

    Here's how I'm handling player code syncing:
    Code (CSharp):
    1.  
    2. public class PlayerMovement {
    3.     public NetworkVariable<Vector3> Position = new NetworkVariable<Vector3>();
    4.     public NetworkVariable<Quaternion> Rotation = new NetworkVariable<Quaternion>();
    5.  
    6.    void Update()
    7.    {
    8.        // Update this player's position on other clients
    9.        if (!IsLocalPlayer)
    10.        {
    11.            transform.position = Position.Value;
    12.            transform.rotation = Rotation.Value;
    13.            return;
    14.        }
    15.  
    16.          // -- Unimportant movement logic here -- //
    17.  
    18.          SubmitPositionAndRotationServerRpc(OwnerClientId, transform.position, transform.rotation);
    19.    }
    20.  
    21.     [ServerRpc(RequireOwnership = false)]
    22.     void SubmitPositionAndRotationServerRpc(ulong clientId, Vector3 newPosition, Quaternion newRotation)
    23.     {
    24.         Debug.Log("pos:" + newPosition + " clientId=" + clientId);
    25.         Position.Value = newPosition;
    26.         Rotation.Value = newRotation;
    27.     }
    28. }
    I'm using NetworkVariables to track the position and rotation. I tried switching from NetworkVariables to RPCs, which actually eliminated the problem, but the movement and rotation was super choppy (afaik because RPCs are more expensive). That leads me to believe the issue lies somewhere with these NetworkVariables.

    Additionally, when Start() fires on the PlayerMovement script, if I print transform.position, the coordinates are always the correct coordinates of the spawn point. Yet the very first time SubmitPositionAndRotationServerRpc() is called (and the game decides to spawn the player incorrectly this time), transform.position is no longer correct.

    Here's the behavior I'm describing. In this video, I'm running the exact same build side by side twice. In both instances, the player spawns in the glass for a single frame (which is the origin). The first time, the player gets moved to the other side in the proper position. The second time, he gets moved in the seemingly random but consistent position on my side.


    I've been debugging this for 5 hours now and I'm at a loss. Can anyone explain what's going on here? Why does the client spawn at the origin for a frame when I explicitly set their position? Why is is random whether it moves to the correct position or some random position?

    It's a bit of a difficult problem to explain, so if any clarification is needed please ask!
     
  2. Gabo_campitelli

    Gabo_campitelli

    Joined:
    Oct 17, 2012
    Posts:
    426
    Hi,

    To me the code looks ok. the only thing i can't follow is the missing piece of the puzzle:

    "Vector3 pos = GetNextSpawnPosition(); // Alternates between the two gameobject spawnpoints"

    what logic are you using there?
     
  3. Cyanox

    Cyanox

    Joined:
    Aug 16, 2021
    Posts:
    2
    It just picks from a list of spawn point gameobjects I've defined in the scene. There are only two spawn points, one on either side of the glass.

    Here's the function:
    Code (CSharp):
    1.     [SerializeField] private List<GameObject> spawnPoints = new List<GameObject>();
    2.  
    3.     private int spawnPointRotation = 0;
    4.     private Vector3 GetNextSpawnPosition()
    5.     {
    6.         Vector3 pos = spawnPoints[spawnPointRotation].transform.position;
    7.         spawnPointRotation = (spawnPointRotation + 1) % spawnPoints.Count;
    8.         return pos;
    9.     }