(This post is originally from the wiki I wrote for my recent game project Runner or Dinner. https://github.com/stormouse/horror-story-alpha/wiki/UNet-Multiple-Character-Solution)
- Add components according to the character type after a playerPrefab is spawned: Unity does not support adding NetworkBehaviour to a spawned GameObject
- Bind every possible component to playerPrefab and disable some of them according to the character type: Too hard to organize component relationships on that prefab. Dirty component references.
Let playerPrefab be a ‘player controller’,and the characters are Pawns/Characters in Unreal concepts. A player controller can possess a character.
Pros: this method is universal, easily comprehensible, fully-controlled
Cons: you have to implement possess, and native helper components like NetworkTransform and NetworkAnimator cannot help you anymore.
P.S: I haven’t think over how to implement this feature, will research on Unreal’s implementation further.
Spawn-time Replacement (Our Way!)
If your game has a lobby scene, a traditional way to share information between lobbyPlayer and gamePlayer is to customize OnLobbyServerSceneLoadedForPlayer
// class NetworkLobbyPlayer public override bool OnLobbyServerSceneLoadedForPlayer(GameObject lobbyPlayer, GameObject gamePlayer);
This callback runs when the server notices a client has finished loading game scene. By default it spawns a game player prefab and transfer the control from lobbyPlayer to gamePlayer. But we can ignore the existing gamePlayer and create a new character for this client, which requires ReplacePlayerForConnection
public static bool ReplacePlayerForConnection(Networking.NetworkConnection conn, GameObject player, short playerControllerId);
From server’s view, clients are just connections. We can get a client’s connection from a NetworkIdentity that is spawned for that client, for us that is, the lobbyPlayer.
var clientConn = lobbyPlayer.GetComponent<NetworkIdentity>().connectionToClient;
Then we spawn a new character prefab for the client to ‘possess’.
// instantiate on server side var newPlayer = Instantiate(characterPrefab); // tell all clients to spawn (auto-synced) NetworkServer.Spawn(newPlayer); // destroy auto-created gamePlayer NetworkServer.Destroy(gamePlayer);
No need to worry about synchronization problem. The Spawn method of NetworkServer will ensure that every client scene will create a same copy when they are ready.
Finally, we return false for OnLobbyServerSceneLoadedForPlayer to prevent transfer of player control, because we have destroyed the game player and given the control to newPlayer by replacement.