Unity3d Unity 5.1网络-为主机和所有客户端生成一个对象作为子对象

Unity3d Unity 5.1网络-为主机和所有客户端生成一个对象作为子对象,unity3d,network-programming,multiplayer,unity-networking,Unity3d,Network Programming,Multiplayer,Unity Networking,我有一个可以装备多种武器的玩家对象。装备武器时,其变换的父对象设置为其手。我已经把这件事搞砸了一段时间,无法让它同时适用于主机和客户端。现在,我正在尝试在服务器上安装该武器,并告诉所有客户端设置其父转换 公共网络实例ID武器网络ID; [命令] void Cmd_equibwearm() { var weaponObject=实例化(Resources.Load(“枪”), 手的位置, 四元数。欧拉(0f,0f,0f))作为游戏对象; weaponObject.transform.parent=

我有一个可以装备多种武器的玩家对象。装备武器时,其变换的父对象设置为其手。我已经把这件事搞砸了一段时间,无法让它同时适用于主机和客户端。现在,我正在尝试在服务器上安装该武器,并告诉所有客户端设置其父转换

公共网络实例ID武器网络ID;
[命令]
void Cmd_equibwearm()
{
var weaponObject=实例化(Resources.Load(“枪”),
手的位置,
四元数。欧拉(0f,0f,0f))作为游戏对象;
weaponObject.transform.parent=手;
NetworkServer.Spawn(武器对象);
//成套武器
var-wearm=weaponObject.GetComponent()作为武器;
weaponNetId=weaponObject.GetComponent().netId;
Rpc_SetParentGameobject(武器ID);
}
[客户端RPC]
public void Rpc_SetParentGameobject(NetworkInstanceId netID)
{   
武器网络ID=网络ID;
}
在更新中,我正在更新武器转换

void更新(){
//在客户端上设置子武器转换
如果(!isServer){
if(武器ID.Value!=0&!武装){
GameObject子对象=NetworkServer.FindLocalObject(武器网络ID);
if(child!=null){
child.transform.parent=手;
}
}
}

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

我们在多人游戏中也做了类似的事情。要让它工作,你需要做一些事情。首先,概念:

正如您所发现的,在服务器上设置武器的父对象很简单。只需像通常在Unity中一样设置变换的父对象。但是,在使用
NetworkServer.Spawn在服务器上生成此对象后,它将稍后在场景根目录中的客户端上生成(生成的预置之外的层次结构不同步)

因此,为了调整客户端的层次结构,我建议您:

  • 使用SyncVar在服务器和客户端之间同步父对象的netID
  • 在客户端上生成对象时,使用同步netID查找父对象,并将其设置为变换的父对象

因此,我会调整您的代码,使其看起来像这样。首先,在生成武器之前设置武器的父netId。这将确保在客户端生成武器时,会设置netId

[命令]
void Cmd_equibwearm()
{
var weaponObject=实例化(Resources.Load(“枪”),
手的位置,
四元数。欧拉(0f,0f,0f))作为游戏对象;
weaponObject.parentNetId=hand.netId;//设置父网络ID
weaponObject.transform.parent=hand;//在服务器上设置父转换
NetworkServer.Spawn(weaponObject);//生成对象
}

然后在你的武器课上:

  • 添加parentNetId属性
  • 将其标记为
    [SyncVar]
    ,以便在服务器和客户端副本之间进行同步
  • 在客户端上生成时,使用netId查找父对象并将其设置为变换的父对象
也许是这样的:

[SyncVar]
公共网络实例ID parentNetId;
公共覆盖无效OnStartClient()
{
//当我们在客户机上生成时,
//使用父对象的ID查找父对象,
//并将其设置为变换的父级。
GameObject parentObject=ClientScene.FindLocalObject(parentNetId);
SetParent(parentObject.transform);
}

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

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

像这样的

GameObject parentObject=ClientScene.FindLocalObject(parentNetId);
字符串路径towaponholder=“Obj/targetObj”;
SetParent(parentObject.transform.Find(pathToWeaponHolder));

在阅读了Andy Barnard的解决方案后,我提出了这个稍加修改的解决方案。在
网络标识
需要更改父对象时,服务器可以随时调用客户端RPC,而不是SyncVar。这确实要求父对象也具有
网络标识
(尽管它不需要是注册的预置)

public void服务器\u SetParent(NetworkIdentity parentNetworkIdentity){
if(parentNetworkIdentity!=null){
//在服务器上本地设置
transform.SetParent(parentNetworkIdentity.transform);
//在客户端上远程设置
RpcClient_SetParent(parentNetworkIdentity.netId,resetTransform);
}
否则{
//在服务器上本地设置
transform.SetParent(null);
//在客户端上远程设置
RpcClient_SetParent(NetworkInstanceId.Invalid,resetTransform);
}
}
[客户端RPC]
void RpcClient\u SetParent(NetworkInstanceId newParentNetId){
Transform parentTransform=null;
if(newParentNetId!=网络实例ID.Invalid){
//通过netid查找父对象并将自己设置为子对象
var parentGobj=ClientScene.FindLocalObject(newParentNetId);
if(parentGobj!=null){
parentTransform=parentGobj.transform;
}
否则{
Debug.LogWarningFormat(“{0}找不到网络标识“{1}.”,gameObject.name,newParentNetId.Value);
}
}
transform.SetParent(parentTransform);
}
这两个是
网络行为的一部分
,显然
需要组件(typeof(NetworkIdentity))