Different Prefab for each player NetCode

Hi all,
I'm sorry if this has already been answered, but after searching around I cannot find a simple answer...
My game has 2 players only. I would like to have player 1 and player 2 each have their own prefab instantiated and spawned. I cant seem to find out how to override the default player prefab or use NONE and again, have it spawn my own prefabs (each prefab different for each player). I've looked into SpawnAsPlayerObject but I'm new to netcode and am kinda lost. Any help would be great.
Thanks
King

2 Likes

In the NetworkManager inspector, set "Player Prefab" to none and add your different player prefabs as NetworkPrefabs below that. Then as an example script:

public class PlayerSpawner : NetworkBehaviour {
    [SerializeField] private GameObject playerPrefabA; //add prefab in inspector
    [SerializeField] private GameObject playerPrefabB; //add prefab in inspector

    [ServerRpc(RequireOwnership=false)] //server owns this object but client can request a spawn
    public void SpawnPlayerServerRpc(ulong clientId,int prefabId) {
        GameObject newPlayer;
        if (prefabId==0)
             newPlayer=(GameObject)Instantiate(playerPrefabA);
        else
            newPlayer=(GameObject)Instantiate(playerPrefabB);
        netObj=newPlayer.GetComponent<NetworkObject>();
        newPlayer.SetActive(true);
        netObj.SpawnAsPlayerObject(clientId,true);
    }
}
8 Likes

Cosmo,
Thank you for the code sample. I've tried implementing your solution but I'm getting an error in the console "Don't know how to serialize GameObject...." any further help would be great.
King

Not sure why you're getting that error, can you post your code?

Here is the error:
SpawnController.cs(74,4): error - Don't know how to serialize GameObject - implement INetworkSerializable or add an extension method for FastBufferWriter.WriteValueSafe to define serialization.

        [ServerRpc(RequireOwnership = false)] //server owns this object but client can request a spawn
        public void MP_CreatePlayerServerRpc(ulong clientId,  GameObject playerToSpawn)
        {
            GameObject newPlayer = (GameObject)Instantiate(playerToSpawn);
            NetworkObject netObj = newPlayer.GetComponent<NetworkObject>();
            newPlayer.SetActive(true);
            netObj.SpawnAsPlayerObject(clientId, true);
        }

Its giving the error on the GameObject newPlayer line.

2 Likes

Well yes: you're passing a GameObject as an argument, which you cannot do over an Rpc because the GameObject cannot be serialized over the network. Note that the way I solved this in the example is by giving the spawner access to all prefabs (I only included two, but it could be a List), and passing the index/identifier of the prefab to use for that player.

2 Likes

Cosmo,
Here is the new code, and this shouldn't be that hard to do in my opinion but here I am... lol

[ServerRpc(RequireOwnership = false)] //server owns this object but client can request a spawn
        public void MP_CreatePlayerServerRpc(ulong clientId, int prefabId)
        {
            if (prefabId == 0)
                tempGO = (GameObject)Instantiate(player1);
            else
                tempGO = (GameObject)Instantiate(player2);
            NetworkObject netObj = tempGO.GetComponent<NetworkObject>();
            tempGO.SetActive(true);
            netObj.SpawnAsPlayerObject(clientId, true);
        }

The error I'm getting now:
NullReferenceException: Object reference not set to an instance of an object
Unity.Netcode.NetworkBehaviour.get_NetworkManager ()......

I've set the prefabs in variables separately like you did in your code example. I understand what you said about using a list in the future for more possible player prefabs. I'm not understanding why it cant find my reference to my prefab.
Thanks
King

1 Like

Ok, got rid of the null reference error by adding a NETWORKOBJECT to my spawn controller, so my RPC will work. (Duh!) It will spawn player 1 when I click on HOST, but when I connect with the client, its NOT instantiating or spawning my CLIENT, and no error(s)... I do get a warning in the condole when the client connects:
"Attempt to send to not connected connection {1}"
Thanks
King

1 Like

Sounds like you're trying to spawn too soon. To make sure the client is fully connected you can start your spawning sequence in the spawner code's OnNetworkSpawn.

2 Likes

CosmoM,
I'm not sure im passing in the right ClientId for the client to be spawned....

public void MP_InitClientPlayer()
        {
            SpawnController.Instance.MP_CreatePlayerServerRpc(NetworkManager.Singleton.LocalClientId, 1);
                    }

That is what im passing into my spawning method....

1 Like

That's the clientId of the caller, so if the client is the one calling MP_InitClientPlayer, then it looks good. Where do you call MP_InitClientPlayer though, are you sure it's after network initialization?

2 Likes
public void MP_HostAGane()
{
            //Host a game
    networkController.MP_Host();
            //Create a player and spawn
    globalPlayerManager.MP_InitHostPlayer();
}

public void MP_JoinAGane()
{
            networkController.MP_Client();
            globalPlayerManager.MP_InitClientPlayer();
}
        public void MP_Host()
        {
            print("HOST A GAME PRESSED");
            NetworkManager.Singleton.StartHost();
         }

        public void MP_Client()
        {
            print("JOIN A GAME PRESSED");
            NetworkManager.Singleton.StartClient();
        }
        public void MP_InitHostPlayer()
        {
            SpawnController.Instance.MP_CreatePlayerServerRpc(NetworkManager.Singleton.LocalClientId, 0);
        }

        public void MP_InitClientPlayer()
        {
            SpawnController.Instance.MP_CreatePlayerServerRpc(NetworkManager.Singleton.LocalClientId, 1);
        }

Yep, you call it too soon. Don't follow up your networkController.MP_Client() call up immediately by globalPlayerManager.MP_InitClientPlayer(), because the server won't have set up your connection yet. Instead, do the globalPlayerManager.MP_InitClientPlayer() call only when the connection has completed, either through a NetworkManager.Singleton.OnClientConnectedCallback or, as I suggested previously, by doing the InitClientPlayer call in an OnNetworkSpawn, in the spawner object's code for example. E.g.:

public override void OnNetworkSpawn() {
    if (IsServer)
        MP_CreatePlayerServerRpc(NetworkManager.Singleton.LocalClientId,0);
    else
        MP_CreatePlayerServerRpc(NetworkManager.Singleton.LocalClientId,1);
}
7 Likes

Success! I added OnNetworkSpawn() to my SpawnController script like you suggested and it WORKED!
Two DIFFERENT prefabs spawn now, one for each player. I knew this shouldn't be that hard to accomplish...
Thank you for ALL your help CosmoM!
King

Hi ,i have similar question.

I am working on mutliplayer VR project where you can connect as VR or desktop player.
And i want to spawn VR rig to client which selected VR option and on other clients spawn similar object to VR rig that has just Left and Right controllers and HMD transforms synced. (without VR components, that are not needed)

How should i aproach it?

How to spawn different object on other clients that has synced some transforms?

Hello I would like to ask a question, So I have two player objects and I use a script to change their Sprite in game using playerprefs that I save in my main menu but every time I spawn them instead of them having different sprites the all end up having the same Sprite please is there a way to make sure that the saved playerprefs have different value for each client

Thank you @CosmoM , I have been struggling with this for days! Finally some success here :).

@TomiBary , I hope by now you have solved this, but if not: you can check in Awake to see what device you're playing on: if (XRSettings.isDeviceActive) { // set device index to the index indicating the XR rig prefab} else { // device index for default camera } Then use the the above solution to spawn the correct camera objects.

Sync transforms using the ClientNetworkTransform.

1 Like

CosmoM, thanks for sharing your knowledge im new to unity and i want to know if the code that you provided should be dragged into the networkmanager in the inspector?

1 Like