Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Character select in Netcode

Discussion in 'Netcode for GameObjects' started by AWildCoconut, Oct 24, 2022.

  1. AWildCoconut

    AWildCoconut

    Joined:
    Aug 8, 2020
    Posts:
    6
    Making a new game using netcode, first time using it so I'm pretty inexperienced. I'm trying to let you select your character, but I'm having a lot of trouble. I tried setting the player prefab of the networkmanager directly, but that resulted in clients sharing the same character as the host. I've gotten pretty close, where I added a void to the OnClientConnectedCallback, and then used a ClientRPC with a client ID target to find the selected character of the joining player, which worked perfectly. Code looks like this:
    Code (CSharp):
    1.     void SendCharacterRequester(ulong id)//called by the OnClientConnected callback
    2.     {
    3.         Debug.Log("Character request clientRPC being sent");
    4.         GetRequiredCharacterClientRPC(id, new ClientRpcParams { Send = { TargetClientIds = new List<ulong> { id } } }); //send a request to the joining client to get their character preference (awful i know)
    5.     }
    6.  
    7.     [ClientRpc]
    8.     public void GetRequiredCharacterClientRPC(ulong clientid, ClientRpcParams clientparams)//run on the targeted client (the one just joining) to get their character preference, then relay it to the server
    9.     {
    10.         Debug.Log("Character request clientRPC has been recieved by target client, forwarding to server. Requested character: " + FindObjectOfType<NetworkManagerUI>().currentCharacter);
    11.         FindObjectOfType<NonPooledDynamicObjectSpawner>().SendCharacterRequestToServerRPC(FindObjectOfType<NetworkManagerUI>().currentCharacter, clientid); //tells the object spawner to spawn the player object
    12.     }
    But then, when I tried to use a ServerRPC to spawn the object as a player, it said it was trying to spawn an object as a client, because the script I was doing this in wasn't a networkbehaviour, it was a monobehaviour directly attached to the NetworkManager. I tried putting the spawn code into a networkbehaviour, on an object with a networkobject script. The code looks like this:

    Code (CSharp):
    1.     [ServerRpc] //server owns this object but client can request a spawn(RequireOwnership = false)
    2.     public void SendCharacterRequestToServerRPC(int charid, ulong clientid)//gets the clientid and characterid required, and spawns a player for that client id. run on the server
    3.     {
    4.         Debug.Log("Server recieved character request: " + charid);
    5.         NetworkObject ob = Instantiate(FindObjectOfType<NetworkManagerUI>().Characters[charid]).GetComponent<NetworkObject>();
    6.         ob.SpawnAsPlayerObject(clientid);
    7.     }
    I call it from the clientRPC that gets the character preference, but nothing happens. I'm stumped right now, am I doing something wrong? The ClientRPC to get the players character preference seems to work according to the debug logs. If theres a better way to do all this, please tell me. Thanks!
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,533
    Do you have a PlayerPrefab assigned to NetworkManager? If so, remove that, you don't want the default to be spawned.

    Next, you need all selectable player characters in NetworkManager's Network Prefabs list. I guess you already have that but just to be sure.

    I would recommend passing in the client's chosen player prefab ID via connection approval payload, it's just better this way as the server can spawn the player object right away without doing any RPC messaging.

    The way you do it, you unnecessarily have the server request this info from the client. However, the client should rather have a script that runs OnClientConnected and checks if the clientId is the LocalClientId, if so, it sends a ServerRPC. The server needn't ask for this first. But in theory, it should work. Though you didn't say exactly where it stops working. You say "nothing happens", does that mean the "Server received.." log isn't printed either?

    The FindObjectOfType<> seems like a little too optimistic. Ideally, you'd have a ScriptableObject, or just use PlayerPref, to store the ID of the selected character. That way the code is decoupled from UI and everything else and you can safely access that variable without having to worry about any of these monobehaviours to be in the scene (and active). If the RPC messaging works I'm pretty sure it fails with all the convoluted finding of objects to get to a specific character reference. Or the Characters list not being identical for server/host and client.

    My thought is that ServerRPC should have RequireOwnership = false, unless the ClientRPC script is on the same object as the ServerRPC.

    Did you check both host and client console for any messages?
     
  3. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    I agree about that.
    like it says in the comment in the first line of the ServerRPC code, you should write
    [ServerRpc(RequireOwnership = false)]
    if you want to call it from the client.