Search Unity

Resolved Different Prefab for each player NetCode

Discussion in 'Netcode for GameObjects' started by KingTooTall, Feb 4, 2022.

  1. KingTooTall

    KingTooTall

    Joined:
    Oct 17, 2014
    Posts:
    61
    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
     
    nguyenlamlll likes this.
  2. CosmoM

    CosmoM

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

    Code (CSharp):
    1. public class PlayerSpawner : NetworkBehaviour {
    2.     [SerializeField] private GameObject playerPrefabA; //add prefab in inspector
    3.     [SerializeField] private GameObject playerPrefabB; //add prefab in inspector
    4.  
    5.     [ServerRpc(RequireOwnership=false)] //server owns this object but client can request a spawn
    6.     public void SpawnPlayerServerRpc(ulong clientId,int prefabId) {
    7.         GameObject newPlayer;
    8.         if (prefabId==0)
    9.              newPlayer=(GameObject)Instantiate(playerPrefabA);
    10.         else
    11.             newPlayer=(GameObject)Instantiate(playerPrefabB);
    12.         netObj=newPlayer.GetComponent<NetworkObject>();
    13.         newPlayer.SetActive(true);
    14.         netObj.SpawnAsPlayerObject(clientId,true);
    15.     }
    16. }
     
    nguyenlamlll, al33f, Ksanone and 5 others like this.
  3. KingTooTall

    KingTooTall

    Joined:
    Oct 17, 2014
    Posts:
    61
    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
     
  4. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    Not sure why you're getting that error, can you post your code?
     
  5. KingTooTall

    KingTooTall

    Joined:
    Oct 17, 2014
    Posts:
    61
    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.

    Code (CSharp):
    1.         [ServerRpc(RequireOwnership = false)] //server owns this object but client can request a spawn
    2.         public void MP_CreatePlayerServerRpc(ulong clientId,  GameObject playerToSpawn)
    3.         {
    4.             GameObject newPlayer = (GameObject)Instantiate(playerToSpawn);
    5.             NetworkObject netObj = newPlayer.GetComponent<NetworkObject>();
    6.             newPlayer.SetActive(true);
    7.             netObj.SpawnAsPlayerObject(clientId, true);
    8.         }
    Its giving the error on the GameObject newPlayer line.
     
    nguyenlamlll and Mid_D_Man like this.
  6. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    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<GameObject>), and passing the index/identifier of the prefab to use for that player.
     
  7. KingTooTall

    KingTooTall

    Joined:
    Oct 17, 2014
    Posts:
    61
    Cosmo,
    Here is the new code, and this shouldn't be that hard to do in my opinion but here I am... lol

    Code (CSharp):
    1. [ServerRpc(RequireOwnership = false)] //server owns this object but client can request a spawn
    2.         public void MP_CreatePlayerServerRpc(ulong clientId, int prefabId)
    3.         {
    4.             if (prefabId == 0)
    5.                 tempGO = (GameObject)Instantiate(player1);
    6.             else
    7.                 tempGO = (GameObject)Instantiate(player2);
    8.             NetworkObject netObj = tempGO.GetComponent<NetworkObject>();
    9.             tempGO.SetActive(true);
    10.             netObj.SpawnAsPlayerObject(clientId, true);
    11.         }
    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
     
    nguyenlamlll likes this.
  8. KingTooTall

    KingTooTall

    Joined:
    Oct 17, 2014
    Posts:
    61
    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
     
    nguyenlamlll likes this.
  9. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    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.
     
  10. KingTooTall

    KingTooTall

    Joined:
    Oct 17, 2014
    Posts:
    61
    CosmoM,
    I'm not sure im passing in the right ClientId for the client to be spawned....
    Code (CSharp):
    1. public void MP_InitClientPlayer()
    2.         {
    3.             SpawnController.Instance.MP_CreatePlayerServerRpc(NetworkManager.Singleton.LocalClientId, 1);
    4.                     }
    That is what im passing into my spawning method....
     
    nguyenlamlll likes this.
  11. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    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?
     
    nguyenlamlll and JJunior like this.
  12. KingTooTall

    KingTooTall

    Joined:
    Oct 17, 2014
    Posts:
    61
    Code (CSharp):
    1. public void MP_HostAGane()
    2. {
    3.             //Host a game
    4.     networkController.MP_Host();
    5.             //Create a player and spawn
    6.     globalPlayerManager.MP_InitHostPlayer();
    7. }
    8.  
    9. public void MP_JoinAGane()
    10. {
    11.             networkController.MP_Client();
    12.             globalPlayerManager.MP_InitClientPlayer();
    13. }
    Code (CSharp):
    1.         public void MP_Host()
    2.         {
    3.             print("HOST A GAME PRESSED");
    4.             NetworkManager.Singleton.StartHost();
    5.          }
    6.  
    7.         public void MP_Client()
    8.         {
    9.             print("JOIN A GAME PRESSED");
    10.             NetworkManager.Singleton.StartClient();
    11.         }
    12.  
    Code (CSharp):
    1.         public void MP_InitHostPlayer()
    2.         {
    3.             SpawnController.Instance.MP_CreatePlayerServerRpc(NetworkManager.Singleton.LocalClientId, 0);
    4.         }
    5.  
    6.         public void MP_InitClientPlayer()
    7.         {
    8.             SpawnController.Instance.MP_CreatePlayerServerRpc(NetworkManager.Singleton.LocalClientId, 1);
    9.         }
     
  13. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    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.:

    Code (CSharp):
    1. public override void OnNetworkSpawn() {
    2.     if (IsServer)
    3.         MP_CreatePlayerServerRpc(NetworkManager.Singleton.LocalClientId,0);
    4.     else
    5.         MP_CreatePlayerServerRpc(NetworkManager.Singleton.LocalClientId,1);
    6. }
     
  14. KingTooTall

    KingTooTall

    Joined:
    Oct 17, 2014
    Posts:
    61
    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
     
  15. TomiBary

    TomiBary

    Joined:
    Oct 26, 2017
    Posts:
    4
    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?
     
  16. Mid_D_Man

    Mid_D_Man

    Joined:
    Jan 26, 2022
    Posts:
    22
    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
     
  17. mrstruijk

    mrstruijk

    Joined:
    Jan 24, 2018
    Posts:
    52
    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.
     
    TomiBary likes this.
  18. unity_4722F397AFC3472DE78E

    unity_4722F397AFC3472DE78E

    Joined:
    Oct 1, 2022
    Posts:
    2
    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?
     
    hzhou1995 likes this.