Unity Mirror多人游戏:解决GameObject仅对单个玩家可见的主机异常
问题分析
你的问题根源在于主机既是服务器又是客户端,共享同一个对象实例:
当非主机客户端拾取时,服务器将对象设为不可见,再通过TargetRpc给拾取者的客户端单独设为可见——此时客户端和服务器的对象是分离的,状态互不干扰,所以其他客户端看不到,拾取者能看到,逻辑正常。
但主机拾取时,TargetRpc在主机客户端执行的SetObjectVisibility(true)会直接修改服务器端的对象状态(因为两者是同一个实例),导致服务器端的对象变回可见,同步给所有客户端,所以所有人都能看到该对象。
修复方案
改用Mirror官方推荐的观察者可见性系统来控制对象的可见范围,这比手动修改Renderer/Image状态更可靠,也能避免主机的实例冲突问题。
步骤1:配置NetworkIdentity
在目标GameObject的NetworkIdentity组件中,将Visibility设置为Visible To Observers(默认是Visible To All)。这样只有被加入观察者列表的客户端能看到该对象。
步骤2:修改代码逻辑
替换原有的可见性控制代码,改为通过管理观察者列表来限制可见范围:
[Command] public void CmdHideObject() { // 建议替换为更可靠的目标对象查找方式(比如标签、引用),避免FindObjectOfType的不确定性 var targetHideScript = FindObjectOfType<HideScript>(); if (targetHideScript != null) { targetHideScript.HideObject(connectionToClient); } } [Server] public void HideObject(NetworkConnectionToClient pickingClient) { NetworkIdentity netIdentity = GetComponent<NetworkIdentity>(); // 清空所有现有观察者,确保只有拾取者能看到 netIdentity.Observers.Clear(); // 将拾取者的连接加入观察者列表 if (pickingClient != null) { netIdentity.AddObserver(pickingClient); } // 确保服务器端对象处于可见状态,同步给拾取者 SetObjectVisibility(true); } private void SetObjectVisibility(bool visible) { GetComponent<Renderer>().enabled = visible; foreach (var image in GetComponentsInChildren<Image>()) { image.enabled = visible; } }
补充说明
为什么这个方案有效?
- 当
Visibility设为Visible To Observers时,不在观察者列表的客户端会自动隐藏该对象(Mirror不会向这些客户端同步该对象的任何数据)。 - 服务器端保持对象可见,仅将拾取者的连接加入观察者列表,所以只有拾取者能收到同步数据,看到对象;其他客户端看不到。
- 主机的情况被自动处理:主机作为服务器端的连接会被加入观察者列表,同时因为主机客户端复用服务器实例,直接就能看到可见的对象,不会出现状态冲突。
- 当
优化建议
- 不要用
FindObjectOfType<HideScript>(),可以给目标对象设置唯一标签,用GameObject.FindWithTag()查找,或者在玩家脚本中直接引用目标对象,避免场景中有多个实例时出错。
- 不要用
内容的提问来源于stack exchange,提问作者Davor Homa
相关产品推荐
相关产品推荐

