跳转到内容

UI 交互

Interactable

将 UI 实体标记为可交互(接收指针事件)。所有交互控件都需要此组件。

属性类型默认值说明
enabledbooleantrue是否启用交互
blockRaycastbooleantrue阻止事件传递到后面的元素
raycastTargetbooleantrue是否参与射线检测

UIInteraction

由插件自动添加到拥有 Interactable 的实体上,提供逐实体状态:

属性类型说明
hoveredboolean指针悬停在此实体上
pressedboolean实体正被按下
justPressedboolean本帧开始按下
justReleasedboolean本帧释放按下

UIEvents

UIEvents 资源收集每帧的交互事件:

import { defineSystem, addSystem, Res } from 'esengine';
import { UIEvents } from 'esengine';
addSystem(defineSystem(
[Res(UIEvents)],
(events) => {
for (const e of events.query('click')) {
console.log('点击了:', e.entity);
}
}
));

事件类型

类型说明冒泡触发者
click指针在同一实体上按下后释放UIInteraction
press指针按钮按下UIInteraction
release指针按钮释放UIInteraction
hover_enter指针进入实体边界UIInteraction
hover_exit指针离开实体边界UIInteraction
focus元素获得键盘焦点(点击或 Tab)Focusable
blur元素失去键盘焦点Focusable
submit单行 TextInput 中按下 EnterTextInput
changeSlider、Toggle、TextInput 或 Dropdown 的值改变各控件
drag_start指针移动超过拖拽阈值Draggable
drag_move拖拽过程中指针移动Draggable
drag_end拖拽后释放指针Draggable
scrollScrollView 内容滚动ScrollView

UIEvent 结构

每个事件对象包含:

属性类型说明
entityEntity接收事件的实体
typestring事件类型(见上表)
targetEntity触发事件的原始实体
currentTargetEntity当前正在处理事件的实体(冒泡时可能不同于 target)

查询事件

// 查询特定类型的所有事件
for (const e of events.query('click')) {
console.log('点击了:', e.entity);
}
// 检查特定实体是否有特定事件
if (events.hasEvent(buttonEntity, 'click')) {
handleButton();
}
// 消费所有事件(返回并清空队列)
const all = events.drain();

回调订阅

除了每帧轮询事件,还可以使用 .on() 通过回调方式订阅事件。适合在启动系统中进行一次性设置:

import { defineSystem, addStartupSystem, Res, GetWorld } from 'esengine';
import { UIEvents, makeInteractable } from 'esengine';
addStartupSystem(defineSystem(
[Res(UIEvents), GetWorld()],
(events, world) => {
const button = findEntityByName(world, 'StartButton');
// 实体级:仅当该实体触发事件时回调
const unsub = events.on(button, 'click', (e) => {
console.log('开始按钮被点击!');
});
// 全局:任何实体触发该类型事件时回调
events.on('change', (e) => {
console.log('值变更:', e.entity);
});
// 调用 unsub() 移除监听器
}
));
重载形式说明
events.on(entity, type, handler)订阅特定实体的事件,返回 Unsubscribe 函数
events.on(type, handler)全局订阅某类型的所有事件,返回 Unsubscribe 函数

makeInteractable

确保实体拥有 Interactable 组件的工具函数,使用合理的默认值:

import { makeInteractable } from 'esengine';
makeInteractable(world, entity);
// 等价于添加 Interactable { enabled: true, blockRaycast: true, raycastTarget: true }

在代码中创建交互实体时使用此函数,避免手动导入和插入 Interactable 组件。

事件冒泡

pressreleaseclick 事件沿实体层级向上冒泡。如果父级的 Interactable 设置了 blockRaycast: true,冒泡在此停止。hover_enterhover_exit 不会冒泡。

Focusable

启用键盘焦点和 Tab 导航。

属性类型默认值说明
tabIndexnumber0焦点顺序(越小越早)
isFocusedbooleanfalse当前焦点状态

Draggable

启用 UI 元素的拖拽移动。

属性类型默认值说明
enabledbooleantrue启用拖拽
dragThresholdnumber5开始拖拽前的像素移动距离
lockXbooleanfalse锁定水平移动
lockYbooleanfalse锁定垂直移动
constraintMin{x, y} | nullnull拖拽范围最小值(世界坐标)
constraintMax{x, y} | nullnull拖拽范围最大值(世界坐标)

Draggable 同时支持基于 Transform 和基于 UIRect(锚点布局)的元素。当实体拥有 UIRect 时,拖拽会调整布局偏移量;否则直接移动 Transform 位置。

拖拽激活时,引擎会自动为实体添加 DragState 组件:

属性类型说明
isDraggingboolean正在拖拽
startWorldPos{x, y}拖拽开始时的实体位置
currentWorldPos{x, y}当前世界位置
deltaWorld{x, y}本帧移动量
totalDeltaWorld{x, y}从拖拽开始的总移动量
pointerStartWorld{x, y}拖拽开始时的指针世界位置

默认着色

ButtonToggle 没有显式 transition 时,UI 系统会自动为实体的 SpriteImage 颜色应用基于状态的着色:

状态效果
正常无变化
悬停1.15 倍亮度
按下0.75 倍亮度
禁用0.5 倍亮度,0.6 透明度

无需显式定义 ColorTransition 即可获得视觉反馈。

处理管线

UI 交互系统每帧按固定顺序执行:

调度阶段系统作用
Last(上一帧)InputPlugin清除单帧输入状态
PreUpdateUIInteractionSystem命中测试、悬停、按下、释放、点击
PreUpdateDragPlugin拖拽状态追踪、拖拽事件
UpdateButtonSystem、TogglePlugin、SliderPlugin 等控件状态更新

下一步