Defold 的 UI 层由独立的 GUI 系统承载:.gui 资源描述界面结构,.gui_script 驱动逻辑与动画。GUI 与游戏世界(Game Object 体系)相对独立,但有清晰的联动方式。(Defold GUI docs) 本文从节点体系讲到自适应布局与 HUD 实战,帮助你搭建从主菜单到游戏内 HUD 的完整界面。
前置建议:先掌握 Defold Lua 脚本系统深入 中的消息模型,GUI 脚本同样依赖 on_message 通信。
一、GUI 组件与节点体系
1. GUI 组件与 GUI 脚本
一个 UI 通常由两部分组成:
- GUI 组件(.gui 资源):声明节点树,放在 Game Object 上
- GUI 脚本(.gui_script):绑定在该组件上,写逻辑与动画
编辑器操作:右键 → New → GUI 创建 .gui,拖到 Game Object 上作为 Component;再创建 .gui_script 并在 Properties 中绑定到 GUI 组件。
2. 节点类型
GUI 编辑器可创建以下节点:
- Box:矩形色块,可做按钮底、进度条背景
- Text:文本节点
- Texture:图片节点(引用图集内图片)
- Pie:扇形(血量环、冷却圈)
- Slider / Template 等高级节点用于列表与滑动条
-- 运行时动态创建 Box 节点
local node = gui.new_box(vmath.vector3(100, 100, 0), vmath.vector3(120, 60, 0))
gui.set_color(node, vmath.vector4(1, 0.3, 0.3, 1))
3. 节点层级与变换
在 GUI 编辑器的 Outline 中可拖动节点建立父子关系。父节点变换会影响子节点,这与 Game Object 的父子机制一致。节点的 Pivot(锚点)决定旋转/缩放中心:
PIVOT_CENTER:中心PIVOT_NW/N/NE/W/E/SW/S/SE:九宫格锚点
Size Mode 决定节点大小来源:Auto(随内容)、Manual(手动设置)。
4. 节点查找与通用操作
GUI 脚本通过节点 id 或路径获取节点:
function init(self)
self.hp_bar = gui.get_node("hp_bar")
self.title = gui.get_node("menu/title") -- 斜杠寻子节点
end
-- 常用节点操作
gui.set_position(self.hp_bar, vmath.vector3(10, 20, 0))
gui.set_scale(self.title, vmath.vector3(1.2, 1.2, 1))
gui.set_enabled(self.hp_bar, false) -- 显隐
二、文本与字体
1. 创建字体与文字节点
字体资源由 game.project 的 [fonts] 段引用 TTF 文件生成,编辑器里右键 → New → Font 选择字体文件即可。文字节点属性:
- Text:显示内容
- Font:绑定的字体资源
- Size:字号
- Color:颜色
- Leading / Tracking:行距 / 字距
- Alignment:左/中/右对齐
- Line Break:自动换行
2. 动态修改文本
function set_score(self, value)
gui.set_text(gui.get_node("score_text"), tostring(value))
end
中文需要字体支持对应字形(TTF 内含中文子集或大字体文件);大字体体积大,可考虑拆分字体、按需加载。
3. 文本与图集的配合
按钮一般由「背景图(Texture)+ 文字(Text)」两个节点组合,文字叠在图上。把图片放入图集可减少 Draw Call,参见 编辑器与资源管线。
三、布局与自适应
1. 屏幕适配策略
移动与 Web 上横竖屏、不同分辨率的差异巨大。Defold 的相机与 GUI 坐标系基于 game.project 中的 [display] 设置:
[display]
width = 1280
height = 720
GUI 节点坐标默认相对屏幕左下角(原点)。为了在不同分辨率下不变形,常用策略:
- 锚点布局:用
Adjust Mode让节点贴边(左下、顶部居中、右下等) - 留安全区:刘海边距用
Adjust Reference保证避开刘海 - 固定逻辑分辨率 + 缩放:
[display]中设scale_mode = FIT,等比缩放适配
2. Adjust Mode 详解
GUI 节点属性 Adjust Mode 决定它相对哪个「基准」定位:
FIT:等比缩放整个 GUI,保持比例(默认)ZOOM:等比但可能裁切STRETCH:拉伸填满(可能变形,慎用)
配合 Pivot 设置,按钮「永远贴着右上角」等需求一行属性即可完成:
Pivot = NE
Adjust Mode = FIT
Position = (-10, -10, 0) -- 距右上角 10px
3. 响应式布局代码示例
需要更精细的适配时,脚本读取屏幕尺寸计算:
function init(self)
local width = gui.get_size() -- 返回 x, y, z
local half_w = width.x * 0.5
gui.set_position(gui.get_node("center_panel"), vmath.vector3(half_w, 0, 0))
end
4. 多语言与本地化
文本内容集中管理:
local L = require "main.localization"
function init(self)
gui.set_text(gui.get_node("start_btn"), L.get("start_game"))
end
切换语言时遍历语言表更新全部文本节点即可,无需改动布局。
四、动画与特效
1. gui.animate:属性补间
GUI 节点动画使用 gui.animate(),支持位置、缩放、旋转、颜色、透明度等属性的缓动:
-- 淡入
gui.animate(node, gui.PROP_COLOR, vmath.vector4(1, 1, 1, 1),
gui.EASING_INOUTQUAD, 0.3, 0, nil, gui.PLAYBACK_ONCE_FORWARD)
-- 弹跳进入
gui.animate(node, gui.PROP_SCALE, vmath.vector3(1, 1, 1),
gui.EASING_OUTBACK, 0.4)
-- 循环旋转
gui.animate(node, gui.PROP_ROTATION, vmath.vector3(0, 0, 360),
gui.EASING_LINEAR, 2.0, 0, nil, gui.PLAYBACK_LOOP_FORWARD)
常用缓动:gui.EASING_INOUTQUAD(平滑)、gui.EASING_OUTBACK(弹性)、gui.EASING_LINEAR(线性)。
2. 动画回调
gui.animate 最后一个参数可传完成回调,用于「动画结束后切换场景」:
local function on_fade_done()
msg.post("/main/main.collection", "proceed_to_next_level")
end
gui.animate(node, gui.PROP_COLOR, vmath.vector4(0, 0, 0, 1),
gui.EASING_LINEAR, 1.0, 0, on_fade_done, gui.PLAYBACK_ONCE_FORWARD)
3. 序列与循环特效
组合多个 animate 实现复杂序列(命中闪白、宝箱开合、结算滚动):
function flash_white(self, node)
gui.set_color(node, vmath.vector4(1, 1, 1, 1))
gui.animate(node, gui.PROP_COLOR, vmath.vector4(1, 1, 1, 0),
gui.EASING_INQUAD, 0.15)
end
五、HUD 与菜单实现
1. 游戏内 HUD 的常用结构
HUD 常驻屏幕,需要响应游戏事件。推荐结构:HUD 是一个独立 Game Object + GUI 组件,通过消息接收「血量、分数」数据:
-- hud.gui_script
function init(self)
self.score_node = gui.get_node("score")
self.hp_bar = gui.get_node("hp_bar")
end
function on_message(self, message_id, message, sender)
if message_id == hash("update_hp") then
-- 按比例缩宽血条
local scale = message.hp / message.max_hp
gui.set_scale(self.hp_bar, vmath.vector3(math.max(scale, 0.01), 1, 1))
elseif message_id == hash("update_score") then
gui.set_text(self.score_node, tostring(message.score))
end
end
游戏内逻辑只需要 msg.post("hud", "update_hp", {...}),HUD 与玩法完全解耦。
2. 血条进度条的实现
进度条核心是「用 Box 节点宽度表达比例」:
function set_bar(self, node, ratio)
local size = gui.get_size(node)
gui.set_size(node, vmath.vector3(size.x * ratio, size.y, 0))
end
配合背景暗色 Box + 前景亮色 Box 叠层,即是最实用的血条。
3. 主菜单与场景切换
菜单通常用一个独立 Collection 承载。切换场景 = 卸载旧集合、加载新集合(参考 Collection 加载):
-- 主菜单点击「开始」
function on_message(self, message_id, message, sender)
if message_id == hash("start_game") then
msg.post("/menu_proxy", "unload") -- 卸载菜单
msg.post("/game_proxy", "load") -- 加载游戏
end
end
4. 弹窗与确认框
弹窗是「覆盖式」UI:一个半透明全屏 Box 遮罩 + 内容面板 + 按钮。显示/隐藏用 gui.set_enabled,动画入场用 gui.animate:
function show_pause(self)
gui.set_enabled(gui.get_node("pause_root"), true)
gui.animate(gui.get_node("pause_panel"), gui.PROP_COLOR,
vmath.vector4(1, 1, 1, 1), gui.EASING_OUTBACK, 0.3)
end
六、输入处理与交互
1. GUI 接收输入
GUI 脚本默认不接收输入,需先获取:
function init(self)
msg.post(".", "acquire_input")
end
2. 按钮点击检测
按钮交互基于「命中检测 + 输入回调」:
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
local pos = gui.screen_to_node(gui.get_node("start_btn"), action.screen_pos)
if gui.pick_node(gui.get_node("start_btn"), pos.x, pos.y) then
msg.post("#", "start_game")
end
end
end
gui.pick_node 判断点是否落在节点内,是按钮检测的标准 API。
3. 输入与场景聚焦
多个 GUI 同时存在时(HUD + 弹窗),输入焦点容易乱。惯用方案:
- 弹窗打开时,暂停 HUD 脚本输入:
msg.post("hud#gui_script", "disable_input") - 或由全局输入脚本统一分发:先判断是否在弹窗内,是则只响应弹窗
七、GUI 性能与调试
1. 降低 Draw Call
- 所有 UI 图片放同一张图集(含 9-slice 缩放按钮)
- 减少动画对同一节点属性的高频率
gui.set_*(每帧设置最耗) - 静态界面用编辑器静态布局,避免运行时逐帧创建
2. 常见问题
- 文字不显示:字体资源未绑定、或字库不含所需字符
- 节点找不到:id 拼写或路径层级写错,
gui.get_node报 nil - 点击没反应:
acquire_input未调用,或节点被disable/被遮挡 - GUI 随窗口错位:Adjust Mode 与 Pivot 组合不当,检查基准设置
3. 调试技巧
- GUI 编辑器支持直接预览与热重载,改属性即时可见(见 热更新与热重载)
gui.get_node结果打印 id 排查层级错误- Profiler 中关注
gui分区耗时,与物理、渲染对照定位瓶颈
八、总结
Defold GUI 系统的核心优势是「声明式节点 + 脚本驱动动画」:用 .gui 描述界面、用 Adjust Mode 解决自适应、用 gui.animate 与消息实现动效与数据绑定。把 HUD 做成独立对象并只通过消息喂数据,能让 UI 与玩法长期保持低耦合。
至此,脚本(脚本系统)、物理(物理引擎)、UI 三大块已齐备,下一步就是打包发布(跨平台发布)。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。