跳到主要内容

GUI自定义组件

自定义组件用于把一组 GUI 部件封装成可复用结构。它适合商品条目、任务卡片、技能按钮、排行榜行、地图卡片这类重复界面。

自定义组件本身是一个真实 GUI 部件:在普通 GUI 位置写 type: component 后,会创建一个 component 容器,并在它内部加载组件定义里的 root。组件内部仍然可以放 canvastexturelabelbuttonslotscroll 等普通部件。


适合场景

  • 纯配置 GUI:重复结构写成组件,每个实例只传不同 props
  • 脚本前后端分离 GUI:YAML 负责界面结构,clickScriptclickDos 或服务端接口负责业务逻辑。
  • 商品栏、任务列表、技能网格:多个同类组件放进带 layoutcanvasscroll,由父容器负责排布。

不推荐把自定义组件作为传统命令式 GUI 的主要开发方式。比如大量使用 GermGuiScreen.getGuiPart("xxx").setLocationX("100") 逐个移动部件时,直接操作普通部件会更直观。组件内部部件仍然可以通过 商品_1$购买按钮 这类路径访问,但整体设计更适合“配置描述界面,脚本处理交互”的写法。


放置位置

公共组件库可以写在 plugins/GermPlugin/gui/ 目录下任意 yml 文件中,只要文件里有顶级 components 节点就会被加载。

components:
简单标题:
props:
text: "标题"
root:
type: canvas
width: "%fontWidth_${props.text}%"
height: "12"
relativeParts:
文本:
type: label
texts:
- "${props.text}"

一个 yml 可以只写 components,也可以同时写 components 和普通 GUI。components 下可以定义多个组件,不是一个文件只能放一个组件。

某个 GUI 自己使用的小组件可以写在当前 GUI 的 options.components 下。它只对当前 GUI 生效,同名时优先于公共组件。

商店界面:
options:
components:
私有标题:
props:
text: "商店"
root:
type: canvas
width: "%fontWidth_${props.text}%"
height: "12"
relativeParts:
文本:
type: label
texts:
- "&e${props.text}"

配置项

组件定义字段:

配置项位置必填说明
componentsyml 顶级或某个 GUI 的 options组件库入口。顶级为公共组件库,options.components 为当前 GUI 私有组件库。
props某个组件定义下组件参数默认值。引用组件时可以覆盖。
root某个组件定义下组件内部根容器配置。当前按 canvas 容器加载,实际内容写到 relativeParts

组件引用字段:

配置项位置必填说明
type普通部件位置固定写 component
component组件实例节点要引用的组件名。
props组件实例节点传给组件的参数,会覆盖组件定义里的默认 props
普通部件字段组件实例节点locationXlocationYwidthheightenablerelativeParts 等,会覆盖或追加到组件 root 上。

模板变量只使用 ${props.xxx}

texts:
- "&f${props.name}"
clickDos:
- "opcmd<->shop buy %player_name% ${props.id}"

${props.xxx} 是组件模板变量,%player_name%%calculate_100+20%%thisGui_part_width% 这类萌芽原有全局变量照常保留。


商品条目组件

商品条目通常是一组部件:背景、槽位、图标、名称、价格、购买按钮。推荐把它封装成一个 canvas 根组件。

components:
商品条目:
props:
id: "unknown"
identity: "shop_slot_unknown"
icon: "local<->textures/shop/default.png"
name: "商品"
price: "0"
root:
type: canvas
width: "180"
height: "64"
relativeParts:
背景:
type: texture
path: "local<->textures/gui/shop/item_bg.png"
width: "w"
height: "h"
槽位:
type: slot
identity: "${props.identity}"
sync: false
interact: false
invalid: true
locationX: "8"
locationY: "14"
size: "36"
图标:
type: texture
path: "${props.icon}"
locationX: "10"
locationY: "16"
width: "32"
height: "32"
名称:
type: label
locationX: "52"
locationY: "12"
texts:
- "&f${props.name}"
价格:
type: label
locationX: "52"
locationY: "34"
texts:
- "&e${props.price}金币"
购买按钮:
type: button
locationX: "132"
locationY: "18"
width: "40"
height: "26"
defaultPath: "local<->textures/gui/shop/buy.png"
hoverPath: "local<->textures/gui/shop/buy_hover.png"
clickDos:
- "opcmd<->shop buy %player_name% ${props.id}"
clickScript: |
var itemId = '${props.id}';
Log.chat('准备购买商品: ' + itemId);

组件中的 ${props.id} 会替换成当前实例传入的商品 id。脚本和 DOS 都可以使用这些变量。


配合 layout 使用

当前推荐的批量界面写法,是把多个 type: component 实例放进带 layout 的父容器,而不是在组件自身写 countcolumnsitems

商店界面:
options:
startX: "w*0.5-230"
startY: "h*0.5-140"

商品滚动框:
type: scroll
locationX: "20"
locationY: "28"
width: "460"
height: "240"
relative: true
scrollableV: "auto"
invalidH: true
invalidV: false
layout:
type: FLOW
gapX: "10"
gapY: "8"
fillX: "0"
fillY: "0"
scrollableParts:
商品_1:
type: component
component: 商品条目
props:
id: "diamond_sword"
identity: "shop_slot_diamond_sword"
icon: "local<->textures/shop/diamond_sword.png"
name: "钻石剑"
price: "100"
商品_2:
type: component
component: 商品条目
props:
id: "heal_potion"
identity: "shop_slot_heal_potion"
icon: "local<->textures/shop/heal_potion.png"
name: "治疗药水"
price: "80"
商品_3:
type: component
component: 商品条目
props:
id: "magic_scroll"
identity: "shop_slot_magic_scroll"
icon: "local<->textures/shop/magic_scroll.png"
name: "魔法卷轴"
price: "160"

父容器 layout 会根据每个商品条目的宽高自动排布。需要六个商品就写六个组件实例;每个实例只改 props,按钮脚本、槽位结构、文本结构都保留在组件定义中。

访问内部部件时使用组件实例名加 $

商品_1$购买按钮
商品_2$名称
商品_3$槽位

查看组件

使用命令查看当前已加载的公共组件:

/gp gui components

输出会显示组件名和组件 root.type,方便确认组件库是否已经被读取。


兼容说明

  • canvas.relativePartsscroll.scrollableParts 内都可以引用 type: component
  • 组件可以嵌套组件,内部组件同样会按 component 字段继续加载。
  • options.components 只服务当前 GUI;跨多个 GUI 复用时应写在顶级 components 公共库。
  • 动态插入部件时,插入片段里也可以写 type: component,会按普通部件一样创建。
  • componentprops 用于创建组件时展开模板。界面打开后如果要改显示内容,推荐更新组件内部具体部件,或删除后重新插入整个 component。