坐标与锚点:为什么拖动会偏移
触摸位置、节点位置和父节点里的位置,可能属于不同坐标空间。数字都是 (x, y),却不能直接互相赋值。这和把两个不同数据库里的 ID 当作同一实体类似:形式相同,语义不同。
先问坐标属于哪里
| 名称 | 用途 | 常见入口 |
|---|---|---|
| 局部坐标 | 相对父节点放置对象 | node.position、setPosition |
| 世界坐标 | 场景中的统一变换空间 | worldPosition |
| UI 触摸坐标 | 按 UI 设计分辨率处理的触摸位置 | event.getUILocation() |
| 屏幕坐标 | 输入设备/窗口的屏幕位置 | event.getLocation() |
普通 UI 场景中,常用 UITransform.convertToNodeSpaceAR 把 UI 触摸点转换到目标父节点的局部空间。特殊相机、世界空间 UI 或 3D 交互需要显式相机转换,不能照搬。

完整脚本:让父节点下的标记跟随触摸
TouchMarker.ts
import { _decorator, Component, EventTouch, Node, UITransform, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('TouchMarker')
export class TouchMarker extends Component {
@property(Node)
marker: Node | null = null;
onEnable() {
this.node.on(Node.EventType.TOUCH_START, this.place, this);
this.node.on(Node.EventType.TOUCH_MOVE, this.place, this);
}
onDisable() {
this.node.off(Node.EventType.TOUCH_START, this.place, this);
this.node.off(Node.EventType.TOUCH_MOVE, this.place, this);
}
private place(event: EventTouch) {
if (!this.marker || !this.marker.parent) return;
const transform = this.marker.parent.getComponent(UITransform);
if (!transform) return;
const p = event.getUILocation();
const local = transform.convertToNodeSpaceAR(new Vec3(p.x, p.y, 0));
this.marker.setPosition(local);
}
}
在标准 Canvas 下创建 TouchArea [UITransform, TouchMarker],尺寸覆盖测试区域;在其下创建可见的 Marker [Sprite],绑定 marker 字段。此脚本让标记中心追随手指,不保留抓取点偏移,也不实现多指拖拽。
锚点改变什么
锚点是 UI 尺寸与变换的参考点。中心锚点适合宝石、按钮;左下角等锚点适合一些对齐需求。修改锚点后,看起来“偏了”,常常是参考点改变,而非触摸事件失效。
拖拽成品还要记录初始抓取偏移、限制范围、处理 TOUCH_CANCEL,并在结束后校验落点。不要只做 TOUCH_MOVE 就宣称拖拽功能完成。
验收与提问
把父节点平移或缩放后,再触摸四角;目标仍跟随正确位置。给 AI:“请标出每个坐标变量所属空间,并在目标父节点的空间内设置 position。说明此方案是否适用于自定义相机。”