Skip to content
On this page

页面

页面是 ChaUI 配置的最外层容器。普通界面、常驻 HUD、世界空间页面和原版容器替换界面都从一份页面配置开始,页面中的图片、文本、按钮、布局等内容统一放在 elements 列表中。

如果你正在制作第一个页面,建议先阅读制作第一个界面,再把本页作为完整配置手册使用。

适用场景

  • 设置页面尺寸、标题和编辑器坐标写入方式
  • 决定页面以普通界面、HUD、世界页面还是原版容器替换界面打开
  • 准备页面默认变量与 PlaceholderAPI 常量
  • 配置页面打开、关闭事件和可复用方法
  • 管理页面中的全部组件及其父子关系

根级配置

配置项类型默认值可选值或格式说明
id字符串页面文件名Unicode 字母、数字、_-页面唯一 ID,必须与 pages/<id>.yml 的文件名一致。
version整数1整数页面配置版本;页面结构发生较大调整时可以递增,方便管理员识别。
title字符串普通文字页面标题与管理时使用的可读名称。
size映射全窗口widthheight页面画布尺寸,宽高支持数字或动态公式。
coordinateMode枚举absoluteabsolutepercent控制编辑器拖拽后如何保存组件位置。
display映射screenscreenhudworldcontainer页面打开方式及各模式专属配置。
vars映射字符串、数字、布尔值每次建立页面会话时使用的默认变量。
papi映射refreshTicksvalues把服务端 PlaceholderAPI 结果映射为页面只读常量。
events映射openclose页面打开和关闭时执行的 ChaUI Script。
methods映射方法名到脚本页面内可复用的零参数自定义方法。
elements列表组件定义列表页面中的全部组件;组件使用 parent 组成层级。

页面尺寸与坐标模式

size

配置项类型默认值说明
size.width数字或公式window.width页面画布宽度。
size.height数字或公式window.height页面画布高度。

使用窗口公式时,页面会跟随玩家当前 GUI 缩放和窗口尺寸变化:

yaml
size:
  width: window.width       # 占满当前窗口宽度
  height: window.height     # 占满当前窗口高度

也可以使用固定画布,例如 width: 500height: 300。页面运行时会裁剪超出画布的组件,因此画布尺寸应覆盖需要展示的区域。

coordinateMode

编辑器保存行为适合场景
absolute拖拽后把 X、Y 保存为固定数字固定尺寸面板、精确像素排版
percent拖拽后把 X、Y 保存为 window.width * 比例window.height * 比例需要适配不同窗口尺寸的位置

coordinateMode 只影响编辑器拖拽位置时写入的 X、Y,不会自动修改组件宽度和高度。宽高需要适配窗口时,仍应在组件的 layout.widthlayout.height 中主动填写公式。

页面打开模式

display.mode 决定页面显示在哪里。页面只需要填写当前模式对应的配置;例如世界页面不写 display.screen.dimBackground

通用模式

配置项类型默认值可选值说明
display.mode枚举screenscreenhudworldcontainer当前页面的打开模式。
display.allowEscClose布尔值truetruefalse是否允许玩家按 Esc 关闭普通界面、容器替换界面或当前 GUI 子页面。HUD 与世界页面忽略此项。
模式说明
screen普通界面,会接收鼠标和键盘交互,可选择是否绘制背景变暗效果。
hud常驻或临时 HUD,叠加在游戏画面上,可替换指定原版 HUD 区域。
world放置在世界坐标中的空间页面,同一页面可以同时存在多个实例。
container打开匹配的生存背包或通用箱子时自动替换外观,并继续使用原版容器交互。

普通界面 screen

配置项类型默认值说明
display.screen.dimBackground布尔值true是否绘制原版模糊与变暗背景;只在 mode: screen 时生效。
yaml
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布尔值falsetrue 时,玩家进入服务器后自动打开该 HUD。
display.hud.replaces字符串列表原版 HUD 区域白名单隐藏指定原版区域,再由当前页面绘制替代内容。

replaces 支持以下值:

对应原版区域
health生命值
food饥饿值
armor护甲值
air氧气值
experience经验区域
hotbar快捷栏
crosshair准星
mount_health坐骑生命值
jump_meter跳跃蓄力条
yaml
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_playerfollow_player 或有限数字跟随玩家水平朝向,或固定为指定角度。
yaml
display:
  mode: world
  world:
    x: 100
    y: 70
    z: -20
    scale: 0.01
    yaw: follow_player      # 页面始终水平朝向玩家

