Galacean 的触控是基于 Pointer 实现的,它抹平了 Mouse 与 Touch 的差异,使得触控在概念和接口上都得到了统一。
在 Galacean 中,无论是 PC 上的鼠标,移动端的触控笔或是指头,当他在触控范围内发生对应行为时( Down, Move, etc),都会被实例化为 Pointer,您可以在 InputManager 里获取到当前活动着的所有触控点。
需要注意的是,每个触控点都是相互独立的,它们响应对应的事件并回调相应的钩子函数。
每个触控点都会在 PointerDown 或者 PointerMove 中开启自己的一生,在 PointerLeave 或者 PointerCancel 后黯然离场,在 Galacean 中,您可以通过 Pointer.phase
获取这个触控点的即时状态。
参照 W3C 标准 与微软相关文档,Galacean 对触控按键的定义如下:
枚举 | 解释 |
---|---|
None | 无触控按键按下 |
Primary | 设备的主按键,通常为左键(鼠标)或单按键设备上的唯一按键(指头) |
Secondary | 设备的次级按键,通常为右键(鼠标) |
Auxiliary | 设备的辅助按键,通常为滚轮(鼠标) |
XButton1 | 设备的拓展按键,通常为撤销按键(鼠标) |
XButton2 | 设备的拓展按键,通常为恢复按键(鼠标) |
XButton3 | 拓展按键 |
XButton4 | 拓展按键 |
…… | …… |
结合触控按键可以方便地检测触控点在本帧触发的行为:
只需要为添加了 Collider 组件的 Entity 增加触控回调,就可以实现与渲染物体交互的能力。触控回调已经整合到引擎的脚本生命周期中,用户可以很方便地添加以下事件,同时钩子函数中会携带触发此回调的 Pointer 实例。
接口 | 触发时机与频率 |
---|---|
onPointerEnter | 当触控点进入 Entity 的碰撞体范围时触发一次 |
onPointerExit | 当触控点离开 Entity 的碰撞体范围时触发一次 |
onPointerDown | 当触控点在 Entity 的碰撞体范围内按下时触发一次 |
onPointerUp | 当触控点在 Entity 的碰撞体范围内松开时触发一次 |
onPointerClick | 当触控点在 Entity 的碰撞体范围内按下并松开,在松开时触发一次 |
onPointerDrag | 当触控点在 Entity 的碰撞体范围内按下时持续触发,直至触控点解除按下状态 |
⚠️ 触控回调依赖物理引擎,使用此功能前请确保物理引擎已初始化完毕。
如下示例:
触控回调是基于射线检测实现的,若要自定义射线检测也十分简单,只需按照如下步骤即可。
添加碰撞体组件可参考碰撞体组件,实现检测部分的代码逻辑如下:
// 假设当前有一个活动的触控点
const pointer = inputManager.pointers[0];
// 通过触控点得到由相机发射的射线
const ray = camera.screenPointToRay(pointer.position, new Ray());
// 射线与场景的碰撞体进行碰撞检测
const hitResult = new HitResult();
if (scene.physics.raycast(ray, 100, hitResult)) {
console.log("Hit entity", hitResult.entity);
}
通过下方示例可以更直观地理解此过程,示例中为主相机添加了辅助线,侧视相机可以完整观察到主相机射线检测到碰撞体的过程。
截止 2024 年 2 月,不同平台对 PointerEvent 的兼容性已经达到了 96.35% 。
设计思路可参考:https://github.com/galacean/engine/wiki/Input-system-design.
⚠️ 若遇到平台的兼容性问题,可以在 https://github.com/galacean/polyfill-pointer-event 提 issue 。
在移动端,触控会触发 HTML 元素的默认行为,一旦触发默认行为,触控就会从元素上被移除(PointerCancel),可以通过设置监听源的 touchAction
解决,若触控的监听源为默认画布:
(engine.canvas._webCanvas as HTMLCanvasElement).style.touchAction = "none";
这是由于右键触发系统默行为导致的,可以加入下列代码阻止:
document.oncontextmenu = (e) => {
e.preventDefault();
};