Unity 5.1 Networking - spawns an object as a child for the host and all clients

I have a player object that can equip several types of weapons. When a weapon is equipped, its parent transformer is installed in his hand. I talked with this for some time and cannot make it work for both the host and the client. Now I'm trying to arm a weapon on the server and tell all clients so that their parents convert.

public NetworkInstanceId weaponNetId; [Command] void Cmd_EquipWeapon() { var weaponObject = Instantiate (Resources.Load ("Gun"), hand.position, Quaternion.Euler (0f, 0f, 0f)) as GameObject; weaponObject.transform.parent = hand; NetworkServer.Spawn (weaponObject); //set equipped weapon var weapon = weaponObject.GetComponent<Weapon> () as Weapon; weaponNetId = weaponObject.GetComponent<NetworkIdentity> ().netId; Rpc_SetParentGameobject (weaponNetId); } [ClientRpc] public void Rpc_SetParentGameobject(NetworkInstanceId netID) { weaponNetId = netId; } 

And in the update I am updating weapons conversion

  void Update () { // set child weapon tranform on clients if (!isServer) { if (weaponNetId.Value != 0 && !armed) { GameObject child = NetworkServer.FindLocalObject (weaponNetId); if (child != null) { child.transform.parent = hand; } } } 

I know that this is not the most optimized way to do this ... but now I'm just trying to get it to work in any way, and then work on setting it up. It seems like it should be a simple task.

+5
source share
3 answers

We are doing a similar thing in our multiplayer game. To do this, you need to do a few actions. Firstly, the concept of:

Installing a weapon parent on a server is trivial, as you already found. Just install the parent transformer, as usual in Unity. However, after this object appears on the server using NetworkServer.Spawn it will later be spawned on clients in the root of the scene (the hierarchy outside the generated collection is not synchronized).

So, to set up the hierarchy on the client, I would assume that you:

  • Use SyncVar to synchronize the netID of the parent between the server and client.
  • When the object is created on the client, find the parent using the synchronized network identifier and set it as the transformation parent.

So I would tweak your code to look something like this. First, set the parent netId object before you create it. This ensures that when it is created on clients, netId will be installed.

 [Command] void Cmd_EquipWeapon() { var weaponObject = Instantiate (Resources.Load ("Gun"), hand.position, Quaternion.Euler (0f, 0f, 0f)) as GameObject; weaponObject.parentNetId = hand.netId; // Set the parent network ID weaponObject.transform.parent = hand; // Set the parent transform on the server NetworkServer.Spawn (weaponObject); // Spawn the object } 

And then in your weapon class:

  • Add property parentNetId.
  • Mark it as [SyncVar] so that it synchronizes between the server and client copies.
  • When generating on the client, find the parent using netId and set it to the transform parent.

Perhaps something like:

 [SyncVar] public NetworkInstanceId parentNetId; public override void OnStartClient() { // When we are spawned on the client, // find the parent object using its ID, // and set it to be our transform parent. GameObject parentObject = ClientScene.FindLocalObject(parentNetId); transform.SetParent(parentObject.transform); } 
+10
source

I found this post really useful, but I have a small addition.

NetId will be at the root of the hierarchy, so it’s useful to know that you can cross the hierarchy with transform.Find.

Something like that..

 GameObject parentObject = ClientScene.FindLocalObject(parentNetId); string pathToWeaponHolder = "Obj/targetObj"; transform.SetParent(parentObject.transform.Find(pathToWeaponHolder)); 
+4
source

After reading Andy Barnard's solution, I came up with this slightly modified solution. Instead of SyncVar, there is a client RPC to call the server at any time when NetworkIdentity needs to change its parents. This requires the parent to also have NetworkIdentity (although it does not have to be a registered prefab).

 public void Server_SetParent (NetworkIdentity parentNetworkIdentity) { if (parentNetworkIdentity != null) { // Set locally on server transform.SetParent (parentNetworkIdentity.transform); // Set remotely on clients RpcClient_SetParent (parentNetworkIdentity.netId, resetTransform); } else { // Set locally on server transform.SetParent (null); // Set remotely on clients RpcClient_SetParent (NetworkInstanceId.Invalid, resetTransform); } } [ClientRpc] void RpcClient_SetParent (NetworkInstanceId newParentNetId) { Transform parentTransform = null; if (newParentNetId != NetworkInstanceId.Invalid) { // Find the parent by netid and set self as child var parentGobj = ClientScene.FindLocalObject (newParentNetId); if (parentGobj != null) { parentTransform = parentGobj.transform; } else { Debug.LogWarningFormat ("{0} Could not find NetworkIdentity '{1}'.", gameObject.name, newParentNetId.Value); } } transform.SetParent (parentTransform); } 

These two are part of NetworkBehavior , which is obviously RequiresComponent(typeof(NetworkIdentity)) . If you really need this behavior on the client on the server, I would suggest creating a command that passed NetworkIdentity to the server, which simply calls the server’s public method. This one set of network messages is more than optimal, but meh.

0
source

All Articles