Unity AR中重复识别图像时覆盖物不显示的问题排查
问题解决:Unity AR图像重复识别不显示覆盖物
问题说明
我写了一个Unity AR脚本,原本可以扫描图像并在检测到的图像上方显示覆盖物。为了实现覆盖物在图像丢失后延迟10秒再销毁的效果,我移除了eventArgs.updated循环里的_instantiatedPrefabs[trackedImage.referenceImage.name].SetActive(trackedImage.trackingState == TrackingState.Tracking);这行代码,但现在出现了同一图像再次被检测时,覆盖物不再显示的问题。
问题原因
- 直接销毁
ARTrackedImage对象(trackedImage.gameObject)会破坏ARFoundation的跟踪逻辑,后续再检测到同一图像时,无法触发新增跟踪事件来重建覆盖物。 - 字典
_instantiatedPrefabs中保留着已销毁对象的引用,即使图像重新被检测,因为判断!_instantiatedPrefabs.ContainsKey(imageName)不成立,不会重新实例化覆盖物。
修改后的代码
using System.Collections.Generic; using UnityEngine; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.ARSubsystems; using System; using System.Collections; [RequireComponent(typeof(ARTrackedImageManager))] public class PlaceTrackedImages : MonoBehaviour { // AR跟踪图像管理器组件引用 private ARTrackedImageManager _trackedImagesManager; // 需要实例化的预制体数组 public GameObject[] ArPrefabs; // 已实例化预制体的字典,键为图像名称 private readonly Dictionary<string, GameObject> _instantiatedPrefabs = new Dictionary<string, GameObject>(); // 存储每个覆盖物的延迟销毁协程,用于取消销毁 private readonly Dictionary<string, Coroutine> _destroyCoroutines = new Dictionary<string, Coroutine>(); private void Awake() { _trackedImagesManager = GetComponent<ARTrackedImageManager>(); } private void OnEnable() { _trackedImagesManager.trackedImagesChanged += OnTrackedImagesChanged; } private void OnDisable() { _trackedImagesManager.trackedImagesChanged -= OnTrackedImagesChanged; } private void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs) { // 处理新检测到的图像 foreach (var trackedImage in eventArgs.added) { var imageName = trackedImage.referenceImage.name; if (_instantiatedPrefabs.ContainsKey(imageName)) continue; foreach (var curPrefab in ArPrefabs) { if (string.Compare(curPrefab.name, imageName, StringComparison.OrdinalIgnoreCase) == 0) { var newPrefab = Instantiate(curPrefab, trackedImage.transform); _instantiatedPrefabs[imageName] = newPrefab; break; } } } // 处理跟踪状态更新的图像 foreach (var trackedImage in eventArgs.updated) { var imageName = trackedImage.referenceImage.name; if (!_instantiatedPrefabs.TryGetValue(imageName, out var prefab)) continue; if (trackedImage.trackingState == TrackingState.Tracking) { // 图像重新被跟踪,取消延迟销毁并激活覆盖物 prefab.SetActive(true); if (_destroyCoroutines.ContainsKey(imageName)) { StopCoroutine(_destroyCoroutines[imageName]); _destroyCoroutines.Remove(imageName); } } else if (trackedImage.trackingState is TrackingState.Limited or TrackingState.None) { // 图像丢失,启动10秒延迟销毁协程 if (!_destroyCoroutines.ContainsKey(imageName)) { var coroutine = StartCoroutine(DelayedDestroyPrefab(imageName, 10f)); _destroyCoroutines[imageName] = coroutine; } } } // 处理被系统移除的图像,清理资源 foreach (var trackedImage in eventArgs.removed) { var imageName = trackedImage.referenceImage.name; if (_instantiatedPrefabs.ContainsKey(imageName)) { Destroy(_instantiatedPrefabs[imageName]); _instantiatedPrefabs.Remove(imageName); if (_destroyCoroutines.ContainsKey(imageName)) { StopCoroutine(_destroyCoroutines[imageName]); _destroyCoroutines.Remove(imageName); } } } } /// <summary> /// 延迟销毁覆盖物的协程 /// </summary> private IEnumerator DelayedDestroyPrefab(string imageName, float delay) { yield return new WaitForSeconds(delay); if (_instantiatedPrefabs.TryGetValue(imageName, out var prefab)) { Destroy(prefab); _instantiatedPrefabs.Remove(imageName); } _destroyCoroutines.Remove(imageName); } }
关键修改点
- 保留ARTrackedImage对象:不销毁ARFoundation的跟踪载体,确保图像重复检测时能正常触发更新事件。
- 协程控制延迟销毁:用协程实现覆盖物的10秒延迟销毁,在图像重新被跟踪时可以立即取消销毁并激活覆盖物。
- 维护协程引用字典:记录每个覆盖物的销毁协程,避免重复启动协程,同时能及时取消未完成的销毁操作。
- 清理移除的图像资源:在图像被AR系统彻底移除时,清理对应的覆盖物和协程引用,避免内存泄漏。
内容的提问来源于stack exchange,提问作者syskhill
相关产品推荐
相关产品推荐

