Unity多人游戏player.transform.childCount客户端计数异常求助
多人游戏物品拾取同步问题:客户端Player子物体计数异常
我正在开发一款多人游戏,实现物品拾取与丢弃功能时遇到问题:向Player节点添加子物体后,player.transform.childCount在客户端返回3,但预期应为4。添加Debug.Log调试后,客户端输出“3: 1”和“3”,主机端输出“4: 0”——主机端功能正常,但客户端计数错误。
相关代码
public void AddItem(ItemData itemData) { if(itemData == null) return; this.itemData = itemData; icon.sprite = itemData.icon; icon.enabled = true; if (player == null) player = NetworkManager.Singleton.SpawnManager.GetLocalPlayerObject().gameObject; if (player != null) { NetworkObject networkObject = player.GetComponent<NetworkObject>(); if (networkObject != null) { Debug.Log(player.transform.childCount + ": 1"); AddItemToHandServerRpc(itemData.index, networkObject.NetworkObjectId); Debug.Log(player.transform.childCount); if (player.transform.childCount == 4) { GameObject item = player.transform.GetChild(player.transform.childCount - 1).gameObject; item.GetComponent<SetPossionToHand>().enabled = true; item.transform.localScale = new Vector3(.01f, .01f, .01f); } else { Debug.Log("fsjfhshuyferh"); } } else Debug.Log("none"); } else { Debug.LogError("Hand object is not properly initialized or is disabled."); } } public void ClearSlot(bool delete) { itemData = null; icon.sprite = null; icon.enabled = false; if (player.transform.childCount == 4 && delete) { if (player == null) player = NetworkManager.Singleton.SpawnManager.GetLocalPlayerObject().gameObject; GameObject item = player.transform.GetChild(player.transform.childCount - 1).gameObject; DespawnObjectServerRpc(item.GetComponent<NetworkObject>().NetworkObjectId); } } [ServerRpc] void AddItemToHandServerRpc(int index, ulong handId) { GameObject item = list.prefabs[index]; var instance = Instantiate(item); var instanceNetworkObject = instance.GetComponent<NetworkObject>(); Transform player = NetworkManager.Singleton.SpawnManager.SpawnedObjects[handId].gameObject.transform; instanceNetworkObject.SpawnWithOwnership(handId); instanceNetworkObject.TrySetParent(player, false); Debug.Log(player.transform.childCount + ": 0"); }
客户端侧
- 层级视图:

- 控制台输出:

主机侧
- 层级视图:

- 控制台输出:

解决方案
1. 修复ServerRpc调用后的同步时机问题
你在调用AddItemToHandServerRpc后立刻读取player.transform.childCount,但ServerRpc是异步执行的:客户端发送请求到服务器,服务器完成物品实例化、父节点设置后,才会把同步数据发回客户端。这中间的延迟导致客户端调用Rpc后马上读计数,服务器的修改还没同步过来,自然得到旧值。
不要在Rpc调用后立刻执行依赖子物体状态的逻辑,改用以下两种方式处理:
方式一:用NetworkVariable同步物品数量
给Player的NetworkBehaviour添加一个NetworkVariable<int>跟踪手持物品数量,服务器修改后自动同步到所有客户端,客户端监听变量变化再执行逻辑:
public class PlayerInventory : NetworkBehaviour { public NetworkVariable<int> HeldItemCount = new NetworkVariable<int>(0); public void AddHeldItem() { if (IsServer) HeldItemCount.Value++; } void Awake() { HeldItemCount.OnValueChanged += OnHeldItemCountChanged; } void OnHeldItemCountChanged(int oldValue, int newValue) { if (IsLocalPlayer && newValue == 4) { GameObject item = transform.GetChild(transform.childCount - 1).gameObject; item.GetComponent<SetPossionToHand>().enabled = true; item.transform.localScale = new Vector3(.01f, .01f, .01f); } } }
然后在AddItemToHandServerRpc中调用player.GetComponent<PlayerInventory>().AddHeldItem();。
方式二:监听物品Spawn事件
客户端监听NetworkObject的Spawn事件,当物品被同步到本地时再执行初始化逻辑:
void OnEnable() { NetworkManager.Singleton.SpawnManager.OnSpawnedObject += OnSpawnedObject; } void OnDisable() { NetworkManager.Singleton.SpawnManager.OnSpawnedObject -= OnSpawnedObject; } void OnSpawnedObject(NetworkObject obj) { if (IsLocalPlayer && obj.transform.parent == player.transform) { if (player.transform.childCount == 4) { obj.GetComponent<SetPossionToHand>().enabled = true; obj.transform.localScale = new Vector3(.01f, .01f, .01f); } } }
2. 修正ClearSlot的空引用风险
ClearSlot中先访问player.transform.childCount再判断player是否为空,会触发空指针异常,调整代码顺序:
public void ClearSlot(bool delete) { itemData = null; icon.sprite = null; icon.enabled = false; if (delete) { if (player == null) player = NetworkManager.Singleton.SpawnManager.GetLocalPlayerObject().gameObject; if (player != null && player.transform.childCount == 4) { GameObject item = player.transform.GetChild(player.transform.childCount - 1).gameObject; DespawnObjectServerRpc(item.GetComponent<NetworkObject>().NetworkObjectId); } } }
3. 验证物品Prefab的网络配置
- 确保物品Prefab带有
NetworkObject组件,且已添加到NetworkManager的Spawnable Prefabs列表中; - 服务器端
instanceNetworkObject.TrySetParent(player, false)的参数false是正确的,避免修改父物体本地坐标,确保父节点关系能正确同步到客户端。
内容的提问来源于stack exchange,提问作者Jonda_MC
相关产品推荐
相关产品推荐

