页面
页面是 ChaUI 配置的最外层容器。普通界面、常驻 HUD、世界空间页面和原版容器替换界面都从一份页面配置开始,页面中的图片、文本、按钮、布局等内容统一放在 elements 列表中。
如果你正在制作第一个页面,建议先阅读制作第一个界面,再把本页作为完整配置手册使用。
适用场景
- 设置页面尺寸、标题和编辑器坐标写入方式
- 决定页面以普通界面、HUD、世界页面还是原版容器替换界面打开
- 准备页面默认变量与 PlaceholderAPI 常量
- 配置页面打开、关闭事件和可复用方法
- 管理页面中的全部组件及其父子关系
根级配置
| 配置项 | 类型 | 默认值 | 可选值或格式 | 说明 |
|---|---|---|---|---|
id | 字符串 | 页面文件名 | Unicode 字母、数字、_、- | 页面唯一 ID,必须与 pages/<id>.yml 的文件名一致。 |
version | 整数 | 1 | 整数 | 页面配置版本;页面结构发生较大调整时可以递增,方便管理员识别。 |
title | 字符串 | 空 | 普通文字 | 页面标题与管理时使用的可读名称。 |
size | 映射 | 全窗口 | width、height | 页面画布尺寸,宽高支持数字或动态公式。 |
coordinateMode | 枚举 | absolute | absolute、percent | 控制编辑器拖拽后如何保存组件位置。 |
display | 映射 | screen | screen、hud、world、container | 页面打开方式及各模式专属配置。 |
vars | 映射 | 空 | 字符串、数字、布尔值 | 每次建立页面会话时使用的默认变量。 |
papi | 映射 | 空 | refreshTicks、values | 把服务端 PlaceholderAPI 结果映射为页面只读常量。 |
events | 映射 | 空 | open、close | 页面打开和关闭时执行的 ChaUI Script。 |
methods | 映射 | 空 | 方法名到脚本 | 页面内可复用的零参数自定义方法。 |
elements | 列表 | 空 | 组件定义列表 | 页面中的全部组件;组件使用 parent 组成层级。 |
页面尺寸与坐标模式
size
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
size.width | 数字或公式 | window.width | 页面画布宽度。 |
size.height | 数字或公式 | window.height | 页面画布高度。 |
使用窗口公式时,页面会跟随玩家当前 GUI 缩放和窗口尺寸变化:
size:
width: window.width # 占满当前窗口宽度
height: window.height # 占满当前窗口高度也可以使用固定画布,例如 width: 500、height: 300。页面运行时会裁剪超出画布的组件,因此画布尺寸应覆盖需要展示的区域。
coordinateMode
| 值 | 编辑器保存行为 | 适合场景 |
|---|---|---|
absolute | 拖拽后把 X、Y 保存为固定数字 | 固定尺寸面板、精确像素排版 |
percent | 拖拽后把 X、Y 保存为 window.width * 比例、window.height * 比例 | 需要适配不同窗口尺寸的位置 |
coordinateMode 只影响编辑器拖拽位置时写入的 X、Y,不会自动修改组件宽度和高度。宽高需要适配窗口时,仍应在组件的 layout.width、layout.height 中主动填写公式。
页面打开模式
display.mode 决定页面显示在哪里。页面只需要填写当前模式对应的配置;例如世界页面不写 display.screen.dimBackground。
通用模式
| 配置项 | 类型 | 默认值 | 可选值 | 说明 |
|---|---|---|---|---|
display.mode | 枚举 | screen | screen、hud、world、container | 当前页面的打开模式。 |
display.allowEscClose | 布尔值 | true | true、false | 是否允许玩家按 Esc 关闭普通界面、容器替换界面或当前 GUI 子页面。HUD 与世界页面忽略此项。 |
| 模式 | 说明 |
|---|---|
screen | 普通界面,会接收鼠标和键盘交互,可选择是否绘制背景变暗效果。 |
hud | 常驻或临时 HUD,叠加在游戏画面上,可替换指定原版 HUD 区域。 |
world | 放置在世界坐标中的空间页面,同一页面可以同时存在多个实例。 |
container | 打开匹配的生存背包或通用箱子时自动替换外观,并继续使用原版容器交互。 |
普通界面 screen
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
display.screen.dimBackground | 布尔值 | true | 是否绘制原版模糊与变暗背景;只在 mode: screen 时生效。 |
display:
mode: screen
allowEscClose: true # false 时 Esc 不会关闭当前页面
screen:
dimBackground: true # 打开界面时模糊并压暗后方游戏画面allowEscClose: false 只屏蔽玩家通过 Esc 关闭页面,不影响脚本、API、服务器强制关闭、断线或界面切换时的正常清理。Esc 仍会收起当前下拉框、取消输入焦点,并触发页面的 events.key.ESCAPE。
GUI 子页面只检查当前栈顶子页面自己的配置:栈顶禁止关闭时,Esc 不会越过它关闭父页面。容器替换页面禁止关闭时,Esc 也不会继续传给原版容器;设为 true 时则按原版流程正常关闭容器。
HUD
| 配置项 | 类型 | 默认值 | 可选值或格式 | 说明 |
|---|---|---|---|---|
display.hud.always | 布尔值 | false | 为 true 时,玩家进入服务器后自动打开该 HUD。 | |
display.hud.replaces | 字符串列表 | 空 | 原版 HUD 区域白名单 | 隐藏指定原版区域,再由当前页面绘制替代内容。 |
replaces 支持以下值:
| 值 | 对应原版区域 |
|---|---|
health | 生命值 |
food | 饥饿值 |
armor | 护甲值 |
air | 氧气值 |
experience | 经验区域 |
hotbar | 快捷栏 |
crosshair | 准星 |
mount_health | 坐骑生命值 |
jump_meter | 跳跃蓄力条 |
display:
mode: hud
hud:
always: true # 玩家进入服务器后自动打开
replaces:
- health # 隐藏原版生命值区域
- food # 隐藏原版饥饿值区域replaces 为空时,页面只作为额外 HUD 叠加显示,不会隐藏任何原版内容。
世界页面 world
| 配置项 | 类型 | 默认值 | 可选值或格式 | 说明 |
|---|---|---|---|---|
display.world.x | 数字 | 0 | 有限数字 | 默认世界 X 坐标。 |
display.world.y | 数字 | 64 | 有限数字 | 默认世界 Y 坐标。 |
display.world.z | 数字 | 0 | 有限数字 | 默认世界 Z 坐标。 |
display.world.scale | 正数 | 0.01 | 大于 0 | 页面在世界中的缩放比例。 |
display.world.yaw | 字符串或数字 | follow_player | follow_player 或有限数字 | 跟随玩家水平朝向,或固定为指定角度。 |
display:
mode: world
world:
x: 100
y: 70
z: -20
scale: 0.01
yaw: follow_player # 页面始终水平朝向玩家世界页面支持同一页面的多个实例。实际打开时如果业务传入了新的坐标或角度,会使用该实例自己的位置。
原版容器替换 container
container 页面不通过普通页面入口打开,而是在玩家打开匹配的生存背包或一至六行箱子时自动出现。箱子可按标题、行数和优先级匹配;组件再用 containerBinding 绑定原版槽位。第三方插件仍然处理原来的库存点击事件,不需要接入 ChaUI API。
请从认识自动替换开始阅读完整教程。页面字段、标题匹配、玩家背包和槽位交互分别在“替换界面”大类中说明。
默认变量 vars
vars 保存页面打开时使用的默认状态。变量只属于当前页面会话,脚本或服务端业务更新它们后,响应式文字、布局、显隐条件和按钮选中状态会自动使用最新值。
vars:
title: "任务面板" # 字符串
count: 0 # 数字
ready: false # 布尔值变量名必须以 Unicode 字母或 _ 开头,后续可以继续使用 Unicode 字母、数字和 _。例如 playerName、任务进度、_temporary 都可以使用,1stPage 不能作为变量名。
页面中使用 {vars.title} 或中文写法 {变量.title} 读取变量。完整的响应式用法见响应式变量与实时文字。
PAPI 常量 papi
papi.values 把 PlaceholderAPI 模板映射为页面只读常量。左侧是页面内使用的名称,右侧是服务端解析的模板:
papi:
refreshTicks: 20 # 默认每 20 tick 刷新;允许 5 至 1200
values:
balance: "%vault_eco_balance%" # 页面中读取 vals.papi.balance
prefix: "%luckperms_prefix%" # 页面中读取 vals.papi.prefix| 配置项 | 类型 | 默认值 | 可选值或格式 | 说明 |
|---|---|---|---|---|
papi.refreshTicks | 整数 | 20 | 5 至 1200 | 服务端刷新 PAPI 结果的间隔 tick。 |
papi.values | 映射 | 空 | 最多 64 项 | PAPI 常量名到 PlaceholderAPI 模板的映射。 |
映射名遵循与页面变量相同的命名规则。balance 会在页面中变成只读常量 {vals.papi.balance}。页面打开后会立即取得一次结果,后续只在值变化时更新;页面不再使用后会停止刷新。
PlaceholderAPI 是可选增强。服务端未安装时,ChaUI 的其他页面仍可正常使用,无法取得的占位内容会保留原表达式,便于检查配置。
页面事件 events
页面根级事件包含生命周期事件,以及普通界面中的键盘、鼠标和滚轮输入事件:
| 配置项 | 类型 | 默认值 | 触发时机 |
|---|---|---|---|
events.open | ChaUI Script | 空 | 页面作用域建立后最先执行。 |
events.close | ChaUI Script | 空 | 普通关闭、HUD/世界实例关闭或 reload 更换旧页面时执行。 |
events.key.<按键名> | ChaUI Script | 空 | 普通界面中按下指定按键时执行,例如 key.F、key.ESCAPE。 |
events.mouse.left | ChaUI Script | 空 | 鼠标左键按下时执行。 |
events.mouse.right | ChaUI Script | 空 | 鼠标右键按下时执行。 |
events.mouse.middle | ChaUI Script | 空 | 鼠标中键按下时执行。 |
events.mouse.scrollUp | ChaUI Script | 空 | 鼠标滚轮向上滚动时执行。 |
events.mouse.scrollDown | ChaUI Script | 空 | 鼠标滚轮向下滚动时执行。 |
events:
open: |-
methods.initialize()
close: |-
vars.ready = false
key.F:
log("按下了 F")
mouse.left:
log("鼠标位置:{vals.mouse.x}, {vals.mouse.y}")按键名使用大写 GLFW 风格名称。字母和数字可以直接填写,功能键可使用 F1、F2,常用特殊键包括 ESCAPE、ENTER、SPACE、LEFT_SHIFT、RIGHT_CONTROL 等。页面级鼠标事件与命中的组件点击事件会各执行一次,不会互相覆盖。编辑状态不会执行输入脚本;预览状态只执行客户端本地动作。
页面 open 执行前,当前会话的组件对象树已经准备完成,因此初始化方法可以直接读取组件和原始公式:
events:
open: |-
vars.panelX = component("panel").layout.x
vars.panelXFormula = component("panel").layout.expression("x").source
component("tip").layout.x = formula(component("panel").layout.x + 8)普通 = 保存这次计算结果,formula(...) 才会让运行时写入的布局继续响应窗口、变量和其他组件变化。
需要查询完整名称和键盘 ID 时,请查看键盘对照表。
正常打开顺序固定为:页面 open → 按 elements 顺序执行各组件的 create。reload 会先让旧页面执行 close,释放旧页面状态后,再打开新页面并重新执行 open 与全部初始组件的 create。
详细执行顺序、动态组件和编辑器预览行为见页面生命周期与自定义方法。
自定义方法 methods
methods 用于保存页面内可复用的脚本。方法没有参数和返回值,会共享当前页面的变量与组件状态。
methods:
initialize: |-
vars.ready = true
vars.title = "页面已经就绪"
refresh: |-
vars.count = vars.count + 1英文调用:
methods.refresh()中文调用:
方法.refresh()方法名必须以 Unicode 字母或 _ 开头,最长 64 个字符,每个页面最多定义 128 个方法。方法可以调用其他方法,但不能直接或间接递归。
页面组件 elements
elements 是页面中的组件列表。每个组件至少需要 id、type 和 layout:
elements:
- id: title
type: text
parent: "" # 空表示根级组件
layout:
x: 20
y: 20
width: 180
height: 20
text:
value: "标题"parent 可以指向绝对布局、相对布局、网格布局或滚动布局。页面配置仍使用扁平的 elements 列表,不把子组件嵌套写进父组件内部。
每个元素还可以使用下面这些通用显示与交互字段:
| 配置项 | 默认值 | 可选值或格式 | 说明 |
|---|---|---|---|
pointerEvents | auto | auto、block、pass | 决定组件是否成为鼠标或准星当前唯一目标。 |
dragMode | off | off、temporary、remember | 控制玩家是否能拖动组件;物品槽与拖动条不支持。 |
scale | 1 | 非负数字或公式 | 以组件中心缩放画面和命中范围,不改变 layout 保存的坐标与宽高;布局组件不使用此字段。 |
opacity | 1 | 0 至 1 的数字或公式 | 控制画面透明度,不改变命中;布局和实体组件不使用此字段。 |
pointerEvents 的三个值可以这样理解:
auto:按钮、输入框和物品槽会参与命中;其他组件配置了鼠标事件或tooltip后才参与命中,纯装饰会自然穿透。block:组件即使没有脚本,也会挡住后面的组件,适合弹窗面板和遮罩。pass:组件完全退出命中,适合永远不需要交互的装饰图和底色。
鼠标每次只会选择最前面的一个目标。z 越大越靠前;z 相同时,elements 中写在后面的组件靠前。这个唯一目标同时决定按下、松开、点击、悬浮进入/离开、按钮悬浮图片、Tooltip、输入焦点和物品槽操作,不会再分别向后寻找不同组件。不可见组件不参与命中;禁用组件不会执行自己的事件,但仍会挡住后方组件。页面根级 events.mouse.* 是全局监听,仍会正常执行。
组件的最终可见性会检查自身和完整祖先链。任一父级或更高祖先的 visible: false、visibleWhen 为假,整个后代子树都会在布局、素材解析、渲染、Tooltip 和指针命中之前跳过;父级重新可见后,满足自身条件的后代会在下一帧恢复。隐藏父级不会暂停页面生命周期、timer 或毫秒任务。
让玩家拖动组件
dragMode 有三种使用方式:
off:关闭玩家拖动,这是默认值。temporary:允许拖动,但位置只保留到当前页面会话结束。remember:允许拖动,并在玩家松开后把位置记在本地客户端;下次进入同一服务器并打开同一页面时继续使用。
拖动保存的是相对于原布局结果的偏移,不会改写页面 yml、公式或父子关系。布局组件被拖动时,其后代会跟随移动。组件会被限制在页面可视区域内;按下后移动超过 3 个 GUI 像素才算真正拖动,因此普通点击不会轻易误触。真正拖动会保留按下和松开事件,但不会再触发同一次左键点击事件。
只要客户端已有该组件的记忆位置,即使后来把 dragMode 改成 off,记忆仍会继续生效。这样管理员可以先让玩家摆好位置,再关闭后续拖动。若要回到页面原始位置,可用脚本的 resetPosition(),或由管理员重置该玩家的全部组件位置记忆。
物品槽需要保留完整的物品操作,拖动条需要保留滑块拖动,因此 item_slot 与 slider 不允许配置组件本体拖动。编辑器编辑模式仍然只修改页面设计位置;预览中的拖动是隔离的,不会读取或写入玩家正式记忆。
下面是一个不会点击穿透的弹窗结构。dialog_mask 会挡住页面后方按钮,confirm_button 位于更高层并正常接收事件:
elements:
- id: dialog_mask
type: rect
pointerEvents: block
opacity: 0.75
z: 100
layout:
x: 0
y: 0
width: window.width
height: window.height
rect:
color: "#000000"
- id: confirm_button
type: button
pointerEvents: auto
z: 101
layout:
x: window.width * 0.5 - 45
y: window.height * 0.5 - 12
width: 90
height: 24
button:
label: "确认"
events:
leftClick: close()组件字段请继续阅读图片、视频、文本、按钮、矩形、输入框、下拉选择框、点击切换选择框、拖动条、进度条、物品槽、物品展示与实体渲染。布局容器可从绝对布局开始阅读。
大型页面的性能建议
页面只应承载当前需要显示和操作的内容。多个完全不同的功能区如果长期放在同一页面,再用大量 visibleWhen 轮流隐藏,仍会增加页面维护成本,也会让客户端处理更多无用状态。
- 固定标题、公共状态、Tab 和刷新按钮放在根页面。
- 每个 Tab 的卡片、物品展示、临时槽和操作按钮放在独立子页面。
- 同级 Tab 使用
ChaUIAPI.replaceSubPage切换,不要不断叠加openSubPage。 - 纯装饰组件使用
pointerEvents: pass,避免参与不必要的鼠标命中。 - 列表优先使用固定数量的卡片视口和分页,不要一次创建无限数量组件。
- 变量和物品状态只更新当前页面实际需要的部分。
页面拆分方式和完整示例请阅读在 GUI 上打开子页面。
完整配置示例
下面的示例包含普通页面会用到的完整根级字段。HUD 或世界页面应把 display 换成各自的模式配置。
id: status_page # 页面 ID,与 pages/status_page.yml 文件名一致
version: 1 # 页面配置版本
title: 玩家状态页 # 管理和识别页面时使用的标题
size: # 页面画布尺寸
width: window.width
height: window.height
coordinateMode: absolute # 编辑器拖拽后保存固定坐标
display: # 页面打开方式
mode: screen
allowEscClose: true # 是否允许玩家按 Esc 关闭当前界面
screen:
dimBackground: true # 普通界面打开时模糊并压暗游戏画面
vars: # 当前页面会话的默认变量
title: "玩家状态"
ready: false
count: 0
papi: # 服务端 PlaceholderAPI 常量
refreshTicks: 20
values:
balance: "%vault_eco_balance%" # 页面中读取 vals.papi.balance
events: # 页面生命周期与输入事件
open: |-
methods.initialize()
close: |-
stopTimer("status_refresh")
methods: # 页面内可复用方法
initialize: |-
vars.ready = true
elements: # 页面组件列表
- id: status_title
type: text
parent: ""
visible: true
enabled: true
pointerEvents: auto
scale: 1
opacity: 1
z: 10
layout:
x: window.width * 0.5 - 100
y: 24
width: 200
height: 20
text:
value: "{vars.title} 余额:{vals.papi.balance}"
font: ""
color: "#FFFFFF"
textSize: 1
textLineLength: 0
textLineWidth: 0
revealIntervalMs: 0
align: center
tooltip: []
events:
create: vars.count = vars.count + 1 # 文本实例创建后执行一次
leftPress: log("标题已按下")
leftRelease: log("标题已松开")常见问题
页面配置中必须写出所有默认字段吗
不需要。保存页面时,空字符串、空列表和空映射会被省略;读取时会自动使用对应默认值。为了便于学习,上面的完整示例保留了各个配置分区。
为什么切换 coordinateMode 后旧组件没有自动变化
coordinateMode 是编辑器后续拖拽位置的写入方式,不会批量重写已经保存的组件公式。旧组件需要重新拖动或手动调整布局字段。
HUD 页面为什么没有隐藏原版内容
检查 display.mode 是否为 hud,并确认需要隐藏的区域已经加入 display.hud.replaces。空列表只表示叠加显示。
页面事件和组件事件有什么区别
页面使用 open、close 和页面级输入事件;组件可以使用 create、三键按下/松开/点击和悬浮事件。页面打开时先执行 open,再依次执行组件 create。
猹件开发组