- Home /
Spawning a new player instance (UNET)
I'm playing around with the new networking system, and I can't seem to figure out how to spawn a new player instance.
When a player reaches 0 health (or less), I execute the following code on the player GameObject:
NetworkServer.Destroy(gameObject);
// UPDATE: I think I should be using ReplacePlayerForConnection, so added this in code below
After this, I show the respawn GUI (which is just a button right now). When you click the button, it should create a new instance of the player prefab. I tried implementing it like this:
GameObject player = Instantiate<GameObject>(playerPrefab);
NetworkServer.Spawn(player);
NetworkServer.ReplacePlayerForConnection(NetworkManager.singleton.client.connection, player, NetworkManager.singleton.client.connection.playerControllers[0].playerControllerId);
But when I check the GameObject, its isLocalPlayer property isn't true.
I'm not sure about the parameters of ReplacePlayerForConnection. It looks like this should be possible a lot easier than this...
All the UNET examples don't destroy the GameObject, but just hide it. However, I want the player to be able to choose between different prefabs I created in the project (e.g. sniper, warrior, scout, whatever...), so that's not an option.
Other stuff I tried adding:
NetworkClient.connection.playerControllers[0].gameObject = player; // get the local clients connection, get the first playerController (which is the only one) and assign the new player gameobject as its gameobject.
Which doesn't work.
Anyone having any luck with this?
Answer by WillTM · Jun 17, 2015 at 03:15 PM
I think you need to invoke the replacement on the server using a [Command]
[Command]
void CmdReplaceMe(GameObject newPlayerObject)
{
NetworkServer.Spawn(newPlayerObject);
NetworkServer.ReplacePlayerForConnection(connectionToClient, newPlayerObject, playerControllerId);
}
Or something similar ( I still just experimenting myself tbh)
hope that helps :)
Thanks! I've tried to implement this:
[Client]
public void Respawn()
{
CmdRespawn();
}
[Command]
public void CmdRespawn()
{
GameObject player = Instantiate<GameObject>(playerPrefab);
Network$$anonymous$$anager.singleton.client.connection.playerControllers[0].gameObject = player;
NetworkServer.ReplacePlayerForConnection(Network$$anonymous$$anager.singleton.client.connection, player, 0);
}
But it's still not working
ok so I think I'm slightly further than you but not by much :/. my 'replacement' player objects are already in the scene tho.. so try moving the instantiate to the client side before you call the command and pass the reference into the command. make sure the replacement prefab is in the netmanager spawnables and has a net identity too i guess?
Something more like this?
[Client]
public void Respawn()
{
GameObject player = Instantiate<GameObject>(playerPrefab);
NetworkServer.Spawn(player);
NetworkInstanceId id = player.GetComponent<NetworkIdentity>().netId;
CmdRespawn(id);
}
[Command]
public void CmdRespawn(NetworkInstanceId id)
{
GameObject player = NetworkServer.FindLocalObject(id);
NetworkServer.ReplacePlayerForConnection(Network$$anonymous$$anager.singleton.client.connection, player, 0);
NetworkServer.DestroyPlayersForConnection(Network$$anonymous$$anager.singleton.client.connection);
}
But I'm still getting the same error...
I think you may be able to do this in your case
public void Respawn() {
CmdRespawn(playerPrefab);
//clean up old player separately
}
[Command]
public void CmdRespawn(GameObject playerPrefab)
{
//as you are just instantiating the prefab reference according to docs you can do this
NetworkServer.ReplacePlayerForConnection(connectionToClient, playerPrefab, 0);
}
Im not using this Network$$anonymous$$anager.singleton.client.connection but connectionToClient, I get errors if I try it from a Command.. also may not want to destroy the player connection you have just replaced? my understanding is that would be if the client left the game though I may be wrong.
The following code works when the client is the host:
[Client]
public void Respawn()
{
Debug.Log(currentPlayerGameObject.GetComponent<NetworkIdentity>().netId);
CmdRespawn(currentPlayerGameObject.GetComponent<NetworkIdentity>().netId);
}
[Command]
public void CmdRespawn(NetworkInstanceId playerNetId)
{
GameObject oldPlayer = NetworkServer.FindLocalObject(playerNetId);
var conn = oldPlayer.GetComponent<NetworkIdentity>().connectionToClient;
var newPlayer = Instantiate<GameObject>(playerPrefab);
Destroy(oldPlayer.gameObject);
NetworkServer.ReplacePlayerForConnection(conn, newPlayer, 0);
Camera.main.GetComponent<FollowTarget>().target = newPlayer.transform;
}
A second client however gets the warning: "Trying to send command for non-local player. UnityEngine.Networking.NetworkBehaviour:SendCommandInternal(NetworkWriter, Int32, String)" And nothing happens... Still not sure what's up with that...
Answer by csisy · Jun 18, 2015 at 11:27 AM
What's about destroying and recreating the player? I mean when the health drops to 0 on the server, call
NetworkServer.Destroy(player);
which will destroy the specified game object. If you know the NetworkConnection, you can query the corresponding player object if you didnt store it.
NetworkPlayer player;
if (conn.GetPlayer(playerControllerId, out player))
{
if (player.NetworkIdentity != null && player.NetworkIdentity.gameObject != null)
NetworkServer.Destroy(player.NetworkIdentity.gameObject);
}
If you have only one player object for each client, then you can call
NetworkServer.DestroyPlayersForConnection(connection);
When you press the respawn button on the client, call the
ClientScene.AddPlayer(0);
which will ask the server to spawn the specified player prefab.
At the bottom of this page, you can find some code which could help you.
Edit: I just read that you'd like to spawn other object. Well... AFAIK you can have more player for each connection so you could do the following:
Spawn a "Connection" player which doesn't change
The Connection object can be used to communicate with the server
When the Client press the Respawn button, a message (Command) is sent to the server which would setup which object you want to spawn (sniper, warrior, etc.) using the Connection object.
At the Server-side, you store the prefab for playerControllerId 1
The client sends the add player request for id 1, by calling ClientScene.AddPlayer(1)
In the Server, if you're using the NetworkManager, you can override the OnServerAddPlayer function
Here you can check if the playerControllerId is 1, you spawn the stored prefab object.
Or something like this...
Answer by seanr · Jun 17, 2015 at 04:58 AM
try NetworkServer.ReplacePlayerForConnection()
Thanks! I was just experimenting with this, but have difficulties with filling in the correct parameters.
First parameter is the "Connection which is adding the player." So I use Network$$anonymous$$anager.client.connection
Second is the "Player object spawned for the player." So in my case, I use the instantiated gameObject from the code above (player)
Third parameter is "The player controller ID number as specified by client." I think this is where things go wrong. I tried using Network$$anonymous$$anager.client.connection.playerControllers[0].playerControllerId
because there should be only one connection on the client. This seems to work (most of the time) when the client is also the server, but doesn't seem to work on a second client.
So the function call I use is:
NetworkServer.ReplacePlayerForConnection(Network$$anonymous$$anager.client.connection, player, Network$$anonymous$$anager.client.connection.playerControllers[0].playerControllerId);
Any ideas what it is I'm doing wrong?
I read on http://forum.unity3d.com/threads/switching-between-several-player-objects.333649/ that the new GameObject should already be spawned on the server before calling the ReplacePlayerForConnection function.
Right now I was doing the following:
GameObject player = Instantiate<GameObject>(playerPrefab);
NetworkServer.ReplacePlayerForConnection(Network$$anonymous$$anager.client.connection, player, Network$$anonymous$$anager.client.connection.playerControllers[0].playerControllerId);
NetworkServer.Spawn(player);
However, am I right in thinking It should be the other way around?
GameObject player = Instantiate<GameObject>(playerPrefab);
NetworkServer.Spawn(player); // first spawn!!!
NetworkServer.ReplacePlayerForConnection(Network$$anonymous$$anager.client.connection, player, Network$$anonymous$$anager.client.connection.playerControllers[0].playerControllerId);
if there is one player per client, then it should always be zero.
Still not able to get this thing to work. This is my code:
GameObject player = Instantiate<GameObject>(playerPrefab);
NetworkServer.Spawn(player);
NetworkServer.ReplacePlayerForConnection(Network$$anonymous$$anager.singleton.client.connection, player, Network$$anonymous$$anager.singleton.client.connection.playerControllers[0].playerControllerId);
But I keep getting the following error:
Local invoke: Failed to find message handler for message ID 4
UnityEngine.Networking.NetworkServer:ReplacePlayerForConnection(NetworkConnection, GameObject, Int16)
Game$$anonymous$$anager:Respawn() (at Assets/SA$$anonymous$$BA/Scripts/Game$$anonymous$$anager.cs:20)
UnityEngine.EventSystems.EventSystem:Update()
Your answer
Follow this Question
Related Questions
MLAPI spawn player prefab 2 Answers
GameObject is spawned twice 0 Answers
Spaw dynamic (unregistered) object on network (UNET) 0 Answers
Unity Photon syncing objects 0 Answers
Can't set timer 1 Answer