Unity VR构建切换场景后Meta Quest 2控制器失效问题求助
问题描述与修复方案
1.0 应用目标
允许用户上传3D模型,通过UI Canvas按钮实时调整(缩放、纹理),再将该模型带入其他场景(如厨房环境)进行进一步调整或移动。
2.0 技术栈
Unity XR Interaction Toolkit、Unity3D、C#、MacOS
3.0 问题
点击切换场景按钮后,新场景成功加载且3D模型保留,但Meta Quest 2控制器在新场景中完全失效,无法进行移动操作。
4.0 排查过程
- 初始阶段控制台持续输出
You have two event systems警告,无报错信息。移除场景2的Event System后,控制器恢复正常,但场景2的UI Canvas按钮失效,推测是前场景未正确卸载导致双Event System冲突。 - 使用
SceneManager.UnloadScene()后警告消失,但VR控制器仍失效,编辑器内测试一切正常。尝试SceneManager.UnloadSceneAsync()、先禁用再启用场景2的Event System等操作均无效果。 - 初步定位问题出在跨场景迁移模型的
SwitchScene脚本,推测XR Interaction Toolkit的核心组件未完成正确迁移——比如用户上传模型的XR Grab Interactable组件在新场景中丢失了对XR Interaction Manager GameObject的引用。
5.0 现有SwitchScene脚本
using System.Collections; using UnityEngine; using UnityEngine.SceneManager; public class SwitchScene : MonoBehaviour { // 在Inspector中指定要加载的场景名称 public string sceneName; // 要迁移到下一场景的GameObject private GameObject userModel; public void OnClick() { // 查找用户上传的模型(父GameObject名为"1") userModel = GameObject.Find("1"); StartCoroutine(LoadYourAsyncScene()); } IEnumerator LoadYourAsyncScene() { // 记录当前场景,用于后续卸载 Scene currentScene = SceneManager.GetActiveScene(); // 异步加载目标场景(加载模式为Additive) AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive); // 等待场景加载完成 while (!asyncLoad.isDone) { yield return null; } // 将模型移动到新加载的场景 SceneManager.MoveGameObjectToScene(userModel, SceneManager.GetSceneByName(sceneName)); // 卸载当前场景 SceneManager.UnloadScene(currentScene); } }
修复方案
针对控制器失效和XR组件引用丢失的问题,给出以下修复步骤:
1. 统一XR核心组件的持久化管理
将XR Interaction Manager、XR Origin等XR核心组件放在一个永不卸载的持久化场景(比如XRSetup场景)中,项目启动时优先加载该场景,后续业务场景均以Additive模式加载,避免场景切换时销毁或丢失这些核心依赖组件。
2. 修正场景切换逻辑,设置新场景为激活状态
当前脚本卸载旧场景后未指定新场景为激活场景,导致XR输入系统无法正确绑定。修改LoadYourAsyncScene协程:
using System.Collections; using UnityEngine; using UnityEngine.SceneManager; public class SwitchScene : MonoBehaviour { public string sceneName; private GameObject userModel; public void OnClick() { userModel = GameObject.Find("1"); StartCoroutine(LoadYourAsyncScene()); } IEnumerator LoadYourAsyncScene() { Scene currentScene = SceneManager.GetActiveScene(); AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive); asyncLoad.allowSceneActivation = true; while (!asyncLoad.isDone) { yield return null; } Scene targetScene = SceneManager.GetSceneByName(sceneName); // 将模型迁移到新场景 SceneManager.MoveGameObjectToScene(userModel, targetScene); // 设置新场景为激活场景 SceneManager.SetActiveScene(targetScene); // 异步卸载旧场景,避免阻塞主线程 yield return SceneManager.UnloadSceneAsync(currentScene); } }
3. 修复XR Grab Interactable的引用丢失问题
在模型迁移到新场景后,手动重新绑定XR Grab Interactable的交互管理器引用:
// 在MoveGameObjectToScene之后添加以下代码 XRGrabInteractable grabInteractable = userModel.GetComponentInChildren<XRGrabInteractable>(); if (grabInteractable != null) { grabInteractable.interactionManager = FindObjectOfType<XRInteractionManager>(); }
注意需要引入using UnityEngine.XR.Interaction.Toolkit;命名空间。
4. 处理Event System冲突
保留每个场景的Event System,在场景切换时自动禁用旧场景的Event System,启用新场景的:
// 在SetActiveScene之后添加以下代码 using UnityEngine.EventSystems; using System.Linq; // 禁用旧场景的Event System EventSystem oldEventSystem = currentScene.GetRootGameObjects() .Select(go => go.GetComponent<EventSystem>()) .FirstOrDefault(es => es != null); if (oldEventSystem != null) { oldEventSystem.enabled = false; } // 启用新场景的Event System EventSystem newEventSystem = targetScene.GetRootGameObjects() .Select(go => go.GetComponent<EventSystem>()) .FirstOrDefault(es => es != null); if (newEventSystem != null) { newEventSystem.enabled = true; }
内容的提问来源于stack exchange,提问作者Requies
相关产品推荐
相关产品推荐

