引言
动画是游戏「活」起来的关键。Defold 的动画体系分为三层:Sprite(静态精灵)→ Flipbook(逐帧动画)→ 程序动画(go.animate 缓动),再叠加骨骼动画(Spine)与动画状态机。本文从最基础的 Sprite 组件讲起,逐步覆盖图集与 Flipbook 帧动画、go.animate 的属性缓动系统(位置/旋转/缩放)、动画播放控制与回调、程序化动画曲线,最后讲 Spine/DragonBones 骨骼动画集成与实战动画状态机,让你给 Defold 角色配上流畅的动作系统。
前置:/defold-game-engine-introduction/(组件体系)、/defold-script-system-lua/(msg 与生命周期)。物理表现见 /defold-physics-collision/。
目录
- 1. 动画体系总览:三层动画模型
- 2. Sprite 组件与图集(Atlas)
- 3. Flipbook 逐帧动画:定义与播放
- 4. go.animate 缓动系统:位置/旋转/缩放动画
- 5. 缓动曲线与 Easing 类型
- 6. 动画控制:播放、停止、暂停与回调
- 7. 程序化动画:曲线路径与贝塞尔控制
- 8. Spine/DragonBones 骨骼动画集成
- 9. 动画状态机:从待机到攻击的实战
- 10. 速查表
- 延伸阅读
1. 动画体系总览:三层动画模型
Defold 动画的三种形态:
① Sprite(静态):一张图 → 换图切帧
② Flipbook(逐帧):图集多帧循环 → 传统动画
③ 程序动画(go.animate):属性插值 → 移动/旋转/缩放/透明度
└─ 骨骼动画(Spine/DragonBones):复杂角色骨骼驱动
各自适用场景:
| 方式 | 适用 | 性能 | 制作 |
|---|---|---|---|
| Sprite 换图 | 简单状态(换表情) | 极高 | 美术出图 |
| Flipbook | 跑动/攻击逐帧 | 高 | 帧序列 |
| go.animate | 位移/UI/粒子运动 | 极高 | 代码控制 |
| Spine | 角色骨骼动画 | 中 | 骨骼软件 |
心智:动画 = 让「属性或帧」随时间变化——先想清楚要动什么,再选这三层里的对应工具。
2. Sprite 组件与图集(Atlas)
Sprite 组件显示一张图——图集(Atlas)统一管理图片资源:
Atlas(图集):
把多张图片打包成一张大图 + 元数据
好处:一次纹理上传、精灵按名引用
代码里创建 Sprite 并换图:
-- 给 game object 添加 sprite 组件
local sp = msg.url("#sprite") -- 引用 sprite 组件
-- 播放图集中的某张图(Flipbook 或静态帧)
sprite.play_flipbook(sp, hash("idle"))
-- 或直接换静态图
msg.post(sp, "set_texture", { texture = resource.get_atlas("atlas:main") })
Sprite 常用属性(go.get/set):
go.set(sp, "scale", vmath.vector3(2, 2, 2)) -- 缩放
go.set(sp, "tint", vmath.vector4(1, 0.5, 0.5, 1)) -- 染色(命中闪白)
go.set(sp, "size", vmath.vector3(128, 128, 0)) -- 渲染尺寸
| 属性 | 作用 | 常用场景 |
|---|---|---|
| scale | 缩放 | 放大缩小 |
| tint | 染色 | 受击闪白/阵营颜色 |
| size | 尺寸 | 自适应 |
| flip | 翻转(水平/垂直) | 朝向切换 |
记忆:Sprite = 图集里的一张图 + 渲染属性——换图用
sprite.play_flipbook、调属性用go.set。
3. Flipbook 逐帧动画:定义与播放
Flipbook = 图集里的一组帧,按序列播放:
制作流程:
1. 美术导出帧序列:walk_1.png … walk_8.png
2. 导入 Defold → 图集自动生成 Flipbook 动画:walk
3. 代码播放动画名字
播放 Flipbook:
local sp = msg.url("#sprite")
-- 播放一次(p 参数)
sprite.play_flipbook(sp, hash("walk"), { playback = go.PlaybackMode.PLAYBACK_ONCE_FORWARD })
-- 循环播放
sprite.play_flipbook(sp, hash("walk"), { playback = go.PlaybackMode.PLAYBACK_LOOP_FORWARD })
-- 倒放/镜像
sprite.play_flipbook(sp, hash("walk"), { playback = go.PlaybackMode.PLAYBACK_ONCE_BACKWARD })
播放模式速查:
| PlaybackMode | 行为 |
|---|---|
| ONCE_FORWARD | 正向播一次 |
| ONCE_BACKWARD | 反向播一次 |
| LOOP_FORWARD | 正向循环 |
| LOOP_BACKWARD | 反向循环 |
| LOOP_PINGPONG | 来回循环 |
手动控制帧(帧精度控制):
-- 手动设置当前帧(按索引)
sprite.set_hflip(sp, true) -- 水平翻转
-- 或设置动画进度
go.set(sp, "cursor", 0.5) -- 播到 50%
记忆:Flipbook 是「帧序列 + 播放模式」——
playback参数决定播一次/循环/来回,适合跑动攻击等帧动画。
4. go.animate 缓动系统:位置/旋转/缩放动画
go.animate = 让任意属性随时间插值变化——Defold 程序动画的核心:
-- 移动:让 position 从当前位置动画到目标
go.animate(".", "position", go.PlaybackMode.PLAYBACK_ONCE_FORWARD,
vmath.vector3(100, 100, 0), go.Easing.LINEAR, 2.0)
-- 旋转
go.animate(".", "rotation", go.PlaybackMode.PLAYBACK_ONCE_FORWARD,
vmath.quat_rotation_z(math.rad(180)), go.Easing.IN_OUT_QUAD, 1.0)
-- 缩放
go.animate(".", "scale", go.PlaybackMode.PLAYBACK_LOOP_PINGPONG,
vmath.vector3(1.5, 1.5, 1), go.Easing.OUT_BOUNCE, 0.5)
-- 透明(GUI/Sprite 染色 alpha)
go.animate("#sprite", "tint.w", go.PlaybackMode.PLAYBACK_ONCE_FORWARD,
0, go.Easing.IN_QUAD, 0.3)
go.animate 签名:
go.animate(url, property, playback, to, easing, duration)
-- 对象 属性 模式 目标值 曲线 时长(秒)
| 参数 | 说明 |
|---|---|
| property | 属性(position/rotation/scale/tint.w…) |
| playback | 播放模式(同 Flipbook) |
| to | 目标值 |
| easing | 缓动曲线(见下节) |
| duration | 时长(秒) |
记忆:go.animate 五要素——属性、模式、目标值、曲线、时长——位置/旋转/缩放/透明度都能动,UI 与角色运动一网打尽。
5. 缓动曲线与 Easing 类型
缓动曲线决定「快慢节奏」——从线性到弹性:
LINEAR 匀速
IN_QUAD 先慢后快
OUT_QUAD 先快后慢
IN_OUT_QUAD 两头慢中间快
OUT_BOUNCE 结束弹跳
OUT_ELASTIC 结束回弹
IN_BACK / OUT_BACK 超出再回(回拉效果)
曲线选型直觉:
| 场景 | 推荐 Easing |
|---|---|
| 匀速移动(滚屏) | LINEAR |
| 加速下落(重力) | IN_QUAD |
| 减速到位(UI 滑入) | OUT_QUAD |
| 弹跳(落地面) | OUT_BOUNCE |
| 回弹(弹性按钮) | OUT_ELASTIC |
-- 按钮回弹效果
go.animate("#btn", "scale", go.PlaybackMode.PLAYBACK_LOOP_PINGPONG,
vmath.vector3(1.1, 1.1, 1), go.Easing.OUT_ELASTIC, 0.8)
记忆:缓动曲线是「动感的灵魂」——匀速平铺、回弹有趣、弹性 Q 弹,游戏手感全靠它调。
6. 动画控制:播放、停止、暂停与回调
控制正在进行的动画:
-- 取消所有正在进行的 go.animate 动画
go.cancel_animations(".", "position")
-- 取消该对象所有属性动画
go.cancel_animations(".", go.PROP_ANIM_POSITION)
-- 停止 Flipbook 播放
msg.post("#sprite", "play_animation", { id = hash("idle"), playback = ... })
动画完成回调(animation_done 消息):
-- 在 receive_message 里监听
function on_message(self, message_id, message, sender)
if message_id == hash("animation_done") then
-- message.animation_id 是刚播完的动画名
-- 用于:播完攻击→回待机;播完死亡→销毁
print("动画播完:", message.animation_id)
end
end
播放完成后的链式动画:
-- 攻击动画播完 → 自动回待机
function on_message(self, message_id, message, sender)
if message_id == hash("animation_done") then
sprite.play_flipbook("#sprite", hash("idle"), { playback = go.PlaybackMode.PLAYBACK_LOOP_FORWARD })
end
end
记忆:动画编排靠
animation_done回调——播完攻击接待机、播完死亡接销毁,一条链把动作串起来。
7. 程序化动画:曲线路径与贝塞尔控制
go.animate 只能直线/单属性——复杂路径要程序控制:
正弦浮动(敌人上下漂浮):
-- 每帧更新 y 位置
local t = self.t or 0
self.t = t + dt * 2.0
local base_y = self.base_y or 0
go.set(".", "position.y", base_y + math.sin(self.t) * 20)
贝塞尔曲线路径(弹道/引导子弹):
-- 二次贝塞尔:P(t) = (1-t)²P0 + 2(1-t)t·P1 + t²·P2
local function quad_bezier(p0, p1, p2, t)
local it = 1 - t
return it * it * p0 + 2 * it * t * p1 + t * t * p2
end
-- 每帧推进 t
self.t = math.min(self.t + dt * self.speed, 1)
local x = quad_bezier(self.start.x, self.ctrl.x, self.end.x, self.t)
local y = quad_bezier(self.start.y, self.ctrl.y, self.end.y, self.t)
go.set(".", "position", vmath.vector3(x, y, 0))
插值工具:
| 手段 | 用途 |
|---|---|
| 每帧计算 + go.set | 任意曲线 |
| math.sin | 周期性浮动 |
| 贝塞尔 | 平滑路径 |
| vmath.lerp | 两值线性插值 |
记忆:复杂运动 = 每帧「算好位置再 set」——go.animate 管简单插值,正弦/贝塞尔管花活。
8. Spine/DragonBones 骨骼动画集成
骨骼动画 = 用「骨骼」驱动蒙皮,适合复杂角色(四肢、表情):
Spine / DragonBones:
美术在骨骼软件里摆动作
导出 JSON + 纹理图集
Defold 加载 → 作为骨骼动画资源播放
集成步骤:
1. 导出 Spine JSON + atlas
2. 拖入 Defold 资源 → 自动生成模型组件
3. 代码控制骨骼动画
代码控制骨骼动画:
-- 播放骨骼动画(通过模型组件)
local model = msg.url("#model")
msg.post(model, "play_anim", { id = hash("run") })
-- 监听播完
function on_message(self, message_id, message, sender)
if message_id == hash("animation_done") then
msg.post("#model", "play_anim", { id = hash("idle") })
end
end
骨骼 vs Flipbook 选择:
| 维度 | Flipbook | 骨骼动画 |
|---|---|---|
| 灵活性 | 固定帧 | 骨骼实时变换 |
| 体积 | 每帧一张图 | 一套骨骼 + 蒙皮 |
| 制作成本 | 逐帧画 | 骨骼绑定 |
| 适合 | 像素/低面 | 复杂角色 |
记忆:角色复杂用 Spine/DragonBones,简单用 Flipbook——骨骼动画一套动作多方向复用,代价是绑定成本。
9. 动画状态机:从待机到攻击的实战
角色动画状态机(有限状态机,参考 /defold-game-engine-complex-logic-state-management/):
-- 状态定义
local STATE = { IDLE = 1, RUN = 2, ATTACK = 3, HURT = 4, DEAD = 5 }
-- 状态 → 动画映射
local ANIM = {
[STATE.IDLE] = hash("idle"),
[STATE.RUN] = hash("run"),
[STATE.ATTACK] = hash("attack"),
[STATE.HURT] = hash("hurt"),
[STATE.DEAD] = hash("dead"),
}
function init(self)
self.state = STATE.IDLE
end
-- 切状态统一入口
local function set_state(self, new_state)
self.state = new_state
local anim = ANIM[new_state]
local playback = go.PlaybackMode.PLAYBACK_ONCE_FORWARD
if new_state == STATE.IDLE or new_state == STATE.RUN then
playback = go.PlaybackMode.PLAYBACK_LOOP_FORWARD -- 待机/跑动循环
end
sprite.play_flipbook("#sprite", anim, { playback = playback })
end
-- 攻击播完 → 回待机
function on_message(self, message_id, message, sender)
if message_id == hash("animation_done") then
if self.state == STATE.ATTACK then set_state(self, STATE.IDLE) end
end
end
记忆:动画状态机 = 状态 → 动画映射 + 统一切换函数 + animation_done 回稳态——一套映射管住所有动作,避免动画乱飞。
10. 速查表
| 需求 | 做法 |
|---|---|
| 显示一张图 | Sprite + Atlas |
| 播逐帧动画 | sprite.play_flipbook(sp, hash("walk"), ...) |
| 属性动画 | go.animate(".", "position", ..., target, easing, dur) |
| 循环播放 | playback = LOOP_FORWARD |
| 弹性/弹跳 | go.Easing.OUT_ELASTIC / OUT_BOUNCE |
| 取消动画 | go.cancel_animations(".", "position") |
| 播完回调 | 监听 animation_done |
| 浮动/路径 | 每帧 sin/贝塞尔 + go.set |
| 骨骼动画 | Spine/DragonBones 导出 + model.play_anim |
| 状态机 | STATE→ANIM 映射 + set_state + 回调回稳态 |
一句话记忆:动画三层——Sprite 换图、Flipbook 帧序列、go.animate 属性插值;播放模式管循环、Easing 管手感、animation_done 管串联;复杂角色上 Spine、花活路径走每帧贝塞尔、状态机用映射统一——Defold 动画体系一条链路全打通。
延伸阅读
- /defold-game-engine-complex-logic-state-management/ — 状态机与 AI 行为
- /defold-script-system-lua/ — msg 消息与生命周期
- /defold-editor-asset-pipeline/ — 图集与资源管线
- /defold-ui-gui/ — GUI 动画与 HUD
- /defold-performance-optimization/ — 精灵性能优化
- [[graphics]] — 2D 渲染与图集原理
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。