UI 控件
所有控件都需要在同一实体上添加 Interactable 和 UIRect。交互事件处理详见 UI 交互。
获取特定控件
当你在场景中放置了多个控件后,使用 Query 来查找特定的那一个。
按标签查找(推荐)
定义一个 Tag 并在编辑器中挂到实体上,然后用 query.single() 直接获取唯一匹配的实体:
import { defineTag, defineSystem, addSystem, Query, Mut, ProgressBar } from 'esengine';
const HealthBar = defineTag('HealthBar');
addSystem(defineSystem( [Query(Mut(ProgressBar), HealthBar)], (query) => { const [, bar] = query.single()!; bar.value = playerHealth / maxHealth; }));按名称查找
编辑器中每个实体都有 Name 组件。将它和控件组件一起查询:
addSystem(defineSystem( [Query(Mut(ProgressBar), Name)], (query) => { for (const [, bar, name] of query) { if (name.value === 'HealthBar') bar.value = playerHealth / maxHealth; if (name.value === 'ManaBar') bar.value = playerMana / maxMana; if (name.value === 'ExpBar') bar.value = playerExp / expToNextLevel; } }));用 findEntityByName + 直接访问
使用 GetWorld() 获取 World 实例。findEntityByName() 按编辑器中的名称查找实体,然后 world.get() / world.set() 以 O(1) 读写组件:
import { GetWorld, findEntityByName, ProgressBar } from 'esengine';
addSystem(defineSystem( [GetWorld()], (world) => { const entity = findEntityByName(world, 'HealthBar'); if (entity) { const bar = world.get(entity, ProgressBar); bar.value = playerHealth / maxHealth; world.set(entity, ProgressBar, bar); } }));处理特定控件的事件
Tag + hasEvent
先用 query.single() 获取实体 ID,然后用 UIEvents.hasEvent() 直接检查——无需循环:
import { defineTag, defineSystem, addSystem, Res, Query } from 'esengine';import { UIEvents, Button } from 'esengine';
const StartButton = defineTag('StartButton');
addSystem(defineSystem( [Res(UIEvents), Query(Button, StartButton)], (events, query) => { const match = query.single(); if (match && events.hasEvent(match[0], 'click')) { startGame(); } }));使用 UIInteraction(无需 UIEvents)
对于简单的按下/释放检测,直接读取 UIInteraction 组件:
import { UIInteraction } from 'esengine';
const StartButton = defineTag('StartButton');
addSystem(defineSystem( [Query(UIInteraction, StartButton)], (query) => { const match = query.single(); if (match) { const [, interaction] = match; if (interaction.justPressed) startGame(); } }));按名称匹配事件
当需要在一个系统中处理多个控件的事件时:
addSystem(defineSystem( [Res(UIEvents), Query(Button, Name)], (events, query) => { for (const e of events.query('click')) { for (const [entity, , name] of query) { if (entity === e.entity) { console.log('点击了:', name.value); } } } }));Button
在 Interactable 之上的状态机,支持可选颜色过渡。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
state | ButtonState | Normal | Normal (0)、Hovered (1)、Pressed (2)、Disabled (3) |
transition | ColorTransition | null | null | 各状态颜色 |
cmds.entity(button).insert(Button, { state: 0, transition: { normalColor: { r: 1, g: 1, b: 1, a: 1 }, hoveredColor: { r: 0.9, g: 0.9, b: 0.9, a: 1 }, pressedColor: { r: 0.7, g: 0.7, b: 0.7, a: 1 }, disabledColor: { r: 0.5, g: 0.5, b: 0.5, a: 0.5 }, },});Toggle
复选框,可选分组实现互斥。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
isOn | boolean | true | 开关状态 |
graphicEntity | Entity | 0 | 显示/隐藏的子实体(勾选标记) |
group | Entity | 0 | ToggleGroup 实体,实现单选按钮行为 |
transition | ColorTransition | null | null | 颜色过渡 |
onColor | Color | {r:0.2, g:0.6, b:1, a:1} | 开启时的颜色(无 transition 时使用) |
offColor | Color | {r:0.4, g:0.4, b:0.4, a:1} | 关闭时的颜色(无 transition 时使用) |
ToggleGroup — 挂载到父实体使子级 Toggle 互斥:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
allowSwitchOff | boolean | false | 允许所有 Toggle 关闭 |
监听开关变化:
addSystem(defineSystem( [Res(UIEvents), Query(Toggle, Name)], (events, query) => { for (const e of events.query('change')) { for (const [entity, toggle, name] of query) { if (entity === e.entity) { console.log(name.value, '现在是:', toggle.isOn); } } } }));Slider
交互式数值滑块,带填充和可选可拖拽手柄。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | number | 0 | 当前值 |
minValue | number | 0 | 最小值 |
maxValue | number | 1 | 最大值 |
direction | FillDirection | LeftToRight | LeftToRight (0)、RightToLeft (1)、BottomToTop (2)、TopToBottom (3) |
fillEntity | Entity | 0 | 填充可视化的子实体 |
handleEntity | Entity | 0 | 可拖拽手柄的子实体 |
wholeNumbers | boolean | false | 吸附到整数 |
监听滑块变化:
addSystem(defineSystem( [Res(UIEvents), Query(Slider, Name)], (events, query) => { for (const e of events.query('change')) { for (const [entity, slider, name] of query) { if (entity === e.entity) { if (name.value === 'VolumeSlider') setVolume(slider.value); if (name.value === 'BrightnessSlider') setBrightness(slider.value); } } } }));ProgressBar
非交互式进度条显示。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | number | 0 | 进度 0–1 |
fillEntity | Entity | 0 | 填充的子实体 |
direction | FillDirection | LeftToRight | 填充方向 |
Dropdown
下拉列表选择器。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
options | string[] | [] | 选项标签 |
selectedIndex | number | -1 | 选中项索引 |
isOpen | boolean | false | 列表可见性 |
listEntity | Entity | 0 | 下拉列表的子实体 |
labelEntity | Entity | 0 | 显示选中标签的子实体 |
ScrollView
带惯性和弹性回弹的滚动容器。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
contentEntity | Entity | 0 | 持有可滚动内容的子实体 |
horizontalEnabled | boolean | false | 启用水平滚动 |
verticalEnabled | boolean | true | 启用垂直滚动 |
scrollX | number | 0 | 当前水平偏移 |
scrollY | number | 0 | 当前垂直偏移 |
inertia | boolean | true | 启用惯性滚动 |
decelerationRate | number | 0.135 | 惯性摩擦力 |
elastic | boolean | true | 边缘回弹 |
wheelSensitivity | number | 0.1 | 鼠标滚轮灵敏度 |
contentWidth | number | 0 | 可滚动内容总宽度(由插件自动计算) |
contentHeight | number | 0 | 可滚动内容总高度(由插件自动计算) |
ListView
大列表虚拟滚动——仅渲染可见项目。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
itemHeight | number | 40 | 固定项目高度 |
itemCount | number | 0 | 总项目数 |
scrollY | number | 0 | 滚动位置 |
overscan | number | 2 | 视口上下额外渲染的项目数 |
自定义项目渲染器
使用 setListViewRenderer() 定义每个列表项的填充方式。回调接收项目索引和要配置的实体:
import { setListViewRenderer, Text, Sprite } from 'esengine';
setListViewRenderer(listEntity, (index, itemEntity) => { world.set(itemEntity, Text, { content: `Item ${index}`, fontSize: 16 });});| 参数 | 类型 | 说明 |
|---|---|---|
index | number | 列表项的 0 基索引 |
entity | Entity | 该列表项的实体——用组件填充它 |
TextInput
可编辑文本输入框,支持光标、选择和占位符。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | '' | 当前文本 |
placeholder | string | '' | 为空时的提示 |
placeholderColor | Color | {r:0.6, g:0.6, b:0.6, a:1} | 占位符颜色 |
fontFamily | string | 'Arial' | 字体 |
fontSize | number | 16 | 字号 |
color | Color | {r:1, g:1, b:1, a:1} | 文本颜色 |
backgroundColor | Color | {r:0.15, g:0.15, b:0.15, a:1} | 背景颜色 |
padding | number | 6 | 内边距 |
maxLength | number | 0 | 最大字符数(0 = 无限) |
multiline | boolean | false | 允许换行 |
password | boolean | false | 密码遮罩 |
readOnly | boolean | false | 禁止编辑 |
focused | boolean | false | 当前是否获得焦点 |
cursorPos | number | 0 | 文本中的当前光标位置 |
dirty | boolean | true | 是否需要重新渲染 |
事件: change(值变化)、submit(单行模式下按 Enter)