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

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


person James    schedule 11.07.2015    source источник


Ответы (3)


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

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

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

  • Используйте SyncVar для синхронизации netID родительского объекта между сервером и клиентом.
  • Когда объект создается на клиенте, найдите родителя, используя синхронизированный 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);
}
person Andy Barnard    schedule 14.07.2015
comment
я пытаюсь использовать этот метод. есть ли причина, по которой netId изменится? я только устанавливаю его, как в приведенном примере, в OnStartClient. - person Kevin Kuyl; 06.12.2016
comment
Не могу поверить, что это способ сделать это ... черт! - person Leonardo Chaia; 24.07.2017
comment
Почему вы отменили мою правку? Я добавил подсветку кода, чтобы его было легче читать. - person derHugo; 15.11.2017
comment
Не хотел спариваться. Я отказался случайно и не могу вернуться, так как у меня недостаточно очков репутации. Не могли бы вы отправить еще раз? Приветствую за редактирование :) - person Andy Barnard; 15.11.2017
comment
Хорошее решение, но netId уникален для всех клиентов и серверов, поэтому syncvar может не понадобиться. docs.unity3d.com/2018.2/Documentation/ScriptReference/ - person Menyus; 24.04.2021

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

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

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

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

    transform.SetParent(parentObject.transform.Find(pathToWeaponHolder));
person JonJon    schedule 15.09.2016

Прочитав решение Энди Барнарда, я придумал это слегка измененное решение. Вместо 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 серверу, которая просто вызывала бы общедоступный метод сервера. Это на один набор сетевых сообщений более чем оптимально, но м-м-м.

person rodamn    schedule 18.10.2017