You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity AR中重复识别图像时覆盖物不显示的问题排查

问题解决:Unity AR图像重复识别不显示覆盖物

问题说明

我写了一个Unity AR脚本,原本可以扫描图像并在检测到的图像上方显示覆盖物。为了实现覆盖物在图像丢失后延迟10秒再销毁的效果,我移除了eventArgs.updated循环里的_instantiatedPrefabs[trackedImage.referenceImage.name].SetActive(trackedImage.trackingState == TrackingState.Tracking);这行代码,但现在出现了同一图像再次被检测时,覆盖物不再显示的问题。

问题原因

  1. 直接销毁ARTrackedImage对象(trackedImage.gameObject)会破坏ARFoundation的跟踪逻辑,后续再检测到同一图像时,无法触发新增跟踪事件来重建覆盖物。
  2. 字典_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 00:07:46