弹层、对话框、底部页面、卡片、HUD 面板、提醒和消息条,它们的面都按 80% 不透明度绘制。 确认框背后,那场仗还在打;背包页面底下,地图依然看得见。在游戏里这不是装饰 —— 正是它让玩家不丢失方位。一张完全不透明的面板,会在窗口打开的那一刻抹掉他刚才站在哪里。
变淡的只有那张面。 文字、图标、按钮、角标和快捷槽依然清晰,边框和阴影也一样。
半透明面板只有在轮廓利落的时候才像玻璃;把轮廓也淡下去,就看不出面板到哪儿为止了。
把内容淡下去,界面就根本读不了 —— 那不是透明窗口,那是 bug。正因如此,这个值从来不通过
modulate.a 施加。
五层,从最具体的一层开始
| 顺序 | 写在哪里 | 单位 | 影响范围 |
|---|---|---|---|
| ① | 调用时传进去的参数 —— surface.alpha、GoStyle.card(…, alpha) | 比例 0.0–1.0 | 一个窗口 |
| ② | GoConfig.container_alpha_overrides[kind] | 比例 0.0–1.0 | 整个项目里的某一类面板 |
| ③ | GoConfig.metric_overrides[<kind>_alpha] | 百分数 | 把所有尺寸集中放在一处的项目 |
| ④ | GoConfig.container_alpha | 比例 0.0–1.0 | 一次管住所有面板 |
| ⑤ | 主题里的 GoHud/constants/<kind>_alpha | 百分数 | 这套外观自己的决定 —— 事实来源 |
五层一个都没设的时候,值就是 100,所以旧主题不会有任何变化。
不透明度处处都是比例 —— 只有主题例外。 每一个
alpha 字段和参数都取 0.0–1.0,
跟 Color.a、modulate.a 是同一套单位,负数表示“没设”。唯一的例外是主题里的常量,
因为 Theme 常量存不了浮点数:它们,以及覆盖它们的那一层(metric_overrides,
它和主题共用同一批名字、同样的整数类型),写成 80。别的地方都不是。
# 只作用于一个窗口 —— 这个确认框不能挡住背后的战斗
surface.alpha = 0.6
sheet.alpha = 0.7
dialogs.alpha = 0.9 # @export 字段也是比例
drawer.alpha = 0.7
GoPopover.open(slot, body, {"alpha": 0.9})
var glass := GoStyle.card(Color.TRANSPARENT, -1.0, -1.0, -1.0, 0.5)
# 按类别 —— HUD 几乎保持不透明,因为它的文字直接压在世界上
GoUi.config.container_alpha_overrides = {
GoTheme.BOX_PANEL: 0.7, # 对话框和底部页面可以透透气
GoTheme.BOX_HUD: 0.95,
}
GoUi.refresh() # 重画已经打开的控件
# 整个项目 —— 画面太热闹的世界会想把面板调回不透明
GoUi.config.container_alpha = 1.0
GoUi.refresh()
GoUi.surface_alpha(GoTheme.BOX_PANEL) # 解析之后的比例,例如 0.8
谁跟着这个值走,谁不跟
| 跟着走 —— 容器 | 不跟 —— 你按的东西,以及标记 |
|---|---|
GoSurface(对话框、底部页面和下拉背后的那一层外壳)· GoSheet · GoDialogs · GoDrawer · GoPopover · GoNotice · GoSnackbar · GoPromptCard · GoCoachMark · GoConsole |
所有按钮 · GoSlot(快捷槽)· GoBadge · 分段控件 · 选择单元格 · 标签片 · 圆形底和头像 |
GoStyle.card() · hud_panel() · overlay_panel() · alert() · plate() · edge_card_panel() · style_notice_panel() · floating() · box() · surface() |
所有文字和图标 |
状态被冲淡的按钮,就说不清自己是什么了;而角标存在的理由,就是让那两个字看得清 —— 两者都不是容器,所以都不跟这个值走。
弹出菜单保持不透明(
popup_alpha 为 100)。引擎可能把 PopupMenu
放进它自己的 Window 里,而在那里,操作系统不会把它和游戏画面合成:半透明出来是黑的,
不是透的。只有把子窗口内嵌的项目(gui_embed_subwindows)才该把它调低。
来自真实屏幕的三个细节
-
这个值是乘上去的,不是替换。 随包 HUD 的面本来就是 0.92 的 alpha,所以
hud_alpha为 80 时落在 0.736 —— 某套主题特意把一张面做成半透明,这个决定就保留下来。 反过来说:施加两次,面板就淡两次,所以 alpha 只在一个地方相乘(GoSkin.surface_box()), 其余每个画面的函数都只是把参数往下传,而不是再淡一次自己的结果。 -
你传进去的是什么颜色,拿到的就是什么颜色。
GoStyle.plate(…, fill)会原样保留显式给出的填充色:Color(ink, 0.14)已经写明了自己的 alpha, 再把面板不透明度乘进去,等于把调用者的意图砍两遍。GoStyle.floating(…, opaque = true)出于同样的道理忽略这个值 —— 把面填成不透明,本来就是这个标志的全部用处。 -
不是 gohud 建的面板,也照同一条规则来。
GoStyle.fade_panel(node)—— 要在节点进入场景树之后调用,这样它才读得到自己继承来的主题。它是幂等的:原来的那张面会被记下来, 每次调用都从它重算,所以反复重画也不会叠加。换过主题之后用GoStyle.forget_face(node)把这份记忆丢掉,下一次调用就会认新的面。
自定义的面也会变淡 —— 而这正是会悄悄坏掉的地方
斜切角的面板(GoStyleBoxCut)和锻造边框(GoStyleBoxMedieval)不是
StyleBoxFlat,它们是自己画出来的。要是只把不透明度放进 flat 那一支,这两套主题就会悄悄忽略它,
而所有基于数值的测试照样通过。两者都让自己的背景变淡,同时把外发光、铆钉、角落雕花和斜面保持在满强度 ——
而中世纪那张面的纹理和斜面本来就和它的背景 alpha 成正比,所以会跟着面板一起淡下去。
说到底是这么回事:bg_color.a == 0.8 只是一个数。世界透得过来,而文字依然读得清
才是要求。前者 gohud 在无头测试套件里查,后者靠在虚拟显示器上把面板画到斜条纹之上来看
(tests/gohud_alpha_shot.gd)—— 平面的、斜切的和锻造的三种面,深色和浅色主题都要过。