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

AR Tracked Image Manager下实现对象在图片上移动的技术咨询

问题原因

你的代码在OnTrackedImagesChanged的updated分支中,每次AR跟踪图像更新时,都会强制将对象的位置和旋转同步到跟踪图像的中心——这就是拖拽对象后,它立刻弹回图片中心的核心原因。

解决方案

需要给每个跟踪对象记录其相对于跟踪图像的位置/旋转偏移量,这样即使图像位置更新,对象也会保持用户拖拽后的相对位置,而非被强制重置到中心。

步骤1:定义偏移数据类

先创建一个类来存储每个对象与对应跟踪图像的偏移信息:

[System.Serializable]
public class TrackedObjectOffset
{
    public GameObject obj;
    public Vector3 positionOffset;
    public Quaternion rotationOffset;
}

步骤2:修改跟踪管理器逻辑

将原有的对象字典替换为存储偏移信息的字典,更新图像时用偏移量计算对象位置,而非直接赋值:

public class MultipleImagesTrackingManager : MonoBehaviour
{
    public ARTrackedImageManager trackedImageManager;
    public GameObject[] objectsToSpawn;

    // 存储对象及对应的相对偏移量
    private Dictionary<string, TrackedObjectOffset> trackedObjects = new Dictionary<string, TrackedObjectOffset>();

    void Start()
    {
        trackedImageManager.trackedImagesChanged += OnTrackedImagesChanged;
    }

    void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
    {
        foreach (var trackedImage in eventArgs.added)
        {
            if (!trackedObjects.ContainsKey(trackedImage.referenceImage.name))
            {
                GameObject objectToSpawn = GetObjectToSpawn(trackedImage.referenceImage.name);
                if (objectToSpawn != null)
                {
                    GameObject spawnedObject = Instantiate(objectToSpawn, trackedImage.transform.position, trackedImage.transform.rotation);
                    // 初始化偏移量为0(默认在图像中心)
                    var offset = new TrackedObjectOffset
                    {
                        obj = spawnedObject,
                        positionOffset = Vector3.zero,
                        rotationOffset = Quaternion.identity
                    };
                    trackedObjects.Add(trackedImage.referenceImage.name, offset);
                    
                    // 给对象添加拖拽组件并关联图像名称
                    var dragger = spawnedObject.AddComponent<ObjectDragger>();
                    dragger.SetLinkedImageName(trackedImage.referenceImage.name);
                }
            }
        }

        foreach (var trackedImage in eventArgs.updated)
        {
            if (trackedObjects.TryGetValue(trackedImage.referenceImage.name, out var offsetData))
            {
                // 用跟踪图像位置+偏移量计算对象最终位置
                offsetData.obj.transform.position = trackedImage.transform.TransformPoint(offsetData.positionOffset);
                offsetData.obj.transform.rotation = trackedImage.transform.rotation * offsetData.rotationOffset;
            }
        }

        foreach (var trackedImage in eventArgs.removed)
        {
            if (trackedObjects.TryGetValue(trackedImage.referenceImage.name, out var offsetData))
            {
                Destroy(offsetData.obj);
                trackedObjects.Remove(trackedImage.referenceImage.name);
            }
        }
    }

    GameObject GetObjectToSpawn(string imageName)
    {
        foreach (var obj in objectsToSpawn)
        {
            if (obj.name == imageName)
            {
                return obj;
            }
        }
        return null;
    }

    // 供拖拽脚本调用,更新对象相对偏移量
    public void UpdateObjectOffset(string imageName, Vector3 newPosOffset, Quaternion newRotOffset)
    {
        if (trackedObjects.TryGetValue(imageName, out var offsetData))
        {
            offsetData.positionOffset = newPosOffset;
            offsetData.rotationOffset = newRotOffset;
        }
    }
}

步骤3:实现拖拽逻辑

给可拖拽对象添加拖拽处理脚本ObjectDragger:

using UnityEngine;
using UnityEngine.EventSystems;

public class ObjectDragger : MonoBehaviour, IPointerDownHandler, IDragHandler
{
    private MultipleImagesTrackingManager trackingManager;
    private Camera arCamera;
    private string linkedImageName;
    private ARTrackedImage linkedTrackedImage;
    private float initialZDistance;

    void Start()
    {
        trackingManager = FindObjectOfType<MultipleImagesTrackingManager>();
        arCamera = Camera.main;
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        // 记录对象与相机的初始距离,确保拖拽时深度不变
        initialZDistance = arCamera.WorldToScreenPoint(transform.position).z;
        // 找到关联的跟踪图像
        linkedTrackedImage = FindLinkedTrackedImage();
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (linkedTrackedImage == null) return;

        // 将屏幕触摸坐标转换为世界坐标
        Vector3 touchWorldPos = arCamera.ScreenToWorldPoint(new Vector3(eventData.position.x, eventData.position.y, initialZDistance));
        transform.position = touchWorldPos;

        // 计算对象相对于跟踪图像的局部偏移
        Vector3 localOffset = linkedTrackedImage.transform.InverseTransformPoint(transform.position);
        Quaternion localRotOffset = Quaternion.Inverse(linkedTrackedImage.transform.rotation) * transform.rotation;
        
        // 更新偏移量到管理器
        trackingManager.UpdateObjectOffset(linkedImageName, localOffset, localRotOffset);
    }

    private ARTrackedImage FindLinkedTrackedImage()
    {
        foreach (var trackedImage in trackingManager.trackedImageManager.trackables)
        {
            if (trackedImage.referenceImage.name == linkedImageName)
            {
                return trackedImage;
            }
        }
        return null;
    }

    // 设置当前对象关联的跟踪图像名称
    public void SetLinkedImageName(string name)
    {
        linkedImageName = name;
    }
}

补充说明

  1. 生成对象时,代码会自动给对象添加ObjectDragger组件并关联对应的跟踪图像名称,无需手动操作。
  2. 如果需要支持旋转拖拽,可以在OnDrag中补充旋转逻辑,更新rotationOffset即可。
  3. 确保AR相机的投影模式为Perspective,否则屏幕坐标转世界坐标会出现错误。

内容的提问来源于stack exchange,提问作者Тихон Тихомиров

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 23:43:11