世界页面支持同一页面的多个实例。实际打开时如果业务传入了新的坐标或角度,会使用该实例自己的位置。

原版容器替换 container

container 页面不通过普通页面入口打开,而是在玩家打开匹配的生存背包或一至六行箱子时自动出现。箱子可按标题、行数和优先级匹配;组件再用 containerBinding 绑定原版槽位。第三方插件仍然处理原来的库存点击事件,不需要接入 ChaUI API。

请从认识自动替换开始阅读完整教程。页面字段、标题匹配、玩家背包和槽位交互分别在“替换界面”大类中说明。

默认变量 vars

vars 保存页面打开时使用的默认状态。变量只属于当前页面会话,脚本或服务端业务更新它们后,响应式文字、布局、显隐条件和按钮选中状态会自动使用最新值。

yaml
vars:
  title: "任务面板"        # 字符串
  count: 0                 # 数字
  ready: false             # 布尔值

变量名必须以 Unicode 字母或 _ 开头,后续可以继续使用 Unicode 字母、数字和 _。例如 playerName任务进度_temporary 都可以使用,1stPage 不能作为变量名。

页面中使用 {vars.title} 或中文写法 {变量.title} 读取变量。完整的响应式用法见响应式变量与实时文字

PAPI 常量 papi

papi.values 把 PlaceholderAPI 模板映射为页面只读常量。左侧是页面内使用的名称,右侧是服务端解析的模板:

yaml
papi:
  refreshTicks: 20         # 默认每 20 tick 刷新;允许 5 至 1200
  values:
    balance: "%vault_eco_balance%" # 页面中读取 vals.papi.balance
    prefix: "%luckperms_prefix%"   # 页面中读取 vals.papi.prefix
配置项类型默认值可选值或格式说明
papi.refreshTicks整数2051200服务端刷新 PAPI 结果的间隔 tick。
papi.values映射最多 64 项PAPI 常量名到 PlaceholderAPI 模板的映射。

映射名遵循与页面变量相同的命名规则。balance 会在页面中变成只读常量 {vals.papi.balance}。页面打开后会立即取得一次结果,后续只在值变化时更新;页面不再使用后会停止刷新。

PlaceholderAPI 是可选增强。服务端未安装时,ChaUI 的其他页面仍可正常使用,无法取得的占位内容会保留原表达式,便于检查配置。

页面事件 events

页面根级事件包含生命周期事件,以及普通界面中的键盘、鼠标和滚轮输入事件:

配置项类型默认值触发时机
events.openChaUI Script页面作用域建立后最先执行。
events.closeChaUI Script普通关闭、HUD/世界实例关闭或 reload 更换旧页面时执行。
events.key.<按键名>ChaUI Script普通界面中按下指定按键时执行,例如 key.Fkey.ESCAPE
events.mouse.leftChaUI Script鼠标左键按下时执行。
events.mouse.rightChaUI Script鼠标右键按下时执行。
events.mouse.middleChaUI Script鼠标中键按下时执行。
events.mouse.scrollUpChaUI Script鼠标滚轮向上滚动时执行。
events.mouse.scrollDownChaUI Script鼠标滚轮向下滚动时执行。
yaml
events:
  open: |-
    methods.initialize()
  close: |-
    vars.ready = false
  key.F:
    log("按下了 F")
  mouse.left:
    log("鼠标位置:{vals.mouse.x}, {vals.mouse.y}")

按键名使用大写 GLFW 风格名称。字母和数字可以直接填写,功能键可使用 F1F2,常用特殊键包括 ESCAPEENTERSPACELEFT_SHIFTRIGHT_CONTROL 等。页面级鼠标事件与命中的组件点击事件会各执行一次,不会互相覆盖。编辑状态不会执行输入脚本;预览状态只执行客户端本地动作。

页面 open 执行前,当前会话的组件对象树已经准备完成,因此初始化方法可以直接读取组件和原始公式:

yaml
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 用于保存页面内可复用的脚本。方法没有参数和返回值,会共享当前页面的变量与组件状态。

