@editable 与 Details 面板:让美术也能改你的组件
一个只有程序能改的组件是半成品。@editable 把字段送进 Details 面板,让策划和美术自己调参。这一页把它讲透:能暴露哪些类型、怎么加滑块和数值上下限、怎么分组和写悬浮提示,以及默认值与关卡里的覆盖值到底谁说了算。
一、写在哪、意味着什么
@editable 是一个属性(attribute):以 @ 开头,单独占一行,写在被它修饰的字段上面。这一点和 <public>、<final_super> 那种夹在尖括号里的说明符(specifier)是两套语法,别混。
lift_platform_component<public> := class<final_super>(component):
@editable
Height<public>:float = 200.0
# ▲ ▲ ▲ ▲
# 属性 可见性 类型 默认值(必须有)
加上它之后发生三件事:这个字段出现在 UEFN 的 Details 面板里;代码里写的那个值成为面板里的初始显示值;把这个组件装到不同 entity 上时,每一份都能填不同的值。官方文档对最后一条的表述很直白:改一个可编辑属性的值,只对该实例生效——同一个组件在关卡里有五份,五份可以各不相同。而且改面板不需要重新编译。
这三条合起来,恰好就是蓝图 Instance Editable 的全部语义。你在蓝图变量旁边点亮的那只小眼睛,在 Verse 里变成了一行字。
反过来说:不加 @editable 的字段是纯内部数据,面板里根本看不到。这也是一条设计纪律——只把「真正需要别人调」的东西暴露出去,别把内部状态一股脑摊在面板上,否则策划面对二十个不知道能不能动的格子,只会来问你。
二、能暴露哪些类型
官方文档给出的可编辑类型清单如下。注意「容器和结构体」那一档有个递归条件:里面装的东西也得是可编辑的,否则整个字段都进不了面板。
| 档次 | 类型 | 面板里长什么样 |
|---|---|---|
| 基础类型 | logic、int、float、string、enum |
勾选框、数字输入框、文本框、下拉列表 |
| 容器 | 可编辑类型的 array、map |
可增删的列表 / 键值对列表 |
| 结构体 | 字段全部可编辑的 struct |
一个可展开的分组 |
| 类实例 | 类的实例(包括设备类型) | 一个可指向场景中对象的引用槽 |
把这张表和蓝图变量面板的 Variable Type 下拉对照着看,覆盖面几乎重合:布尔、整数、浮点、字符串、枚举、数组、映射、结构体、对象引用——蓝图里能勾 Instance Editable 的东西,Verse 这边基本都能标 @editable。
「类实例」那一档最像蓝图里的「Actor 引用变量」:在蓝图里你会声明一个 Target Door 变量、类型选某个蓝图类,然后在关卡里用滴管吸一个具体的门。Verse 这边写法是给字段一个类实例的默认值,面板里就会出现同样的引用槽。
三、变体:给格子加护栏
裸 @editable 给出的是一个不设防的输入框——策划可以把 Height 填成 -99999。蓝图里你会在变量的 Details 里设 Slider Range 和 Value Range;Verse 这边则是换用一个更具体的属性变体。官方文档列出的主要变体如下:
| 变体 | 用途 | 蓝图对应 |
|---|---|---|
@editable_number(类型) |
带 MinValue / MaxValue 的数值输入框 | 变量 Details 里的 Value Range |
@editable_slider(类型) |
带上下限和步长的滑块 | 变量 Details 里的 Slider Range |
@editable_text_box |
多行文本框,可限字数 | String 变量 + Multi Line 勾选 |
@editable_vector_slider(类型) |
向量的逐分量滑块,可锁比例 / 归一化 | Vector 变量的三个输入框 |
@editable_vector_number(类型) |
向量的逐分量数值框(不带滑块) | 同上,不给拖动条 |
@editable_container |
容器的显示配置(目前限数组),如是否允许拖动排序 | 数组变量的 Details 选项 |
写法上,这些变体后面跟一个冒号,把配置项缩进写在下面——和 Verse 别处的块结构一致:
guarded_component<public> := class<final_super>(component):
# 滑块:0.0 ~ 10.0,每格 1.0
@editable_slider(float):
MinValue := option{0.0}
MaxValue := option{10.0}
SliderDelta := option{1.0}
Speed<public>:float = 1.0
# 数值框:限制在 0 ~ 10 之间
@editable_number(int):
MinValue := option{0}
MaxValue := option{10}
Lives<public>:int = 3
注意上下限用的是 option{...}——第 18 课讲过的可选值:你可以只设下限不设上限,那就只写 MinValue 一行。这比蓝图那两对必须成双填写的 Range 输入框更灵活一点。
四、分组与悬浮提示:把面板整理得像样
组件参数一多,面板就成了一锅粥。蓝图的解法是给变量填 Category 和 Tooltip;Verse 的解法一样,只是要先把文案声明成 message,再挂到属性上:
# 面板里的分组名与提示文案,声明成可本地化的 message
MotionCategory<public><localizes>:message := "运动参数"
HeightTip<public><localizes>:message := "平台升起的高度,单位厘米"
tidy_component<public> := class<final_super>(component):
@editable:
ToolTip := HeightTip
Categories := array{MotionCategory}
Height<public>:float = 200.0
<localizes> 是本地化说明符:标了它的文案会进入翻译流程,面板上的文字将来能跟着语言走。这一点比蓝图里直接手打一串 Category 字符串要正规——蓝图的 Category 是纯英文标识,不参与本地化。
Categories 收的是一个数组,所以一个字段可以同时归入多个分组。日常够用的做法是:每个功能维度一个分组名常量,组件顶部集中声明,底下的字段各挂各的。
五、默认值与覆盖值:谁说了算
这是最容易产生误会的一节,也是最值得记牢的一节。
- ▢ 代码里的值是「出厂默认」。你写
Height:float = 200.0,意思是「新装上这个组件时,面板里显示 200」。它不是「永远是 200」。 - ▢ 面板里的值是「这一份的覆盖」。官方原话:改动只对该实例生效。同一个组件装在五个 entity 上,五份互不干扰。
- ▢ 改面板不需要重新编译。这是它相对「改代码里的常量」的全部价值——策划改数、程序不用在场。
- ▢ 改代码里的默认值,不会追溯已经填过的实例。这条要特别小心:你把默认值从 200 改成 300 之后,关卡里那些已经被手动改过的实例仍然保持它们自己的值。想让所有人跟着变,要么挨个改面板,要么在代码里绕开这个字段。这和蓝图里改「变量默认值」不会覆盖已放置 Actor 的 Details 面板是完全一样的坑。
由此推出一条实践建议:默认值要写成「一个人不填任何东西也能看到正确效果」的值。默认 0.0 的高度、默认空字符串的名字,只会让第一次装上组件的人以为它坏了。给每个可编辑字段一个能自证的默认值,是对协作者最基本的体贴。
再补一条来自第 27 课正文的对照:蓝图变量还有 Expose on Spawn,Verse 没有同名开关。要在「生成时传参」,做法是构造实例时直接给字段赋值,比如 lift_platform_component{ Height := 300.0 }。这条路径和 Details 面板是并列的两种填值方式,不冲突。
你把组件代码里 Height 的默认值从 200.0 改成了 300.0。关卡里有一块平台,之前被策划在 Details 面板手动填成了 150.0。重新编译后,这块平台的 Height 是多少?
来源
本文整理自 Epic 官方文档:
属性变体的具体配置项会随版本增补,动手前请以当时的官方文档为准。