跳到主要内容

输入

元素通过应用的 ElementInput 响应鼠标、触摸和 XR 控制器。它监听浏览器输入,确定指针下方的元素,并在组件上触发事件。本页介绍全部交互元素共用的事件。按钮在其上增加悬停和按下状态,滚动视图则通过这些事件实现拖动。

启用界面输入​

元素接收输入需要两个条件:应用具有 ElementInput,详见配置;元素自身启用了输入。未启用输入的元素不会被命中,因此仅对交互元素开启,装饰和按钮标签等应关闭,使标签上的点击到达下方按钮。

card.element.useInput = true;

也可以向 addComponent('element', ...) 传入 useInput: true。应用没有 ElementInput 时,调试版引擎会对启用输入的元素记录警告。

各开发方式创建的输入设备不同,这会影响界面与游戏输入的配合:

开发方式输入设备元素输入
引擎传给 AppOptions 的设备需要自行创建,且先于鼠标和触摸设备
编辑器INPUT 中启用的鼠标、触摸、键盘和手柄始终创建,先于其他设备
Reactapp.mouse 和 app.touch,没有键盘设备始终创建,先于其他设备
Web Componentsapp.mouse 和 app.keyboard,没有触摸设备,但元素输入自行处理触摸始终创建,先于其他设备

输入事件​

以下事件在元素组件上触发:

事件触发时机
mouseenter指针进入元素
mouseleave指针离开元素
mousemove指针在元素上移动;若已在元素上按下,则直到释放前的全部移动都交给它
mousedown在元素上按下鼠标键
mouseup在元素上释放鼠标键,或在元素上按下后于任意位置释放
mousewheel指针位于元素上时滚动鼠标滚轮
click鼠标或触摸在同一元素上按下并释放
touchstart触摸在元素上开始
touchmove从元素开始的触摸发生移动,无论移到何处
touchleave从元素开始的触摸移出,每次触摸只触发一次
touchend从元素开始的触摸结束,无论结束位置
touchcancel浏览器取消从元素开始的触摸
selectstart, selectend, selectmove, selectenter, selectleaveXR 控制器或手指向并选择元素,详见 XR 界面

监听事件​

监听元素组件上的事件,不同开发方式的监听代码位置如下:

card.element.on('mouseenter', () => {
card.element.opacity = 1;
});
card.element.on('mouseleave', () => {
card.element.opacity = 0.6;
});

on 返回 EventHandle,调用其 off() 停止监听。

每个处理函数接收事件对象。event.element 始终是最初触发事件的元素,即使当前处理函数属于其祖先;event.event 是原始浏览器事件:

事件对象适用事件属性
ElementMouseEventmouse* 及鼠标 clickx、y 为相对画布左上角的 CSS 像素位置;dx、dy 为上次事件后的移动量;button;wheelDelta 为 -1、0 或 1;以及 ctrlKey、altKey、shiftKey、metaKey
ElementTouchEventtouch* 及触摸 click本次触摸的 x、y;浏览器触摸对象 touch;以及与浏览器事件相同的 touches、changedTouches
ElementSelectEventselect* 及 XR 选择产生的 clickinputSource,即控制器或手

三者还都包含 element、camera(通过哪个摄像机命中元素)及 event。

事件冒泡​

事件先在被命中元素上触发,再沿父元素向上,直到遇到没有 Element 的实体。祖先无论是否启用输入都会收到,因此菜单上一个监听器就能处理所有子项点击:

menu.element.on('click', (event) => {
console.log(`${event.element.entity.name} was clicked`);
});

调用 event.stopPropagation() 可阻止继续向上传播。

防止界面输入传到游戏​

直接读取鼠标或触摸设备的游戏代码,例如点击射击,也会看到落在界面上的点击。stopPropagation() 不仅停止冒泡,还阻止底层浏览器事件继续执行后续监听器。鼠标和触摸设备监听同一浏览器事件,因此在它们之前创建 ElementInput,即可阻止 HUD 点击进入 app.mouse,包括 app.mouse.wasPressed():

// Presses on any input-enabled element in the HUD never reach app.mouse or app.touch
hud.element.on('mousedown', event => event.stopPropagation());
hud.element.on('touchstart', event => event.stopPropagation());

HUD 自身的组元素应保持输入关闭,子项事件仍会冒泡到它。若开启输入,整个组矩形都会阻止点击进入游戏。

编辑器、React 和 Web Components 已先创建 ElementInput。直接使用引擎时,也应按配置先创建它,再创建鼠标与触摸设备。

输入事件

哪个元素接收事件​

元素重叠时,只有一个接收事件。ElementInput 按以下顺序检测,首次命中即停止:

  1. 从上到下检查摄像机。 从最后绘制到最先绘制,确保上层界面优先。只有渲染元素所属图层的摄像机才参与检测。
  2. 从上到下检查图层。 后绘制的图层优先,仅在界面使用多图层时有影响。
  3. 先屏幕空间元素,再世界空间屏幕元素,最后没有屏幕的元素。
  4. 先检查最上方元素。 各组内 drawOrder 最大者优先,通常是层级中更靠后的元素,详见绘制顺序与性能。

屏幕上的元素一旦命中,即使比其他命中点更远,也会结束搜索。世界空间屏幕重叠时,优先级较高者优先。只有没有屏幕的元素才按距离比较,以最近者为准。

检测区域是元素矩形,而非图像可见像素或字形形状。命中扩展可扩大它,遮罩可裁切它。禁用实体不参与检测。

点击与拖动​

鼠标释放或触摸结束时,若仍处于最初按下的同一元素上,则触发 click。释放前移出再移回仍算点击。

在元素上按下后,无论指针移到何处,该元素都会接收全部 mousemove 和最终 mouseup。触摸同理,touchmove 和 touchend 始终交给起始元素,因此指针离开滑块或滚动视图后仍能继续拖动。

触摸屏​

触摸点击后,浏览器会模拟鼠标事件以兼容只处理鼠标的页面。ElementInput 忽略同一元素上的重复模拟 click,但其他事件仍会到达,包括 mouseenter、mousedown 和 mouseup。若点击后隐藏元素,例如按钮关闭对话框,模拟点击就会落到后方对象。取消画布的 touchend 默认行为可阻止模拟,适用于所有开发方式:

app.graphicsDevice.canvas.addEventListener('touchend', (event) => {
event.preventDefault();
});

ElementInput 还会取消画布上的所有 touchmove,因此从画布开始的触摸不会滚动页面。画布嵌在长网页中时需要考虑这一点。

指针锁定​

第一人称游戏等锁定指针时,ElementInput 会忽略鼠标按下。显示菜单前调用 app.mouse.disablePointerLock(),关闭后再锁定。

禁用界面输入​

  • 关闭 useInput 可停止单个元素接收输入,禁用实体也不接收输入。
  • 设置按钮 active 为 false 可保留显示但不响应,详见按钮。
  • 设置 app.elementInput.enabled 为 false 可暂停全部界面输入,例如等待菜单退出动画。此属性尚未收录于 API 参考。

另请参阅​