跳到主要内容

坐标与相机:为什么指针到了,物品却没跟上

拖拽功能里,玩家手指在屏幕上移动,物品却出现在角落,这是最常见的入门问题之一。通常不是数学太难,而是把两种坐标当成了一种。

每次读写位置之前,先问:这是哪个空间里的位置?

屏幕坐标经相机进入世界空间,UI 坐标由 Canvas 和父 RectTransform 决定

四种常见位置​

坐标表达什么常见入口
屏幕坐标指针在屏幕上的像素位置输入事件的位置
世界坐标对象在场景中的位置Transform.position
本地坐标相对父对象的位置Transform.localPosition
UI 布局坐标相对锚点等布局规则的位置RectTransform.anchoredPosition

同样是数字 (100, 100),在不同空间里的含义完全不同。父对象缩放、旋转或锚点变化,也会改变局部位置最终显示的结果。

相机怎样参与转换​

场景视图像开发者自由移动的观察工具;Game 视图主要展示游戏摄像机的输出。你能在 Scene 中看见一个物品,并不保证它在摄像机可见范围、正确层级或正确深度内。

2D 常用正交相机,物品不会随离相机远近而缩小;透视相机会产生近大远小。把屏幕点转换到世界点时,深度或射线命中的平面也要明确,不能只传入 x 和 y 然后假定结果就在棋盘上。

Canvas 拖拽的推荐思路​

UI 物品建议通过 RectTransformUtility 把屏幕位置转成目标父节点的局部位置。下面是思路片段,假设拖拽物体是父 RectTransform 的直接子节点,使用一致的锚点设置;实际工程需保留按下时的抓取偏移。

if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
parentRect, eventData.position, eventData.pressEventCamera,
out Vector2 localPoint))
{
itemRect.localPosition = new Vector3(localPoint.x, localPoint.y, 0f);
}

eventData 来自 UI 指针事件。Screen Space - Overlay 场景下转换相机通常为 null;Camera / World Space 模式要使用正确事件相机。不要随意填 Camera.main 来“试试”。

localPosition 与 anchoredPosition 也不是任意锚点下都可互换。工程里应固定一种布局约定,并在转换函数旁说明约束。

父子关系还影响什么​

把物品从棋盘移动到拖拽层,能让它显示在其他控件前面,但会改变父坐标空间。重新设置父节点后,要决定是否保留世界位置,并重新计算局部位置。

放下时,不要从像素位置直接推断存档格子。由棋盘布局或命中结果得到格子编号,再交给规则判断。业务状态保存的是“第几格”,视图负责把格子映射到画面位置。

验收时改变环境​

至少试竖屏、超长屏、不同 Canvas 缩放和棋盘父节点缩放;从物品边缘抓取,确认没有瞬移到手指中心。给 AI 的任务里写清 Canvas 模式、锚点、父子层级和目标坐标空间,能减少大量来回修补。

参考:UI 基础布局、ScreenPointToLocalPointInRectangle。