Unity Mirror多人跳棋:客户端与主机对象销毁异常求助
Unity Mirror多人跳棋对象销毁同步问题排查与解决
问题核心
- 基于Unity 2022.3.8 + Mirror开发多人跳棋,服务器强制销毁指定对象无法同步至客户端
- 使用
[Command]和[ClientRpc]时,调用CallServerToDestroy()前mainObj.ObjectToDestroy非空,但RpcDebug()中该变量变为null - 静态引用(如
transform.GetChild(0).gameObject)可正常销毁,动态对象引用失效
根本原因
- Mirror中网络对象的跨端引用不能直接传递
GameObject实例,客户端与服务器的对象实例ID不匹配,传递后会丢失有效引用 - 动态对象未正确挂载
NetworkIdentity组件,或未通过NetworkServer.Spawn()生成,导致服务器无法追踪同步状态 - 若
[Command]未设置requiresAuthority = false,非授权客户端发起的调用会被服务器拒绝(如果是服务器主动销毁,需关闭权限校验)
解决方案
1. 确保目标对象符合网络同步要求
- 给需要销毁的对象挂载
NetworkIdentity组件,不要勾选Server Only(除非该对象仅服务器可见) - 所有网络对象必须通过
NetworkServer.Spawn()生成,禁止直接用Instantiate()创建后手动赋值
2. 修改Command/Rpc逻辑,传递netId而非GameObject引用
直接传递GameObject会导致跨端引用失效,改用NetworkIdentity的netId(唯一网络标识)来定位对象:
修改后的核心代码示例
// 调用销毁的入口方法(客户端/服务器均可触发) public void CallServerToDestroy() { if (mainObj.ObjectToDestroy != null && mainObj.ObjectToDestroy.TryGetComponent(out NetworkIdentity targetNetId)) { CmdDestroyTarget(targetNetId.netId); } } // 服务器端Command方法,接收netId并执行销毁 [Command(requiresAuthority = false)] // 允许无权限调用(服务器强制销毁场景) public void CmdDestroyTarget(uint targetNetId) { // 通过netId从服务器已生成对象列表中查找目标 if (NetworkServer.spawned.TryGetValue(targetNetId, out NetworkIdentity targetIdentity)) { // 使用Mirror内置方法销毁,自动同步至所有客户端 NetworkServer.Destroy(targetIdentity.gameObject); // 如需客户端额外逻辑,传递netId而非GameObject RpcNotifyDestroy(targetNetId); } } // 客户端Rpc方法,处理销毁后的额外逻辑(可选) [ClientRpc] public void RpcNotifyDestroy(uint targetNetId) { if (NetworkClient.spawned.TryGetValue(targetNetId, out NetworkIdentity targetIdentity)) { Debug.Log($"客户端已同步销毁对象:{targetIdentity.gameObject.name}"); } }
3. 替代方案:直接使用NetworkServer.Destroy自动同步
如果不需要额外客户端逻辑,服务器直接调用NetworkServer.Destroy()即可,无需手动编写ClientRpc,Mirror会自动向所有客户端发送销毁指令。
关键注意事项
- 禁止在客户端直接销毁网络对象,必须由服务器发起销毁操作
- 检查
NetworkIdentity的Local Player Authority设置,若对象属于玩家权限,需确保服务器有权限销毁(或关闭权限校验) - 若目标对象是临时生成的特效/道具,确保生成时调用
NetworkServer.Spawn()并在销毁时调用NetworkServer.Destroy()
内容的提问来源于stack exchange,提问作者Piotr Siudeja
相关产品推荐
相关产品推荐

