Netcode Spawn Position Issue

Hi all! I’m new here :slight_smile:

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:

public class MainGameController {
    private void Start()
    {
        if (!NetworkManager.Singleton.IsHost)
        {
            Destroy(gameObject);
            return;
        }

        // Spawn host
        SpawnPlayerServerRpc(NetworkManager.Singleton.LocalClientId);

        NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected;
    }

    private void OnClientConnected(ulong obj)
    {
        Debug.Log("Client connected, clientId=" + obj);

        // Spawn client
        SpawnPlayerServerRpc(obj);

        // Start game
        if (GetConnectedClientCount() == 2)
        {
            Debug.Log("Both players connected - game starting!");
        }
    }

    [ServerRpc]
    private void SpawnPlayerServerRpc(ulong clientId)
    {      
        Vector3 pos = GetNextSpawnPosition(); // Alternates between the two gameobject spawnpoints
        NetworkObject newNetworkObject = Instantiate(playerPrefab, pos, Quaternion.identity).GetComponent<NetworkObject>();

        // Spawn client
        newNetworkObject.SpawnAsPlayerObject(clientId, true);
    }
}

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:

public class PlayerMovement {
    public NetworkVariable<Vector3> Position = new NetworkVariable<Vector3>();
    public NetworkVariable<Quaternion> Rotation = new NetworkVariable<Quaternion>();

   void Update()
   {
       // Update this player's position on other clients
       if (!IsLocalPlayer)
       {
           transform.position = Position.Value;
           transform.rotation = Rotation.Value;
           return;
       }

         // -- Unimportant movement logic here -- //

         SubmitPositionAndRotationServerRpc(OwnerClientId, transform.position, transform.rotation);
   }

    [ServerRpc(RequireOwnership = false)]
    void SubmitPositionAndRotationServerRpc(ulong clientId, Vector3 newPosition, Quaternion newRotation)
    {
        Debug.Log("pos:" + newPosition + " clientId=" + clientId);
        Position.Value = newPosition;
        Rotation.Value = newRotation;
    }
}

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!

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?

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:

    [SerializeField] private List<GameObject> spawnPoints = new List<GameObject>();

    private int spawnPointRotation = 0;
    private Vector3 GetNextSpawnPosition()
    {
        Vector3 pos = spawnPoints[spawnPointRotation].transform.position;
        spawnPointRotation = (spawnPointRotation + 1) % spawnPoints.Count;
        return pos;
    }

I can see a couple issues:

  • MainGameController does not inherits from a Mono/Netwrok behaviour. You need to do that in order to use the unity update loop apis and/or send rpc. You could look into Custom messages | Unity Multiplayer Networking if you do not want to extend from them
  • NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected is being set after the RPC call. Is the client connected after or before you hit Start?