在开发基于Unity Mirror网络架构的VR多人手术模拟系统时,我们遇到了一个复杂的客户端移动同步问题:
我们的系统采用了基于角色的多人架构:
系统中存在三套移动机制:
通过自动化调试系统,我们发现了完整的问题链:
// SceneScript.cs OnStartServer()
if (isServer && !StepData.Instance.isOnlie)
{
DebugWrapper.Log("[SERVER] 禁用OVRCameraRig的MoveOVRPlayer组件");
GameObject.Find("OVRCameraRig").GetComponent<MoveOVRPlayer>().enabled = false;
}
// CameraManager.cs Start()
if (!isLocalPlayer)
{
GetComponent<OVRCameraRig>().enabled = false; // 禁用整个OVR系统
GetComponent<OVRManager>().enabled = false;
GetComponent<OVRHeadsetEmulator>().enabled = false;
// ⬇️ 级联效应:MoveOVRPlayer也被禁用了!
}
这是整个工作流程中缺失的关键步骤:
// 应该在CameraManager.cs中添加:
if (!isLocalPlayer) {
GetComponent<OVRCameraRig>().enabled = false;
GetComponent<OVRManager>().enabled = false;
GetComponent<OVRHeadsetEmulator>().enabled = false;
// ⬇️ 关键的缺失步骤!
var moveComponent = GetComponent<MoveOVRPlayer>();
if (moveComponent != null) {
moveComponent.enabled = true; // 重新启用键盘移动
}
}
为了精确定位问题,我们开发了基于Unity RuntimeInitializeOnLoadMethod
的自启动调试系统:
public static class SimpleMoveOVRDebug
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void Initialize()
{
// 自动启动调试系统,无需场景设置
DebugWrapper.Log(" 自启动调试系统已启动");
StartCoroutine(AutoMonitorWorkflow());
}
private static void CheckForCriticalIssue()
{
var networkIdentities = Object.FindObjectsOfType<NetworkIdentity>();
foreach (var identity in networkIdentities)
{
if (!identity.isLocalPlayer && !identity.hasAuthority) // 观察者客户端
{
var moveComponent = identity.GetComponent<MoveOVRPlayer>();
if (moveComponent != null && !moveComponent.enabled)
{
DebugWrapper.LogError(" 发现关键问题:观察者客户端的MoveOVRPlayer被禁用!");
LogSolution();
}
}
}
}
}
在 CameraManager.cs
中添加重新启用逻辑:
private void Start()
{
if (!isLocalPlayer)
{
GetComponent<OVRCameraRig>().enabled = false;
GetComponent<OVRManager>().enabled = false;
GetComponent<OVRHeadsetEmulator>().enabled = false;
// 修复:重新启用MoveOVRPlayer以支持键盘移动
var moveComponent = GetComponent<MoveOVRPlayer>();
if (moveComponent != null) {
moveComponent.enabled = true;
}
if (camere != null) {
Destroy(camere);
}
}
}
为了解决玩家角色交叉绑定问题,我们开发了自动绑定系统:
public class MoveOVRPlayer : MonoBehaviour
{
public GameObject moveplayer;
void Start()
{
StartCoroutine(AutoBindLocalPlayer());
}
IEnumerator AutoBindLocalPlayer()
{
yield return new WaitForSeconds(1f);
if (moveplayer == null)
{
// 自动查找本地玩家
NetworkIdentity[] allNetworkObjects = FindObjectsOfType<NetworkIdentity>();
foreach (NetworkIdentity netObj in allNetworkObjects)
{
if (netObj.isLocalPlayer)
{
moveplayer = netObj.gameObject;
Debug.Log($"[MoveOVRPlayer] 自动绑定到本地玩家: {moveplayer.name}");
break;
}
}
}
}
}
修复 LinkPlayer.cs
中可能导致OVR组件被意外销毁的代码:
if (!NetworkClient.active)
{
if(GameObject.Find("0(Clone)"))
{
GameObject obj = GameObject.Find("0(Clone)");
// 保护包含OVRCameraRig的对象
if (obj.GetComponent<OVRCameraRig>() == null)
{
Destroy(obj.gameObject);
}
else
{
Debug.Log("保护OVRCameraRig对象免于销毁");
}
}
}
通过调试系统验证,修复后的系统表现:
观察者客户端 NetID:6 的MoveOVRPlayer被禁用!
根本原因:OVRCameraRig禁用级联到MoveOVRPlayer
✅ [MoveOVRPlayer] 自动绑定到本地玩家: BasicMotionsDummy(Clone)
✅ 工作流程正常 - 所有客户端都具备移动能力
这个问题的根源在于系统演进过程中,网络优化代码(CameraManager)没有考虑到键盘移动系统(MoveOVRPlayer)的依赖关系。这提醒我们: