【问题标题】:Unity 5.1 Networking - Spawn an object as a child for the host and all clientsUnity 5.1 Networking - 为主机和所有客户端生成一个对象作为子对象
【发布时间】:2015-07-11 17:07:22
【问题描述】:

我有一个可以装备多种武器的玩家对象。装备武器时,其变换的父级设置为它的手。我已经搞砸了一段时间,无法让它对主机和客户端都有效。现在我正在尝试在服务器上装备武器,并告诉所有客户端设置他们的父母变换。

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

我知道这不是最优化的方法......但现在我只是想让它以任何可能的方式工作,然后努力调整它。看起来这应该是一个简单的任务。

【问题讨论】:

    标签: unity3d network-programming multiplayer unity-networking


    【解决方案1】:

    我们在多人游戏中做类似的事情。您需要做一些事情才能使其正常工作。一、概念:

    如您所见,在服务器上设置武器的父级是微不足道的。只需像在 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);
    }
    

    【讨论】:

    • 我正在尝试使用这种方法。 netId 有什么改变的理由吗?我只是在OnStartClient 中设置它,如给出的示例所示。
    • 不敢相信这是这样做的方法......该死!
    • 为什么要还原我的编辑?我添加了代码突出显示,使您的代码更易于阅读。
    • 不是有意交配。我不小心拒绝了,现在因为没有足够高的代表点而无法恢复。可以再提交吗?为编辑干杯:)
    • 很好的解决方案,但 netId 在所有客户端和服务器中都是唯一的,因此可能不需要syncvardocs.unity3d.com/2018.2/Documentation/ScriptReference/…
    【解决方案2】:

    我发现这篇文章非常有用,但我有一个小附录。

    netId 将位于层次结构的根部,因此知道您可以使用 transform.Find 向下遍历层次结构很有用。

    类似这样的..

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

    【讨论】:

      【解决方案3】:

      在阅读了 Andy Barnard 的解决方案后,我想出了这个稍作修改的解决方案。在NetworkIdentity 需要更改父级时,服务器可以调用客户端 RPC,而不是 SyncVar。这确实需要父母也有一个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 传递给服务器的命令,它只会调用服务器的公共方法。这是一组网络消息,不是最佳的,但是嗯。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-02-03
        • 2012-02-15
        • 2022-12-13
        • 1970-01-01
        • 2018-02-24
        • 2015-05-10
        • 1970-01-01
        • 2011-01-30
        相关资源
        最近更新 更多