yaml
methods:
  initialize: |-
    vars.ready = true
    vars.title = "页面已经就绪"
  refresh: |-
    vars.count = vars.count + 1

英文调用:

text
methods.refresh()

中文调用:

text
方法.refresh()

方法名必须以 Unicode 字母或 _ 开头,最长 64 个字符,每个页面最多定义 128 个方法。方法可以调用其他方法,但不能直接或间接递归。

页面组件 elements

elements 是页面中的组件列表。每个组件至少需要 idtypelayout

yaml
elements:
  - id: title
    type: text
    parent: ""              # 空表示根级组件
    layout:
      x: 20
      y: 20
      width: 180
      height: 20
    text:
      value: "标题"

parent 可以指向绝对布局、相对布局、网格布局或滚动布局。页面配置仍使用扁平的 elements 列表,不把子组件嵌套写进父组件内部。

每个元素还可以使用下面这些通用显示与交互字段:

配置项默认值可选值或格式说明
pointerEventsautoautoblockpass决定组件是否成为鼠标或准星当前唯一目标。
dragModeoffofftemporaryremember控制玩家是否能拖动组件;物品槽与拖动条不支持。
scale1非负数字或公式以组件中心缩放画面和命中范围,不改变 layout 保存的坐标与宽高;布局组件不使用此字段。
opacity101 的数字或公式控制画面透明度,不改变命中;布局和实体组件不使用此字段。

pointerEvents 的三个值可以这样理解:

  • auto:按钮、输入框和物品槽会参与命中;其他组件配置了鼠标事件或 tooltip 后才参与命中,纯装饰会自然穿透。
  • block:组件即使没有脚本,也会挡住后面的组件,适合弹窗面板和遮罩。
  • pass:组件完全退出命中,适合永远不需要交互的装饰图和底色。

鼠标每次只会选择最前面的一个目标。z 越大越靠前;z 相同时,elements 中写在后面的组件靠前。这个唯一目标同时决定按下、松开、点击、悬浮进入/离开、按钮悬浮图片、Tooltip、输入焦点和物品槽操作,不会再分别向后寻找不同组件。不可见组件不参与命中;禁用组件不会执行自己的事件,但仍会挡住后方组件。页面根级 events.mouse.* 是全局监听,仍会正常执行。

组件的最终可见性会检查自身和完整祖先链。任一父级或更高祖先的 visible: falsevisibleWhen 为假,整个后代子树都会在布局、素材解析、渲染、Tooltip 和指针命中之前跳过;父级重新可见后,满足自身条件的后代会在下一帧恢复。隐藏父级不会暂停页面生命周期、timer 或毫秒任务。

让玩家拖动组件

dragMode 有三种使用方式:

  • off:关闭玩家拖动,这是默认值。
  • temporary:允许拖动,但位置只保留到当前页面会话结束。
  • remember:允许拖动,并在玩家松开后把位置记在本地客户端;下次进入同一服务器并打开同一页面时继续使用。

拖动保存的是相对于原布局结果的偏移,不会改写页面 yml、公式或父子关系。布局组件被拖动时,其后代会跟随移动。组件会被限制在页面可视区域内;按下后移动超过 3 个 GUI 像素才算真正拖动,因此普通点击不会轻易误触。真正拖动会保留按下和松开事件,但不会再触发同一次左键点击事件。

只要客户端已有该组件的记忆位置,即使后来把 dragMode 改成 off,记忆仍会继续生效。这样管理员可以先让玩家摆好位置,再关闭后续拖动。若要回到页面原始位置,可用脚本的 resetPosition(),或由管理员重置该玩家的全部组件位置记忆。

物品槽需要保留完整的物品操作,拖动条需要保留滑块拖动,因此 item_slotslider 不允许配置组件本体拖动。编辑器编辑模式仍然只修改页面设计位置;预览中的拖动是隔离的,不会读取或写入玩家正式记忆。

下面是一个不会点击穿透的弹窗结构。dialog_mask 会挡住页面后方按钮,confirm_button 位于更高层并正常接收事件:

yaml
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 换成各自的模式配置。

yaml
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。空列表只表示叠加显示。

页面事件和组件事件有什么区别

页面使用 openclose 和页面级输入事件;组件可以使用 create、三键按下/松开/点击和悬浮事件。页面打开时先执行 open,再依次执行组件 create