Defold GUI/UI 开发:节点体系、布局自适应与 HUD 实战

深入 Defold GUI 系统:GUI 节点体系、文本字体与图集、自适应布局与锚点、动画特效,以及 HUD、菜单与弹窗的完整实现方法。

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 三大块已齐备,下一步就是打包发布(跨平台发布 )。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「defold」更多文章

  1. Defold 行为树与游戏 AI 决策
  2. Defold 材质与着色器语言详解
  3. Defold HTML5 导出与 Web 性能优化