Unity 5.1 网络 - 为主机和所有客户端生成 object 作为 child
Unity 5.1 Networking - Spawn an object as a child for the host and all clients
我有一个玩家object可以装备多把武器。当一件武器被装备时,它的变形 parent 被设置到它的手上。我已经搞砸了一段时间,无法让它同时适用于主机和客户端。现在我正在尝试在服务器上装备武器,并告诉所有客户端设置他们的 parents 转换。
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;
}
}
}
我知道这不是最优化的方法..但现在我只是想让它以任何可能的方式工作,然后努力调整它。看来这应该是一个简单的任务。
我们在多人游戏中做了类似的事情。您需要做一些事情才能使其正常工作。一、概念:
如您所见,在服务器上设置武器的父级是微不足道的。只需像通常在 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
}
然后在你的武器里class:
- 添加一个 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);
}
我发现这个 post 非常有用,但我有一个小附录。
netId 将位于层次结构的根部,因此知道您可以使用 transform.Find.
向下遍历层次结构很有用
像这样..
GameObject parentObject = ClientScene.FindLocalObject(parentNetId);
string pathToWeaponHolder = "Obj/targetObj";
transform.SetParent(parentObject.transform.Find(pathToWeaponHolder));
在阅读了 Andy Barnard 的解决方案后,我想出了这个稍微修改过的解决方案。在 NetworkIdentity
需要更改 parent 时,服务器可以调用客户端 RPC,而不是 SyncVar。这确实需要 parent 也有一个 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
传递给服务器的命令,它只会调用服务器的 public 方法。这是一组网络消息,但不是最理想的。
我有一个玩家object可以装备多把武器。当一件武器被装备时,它的变形 parent 被设置到它的手上。我已经搞砸了一段时间,无法让它同时适用于主机和客户端。现在我正在尝试在服务器上装备武器,并告诉所有客户端设置他们的 parents 转换。
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;
}
}
}
我知道这不是最优化的方法..但现在我只是想让它以任何可能的方式工作,然后努力调整它。看来这应该是一个简单的任务。
我们在多人游戏中做了类似的事情。您需要做一些事情才能使其正常工作。一、概念:
如您所见,在服务器上设置武器的父级是微不足道的。只需像通常在 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
}
然后在你的武器里class:
- 添加一个 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);
}
我发现这个 post 非常有用,但我有一个小附录。
netId 将位于层次结构的根部,因此知道您可以使用 transform.Find.
向下遍历层次结构很有用像这样..
GameObject parentObject = ClientScene.FindLocalObject(parentNetId);
string pathToWeaponHolder = "Obj/targetObj";
transform.SetParent(parentObject.transform.Find(pathToWeaponHolder));
在阅读了 Andy Barnard 的解决方案后,我想出了这个稍微修改过的解决方案。在 NetworkIdentity
需要更改 parent 时,服务器可以调用客户端 RPC,而不是 SyncVar。这确实需要 parent 也有一个 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
传递给服务器的命令,它只会调用服务器的 public 方法。这是一组网络消息,但不是最理想的。