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; } }
补充说明
- 生成对象时,代码会自动给对象添加
ObjectDragger组件并关联对应的跟踪图像名称,无需手动操作。 - 如果需要支持旋转拖拽,可以在
OnDrag中补充旋转逻辑,更新rotationOffset即可。 - 确保AR相机的投影模式为
Perspective,否则屏幕坐标转世界坐标会出现错误。
内容的提问来源于stack exchange,提问作者Тихон Тихомиров
相关产品推荐
相关产品推荐

