5

У меня есть объект игрока, который может экипировать несколько видов оружия. Когда оружие оборудовано, его родительский трансформатор устанавливается в его руку. Я некоторое время общался с этим и не могу заставить это работать как для хоста, так и для клиента. Сейчас я пытаюсь вооружить оружие на сервере и сообщать всем клиентам, чтобы их родители трансформировались.Unity 5.1 Networking - порождает объект как ребенка для хоста и всех клиентов

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; 
    } 

И в обновлении Я обновляю оружие преобразования

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; 
      } 
     } 
    } 

Я знаю, что это не самый оптимизированный способ сделать this..but прямо сейчас я просто пытаюсь получить это должно работать любым способом, а затем работать над его настройкой. Похоже, это должна быть простая задача.

ответ

10

Мы делаем аналогичную вещь в нашей многопользовательской игре. Для этого вам нужно сделать несколько действий. Во-первых, концепция:

Установка родителя оружия на сервере тривиальна, как вы уже нашли. Просто установите родительский трансформатор, как обычно в Unity. Однако после появления этого объекта на сервере с NetworkServer.Spawn он позже будет создан на клиентах в корне сцены (иерархия за пределами порожденного сборника не синхронизируется).

Так что для того, чтобы настроить иерархию на клиенте, я хотел бы предложить вам:

  • Используйте SyncVar для синхронизации NETID родительского объекта между сервером и клиентом.
  • Когда объект создается на клиенте, найдите родителя, используя синхронизированный сетевой идентификатор, и установите его как родителя вашего преобразования.

Поэтому я хотел бы настроить свой код, чтобы выглядеть примерно так. Во-первых, установите родительский netId оружия перед его созданием. Это гарантирует, что когда он будет создан на клиентах, будет установлен netId.

[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 
} 

И тогда в своем классе оружия:

  • Добавьте свойство parentNetId.
  • Отметьте как [SyncVar] так, чтобы он синхронизировался между копиями сервера и клиента.
  • Когда порожденный на клиенте, найдите родительский объект, используя netId, и установите его в родительский объект нашего преобразования.

Может быть что-то вроде:

[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's parent. 
    GameObject parentObject = ClientScene.FindLocalObject(parentNetId); 
    transform.SetParent(parentObject.transform); 
} 
+0

Я пытаюсь использовать этот метод. есть ли причина, по которой «netId» изменится? Я устанавливаю его, как в приведенном примере, в 'OnStartClient'. –

+0

Не могу поверить, что это способ сделать это .. черт! –

+0

Почему вы вернули мое редактирование? Я добавил выделение кода, делая ваш код намного лучше, чтобы читать. – derHugo

4

Я нашел этот пост действительно полезным, но у меня есть небольшое дополнение.

netId будет находиться в корне иерархии, поэтому полезно знать, что вы можете перемещаться по иерархии с помощью transform.Find.

Что-то вроде этого ..

GameObject parentObject = ClientScene.FindLocalObject(parentNetId); 
    string pathToWeaponHolder = "Obj/targetObj"; 

    transform.SetParent(parentObject.transform.Find(pathToWeaponHolder)); 
0

После прочтения решения Энди Барнарда я придумал это слегка измененное решение. Вместо SyncVar существует RPC-клиент для вызова сервера в любое время, когда NetworkIdentity необходимо изменить родителей. Это требует от родителя также иметь NetworkIdentity (хотя он не обязательно должен быть зарегистрированным префабом).

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); 
} 

Эти две являются частью NetworkBehavior, который, очевидно, RequiresComponent(typeof(NetworkIdentity)). Если вам действительно нужно это поведение на клиенте на сервере, я бы предложил создать команду, которая передала NetworkIdentity на сервер, который просто вызовет общедоступный метод сервера. Это один набор сетевых сообщений, более оптимальный, но meh.