2D骨骼动画零基础教学,可直接复制使用(但不保证不会出问题)
03/07221 浏览开发心得
================================================================================
2D 骨骼动画系统 — 通用技术文档
================================================================================
整理日期: 2026-03-04
状态: 生效中(随开发迭代更新)
适用范围: 本项目所有骨骼动画相关开发
================================================================================
一、系统概述
================================================================================
基于 UrhoX + NanoVG 的 2D 骨骼动画系统,纯 Lua 实现。
核心引擎(Skeleton.lua)与具体角色数据完全分离,
同一套引擎可驱动任意骨骼结构和任意动画数据。
技术栈: UrhoX 引擎 / Lua 5.4 / NanoVG 矢量渲染
渲染方式: 当前为纯色圆角矩形(可扩展为贴图渲染)
核心设计原则:
· 引擎通用,数据驱动 — 所有角色差异都在数据层,引擎代码不改
· 三层动画叠加 — 身体层 + 上半身叠加层 + 面部层,互不干扰
· 增量动画模式 — 关键帧值叠加到初始姿态,零值 = 不变
================================================================================
二、文件结构
================================================================================
scripts/
skeleton_demo.lua -- Demo 入口(当前角色数据 + 渲染 + UI)
animation/
Skeleton.lua -- 核心引擎(通用,不含任何角色数据)
docs/
骨骼动画系统.txt -- 本文档
================================================================================
三、核心引擎 API — Skeleton.lua
================================================================================
3.1 创建实例
────────────────────────────────────────────────────────────────────────────
local Skeleton = require("animation.Skeleton")
local skel = Skeleton.new(skeletonData, animations)
参数:
skeletonData table[] 骨骼定义数组(父骨骼必须在子骨骼之前)
animations table[] 动画定义数组
返回: Skeleton 实例
3.2 实例属性
────────────────────────────────────────────────────────────────────────────
位置/缩放:
skel.x, skel.y -- 整体位置(屏幕坐标,像素)
skel.globalScale -- 整体缩放(默认 1.0)
skel.flipX -- 水平翻转(默认 false)
播放控制:
skel.loop -- 是否循环播放(默认 true)
skel.speed -- 播放速度倍率(默认 1.0)
skel.playing -- 身体动画是否在播放(只读)
skel.playingFace -- 面部动画是否在播放(只读)
skel.playingUpperBody -- 上半身叠加是否在播放(只读)
skel.currentAnimName -- 当前身体动画名(只读)
skel.upperBodyAnimName -- 当前上半身叠加动画名(只读)
朝向:
skel.direction -- 当前朝向 "front" / "back"
骨骼数据:
skel.bones -- name -> bone table 字典
skel.boneList -- 有序骨骼列表
skel.attachmentSets -- name -> { front={...}, back={...} } 附件集
动画缩放:
skel.animScaleX -- 动画驱动的全局水平缩放(默认 1.0)
3.3 核心方法
────────────────────────────────────────────────────────────────────────────
身体动画:
skel:play(animName)
播放指定身体动画。返回 bool。
skel:update(dt)
每帧更新。内部自动执行:
1) 重置到初始姿态 (setupPose)
2) 应用身体动画增量插值
3) 应用上半身叠加层(如果正在播放)
4) 应用面部动画叠加
5) 刷新世界变换
skel:updateWorldTransform()
手动刷新所有骨骼世界坐标。
修改 x/y/globalScale/flipX 后需调用。
skel:resetToSetupPose()
将所有骨骼重置到初始姿态。
上半身叠加层:
skel:setUpperBodyBones(boneNames)
设置上半身骨骼名列表(string[])。
这些骨骼会被上半身叠加动画覆盖,其余骨骼保持基础动画。
skel:playUpperBodyAnim(animName)
播放上半身叠加动画(从 self.animations 中查找)。
仅影响已设置的上半身骨骼,下半身继续播放基础动画。
动画结束后自动停止。返回 bool。
skel:stopUpperBodyAnim()
手动停止上半身叠加动画。
面部动画:
skel:registerFaceAnimations(faceAnims)
注册面部动画数组。
skel:playFaceAnim(animName)
播放面部动画(叠加在身体/上半身之上)。返回 bool。
skel:stopFaceAnim()
停止面部动画。
朝向:
skel:setDirection(dir)
切换朝向 "front"/"back",自动切换所有骨骼附件。
查询:
skel:getAnimationNames()
返回所有已注册身体动画名的数组。
================================================================================
四、三层动画架构
================================================================================
┌─────────────────────────────────────────────────────┐
│ 第3层: 面部动画叠加层 │
│ 作用: 眨眼、说话、惊讶等表情 │
│ 模式: scaleX/scaleY 绝对值覆盖,其余增量叠加 │
│ 范围: 面部骨骼(眼、嘴) │
├─────────────────────────────────────────────────────┤
│ 第2层: 上半身叠加层 │
│ 作用: 行走/奔跑中攻击,拔剑,举盾等 │
│ 模式: 增量叠加,覆盖上半身骨骼 │
│ 范围: 由 setUpperBodyBones() 指定的骨骼 │
│ 特点: 播完自动停止,下半身保持基础动画 │
├─────────────────────────────────────────────────────┤
│ 第1层: 身体动画(基础层) │
│ 作用: 待机、行走、奔跑、跳跃等全身动作 │
│ 模式: 增量叠加到 setupPose │
│ 范围: 所有骨骼 │
└─────────────────────────────────────────────────────┘
执行顺序: 每帧先重置 → 身体层 → 上半身层覆盖 → 面部层覆盖 → 世界变换
典型用法:
· 站立攻击 = 全身播放 attack(不走叠加层)
· 行走中攻击 = 身体层 walk + 上半身层 attack
· 奔跑中攻击 = 身体层 run + 上半身层 attack
· 任何时候眨眼 = 面部层 blink(独立于身体/上半身)
================================================================================
五、通用数据格式
================================================================================
5.1 骨骼定义格式
────────────────────────────────────────────────────────────────────────────
单根骨骼定义:
{
name = "chest", -- 骨骼名(唯一标识)
parent = "root", -- 父骨骼名(nil = 根骨骼)
x = 0, y = -10, -- 相对父关节的局部偏移(像素)
rotation = 0, -- 局部旋转(弧度,默认 0)
scaleX = 1, scaleY = 1, -- 局部缩放(默认 1)
attachments = { -- 可视附件(多方向)
front = {
offsetX = 0, offsetY = -22, -- 附件中心相对骨骼关节
width = 60, height = 56, -- 附件绘制尺寸
radius = 4, -- 圆角半径
color = {55, 65, 100, 255} -- RGBA 颜色
},
back = { ... } -- 背面附件(颜色/形状可不同)
}
}
规则:
· 父骨骼必须在子骨骼之前定义
· 坐标系: Y 向下为正(屏幕坐标),X 向右为正
· attachments 支持 front/back 两套,由 setDirection() 切换
· 没有 attachments 的骨骼不会被绘制(纯逻辑节点)
5.2 身体动画格式(增量模式)
────────────────────────────────────────────────────────────────────────────
{
name = "idle",
duration = 2.4, -- 动画总时长(秒)
bones = {
骨骼名 = { -- 该骨骼的关键帧数组
{ time = 0.0, y = 0 },
{ time = 1.2, y = -2 }, -- 增量: setupY + (-2)
{ time = 2.4, y = 0 },
},
}
}
关键帧可控属性:
x 位移增量(像素)
y 位移增量(像素)
rotation 旋转增量(弧度)
scaleX 绝对缩放值(root 骨骼的 scaleX 会提升为全局 animScaleX)
scaleY 绝对缩放值
插值方式: 线性插值(lerp)
增量含义: 最终值 = setupPose值 + 关键帧值
零值含义: 关键帧值为 0 = 保持初始姿态
5.3 面部动画格式(绝对值模式)
────────────────────────────────────────────────────────────────────────────
{
name = "blink",
duration = 0.25,
loop = false, -- 是否循环
bones = {
left_eye = {
{ time = 0.0, scaleY = 1.0 }, -- 睁眼
{ time = 0.1, scaleY = 0.05 }, -- 闭眼
{ time = 0.25, scaleY = 1.0 },
},
}
}
与身体动画的区别:
· scaleY/scaleX 使用绝对值,直接覆盖骨骼缩放
· x/y/rotation 仍为增量模式
· 面部动画在所有其他层之后应用
5.4 手部附件格式(贴图替换方案)
────────────────────────────────────────────────────────────────────────────
HAND_VARIANTS = {
hand_L = {
open = {
front = { offsetX=0, offsetY=7, width=14, height=16, radius=3, color={...} },
back = { ... },
},
fist = {
front = { offsetX=0, offsetY=6, width=13, height=13, radius=6, color={...} },
back = { ... },
},
},
}
切换方式:
skel.attachmentSets[handName] = variants[state]
skel.bones[handName].attachment = variants[state][direction]
================================================================================
六、通用参数速查表
================================================================================
以下参数是经过实际测试验证的数值,可作为新动画的起始参考。
所有旋转值单位为弧度(π ≈ 3.14 = 180°,2π ≈ 6.28 = 360°)。
所有位移值单位为像素(相对骨骼初始位置的增量)。
6.1 身体动画参数参考
────────────────────────────────────────────────────────────────────────────
【待机呼吸 idle】 时长 2.4s / 循环
骨骼 属性 范围 说明
──────────────────────────────────────────────
chest y 0 ~ -2 胸部微升降
head y 0 ~ -1 头部跟随
arm_upper_L rotation 0 ~ 0.04 手臂微张
arm_upper_R rotation 0 ~ -0.04 对称
arm_lower_L rotation 0 ~ -0.03 前臂微动
arm_lower_R rotation 0 ~ 0.03 对称
【行走 walk】 时长 0.8s / 循环
骨骼 属性 范围 说明
──────────────────────────────────────────────
chest y 0 ~ -3 上下起伏
head y 0 ~ -1 跟随起伏
arm_upper_* rotation ±0.35 前后摆臂(左右反相)
arm_lower_* rotation -0.05 ~ -0.4 肘部弯曲
leg_upper_* rotation ±0.4 前后迈腿(左右反相)
leg_lower_* rotation 0.05 ~ 0.5 膝部弯曲
【奔跑 run】 时长 0.5s / 循环
骨骼 属性 范围 说明
──────────────────────────────────────────────
chest y 0 ~ -5 大幅颠簸
chest rotation 0.12(恒定) 身体前倾
head y 0 ~ -2 跟随颠簸
arm_upper_* rotation ±0.6 大幅摆臂
arm_lower_* rotation -0.1 ~ -0.6 肘部弯曲
leg_upper_* rotation ±0.7 大步幅
leg_lower_* rotation 0.1 ~ 0.8 膝部弯曲
【攻击 attack】 时长 0.5s / 单次
骨骼 属性 范围 说明
──────────────────────────────────────────────
arm_upper_R rotation 0 → -0.7 → 0.3 → 0 蓄力→挥出→回弹
arm_lower_R rotation 0 → -0.5 → 0.1 → 0 配合
chest rotation 0 → 0.1 → -0.05 → 0 躯干扭转
备注: 攻击支持上半身叠加,行走/奔跑中可叠加播放
【跳跃 jump】 时长 0.7s / 单次
骨骼 属性 范围 说明
──────────────────────────────────────────────
root y 0 → 6 → -30 → -28 → 0 蹲→腾空→滞空→落地
leg_upper_L rotation 0 → 0.4 → -0.3 → 0 蹲弯→腾空收
leg_lower_L rotation 0 → 0.6 → 0.3 → 0 小腿弯曲
leg_upper_R rotation 0 → -0.4 → 0.3 → 0 反相
leg_lower_R rotation 0 → 0.6 → 0.3 → 0 膝弯
arm_upper_L rotation 0 → 0.4 → -0.6 → 0 后摆蓄力→上摆
arm_upper_R rotation 0 → -0.4 → 0.6 → 0 反相
【旋转 spin】 时长 0.5s / 单次
骨骼 属性 范围 说明
──────────────────────────────────────────────
root rotation 0 → 3.14 → 6.283 整圈旋转(2π)
arm_upper_L rotation 0 → -0.8 → -0.8 → 0 离心展开
arm_upper_R rotation 0 → 0.8 → 0.8 → 0 对称
【跳劈 jump_slash】 时长 0.8s / 单次
骨骼 属性 范围 说明
──────────────────────────────────────────────
root y 0 → 5 → -25 → -20 → 0 蹲→跳→滞空→落地
arm_upper_R rotation 0 → -1.5 → 0.8 → 0 高举→劈下
arm_lower_R rotation 0 → -0.8 → -0.3 → 0 配合
arm_upper_L rotation 0 → 0.5 → 0 平衡手
leg_upper_* rotation ±0.3 蹲/收腿
chest rotation 0 → 0.15 → 0 前倾配合
6.2 面部动画参数参考
────────────────────────────────────────────────────────────────────────────
【眨眼 blink】 时长 0.25s / 单次
left_eye/right_eye: scaleY 1.0 → 0.05 → 1.0
【说话 talk】 时长 0.5s / 循环
mouth: scaleY 1.0 → 2.5 → 1.0 → 2.0 → 1.0
【惊讶 surprised】 时长 0.8s / 单次
left_eye/right_eye: scaleY 1.0 → 1.5(保持)→ 1.0
mouth: scaleY 1.0 → 3.0(保持)→ 1.0
6.3 参数调整规律总结
────────────────────────────────────────────────────────────────────────────
rotation 参考范围:
微动(呼吸) ±0.03 ~ 0.05
小幅(行走摆臂) ±0.3 ~ 0.4
中幅(奔跑摆臂) ±0.5 ~ 0.7
大幅(攻击挥砍) ±0.7 ~ 1.5
整圈旋转 6.283(2π)
y 位移参考范围:
微动(呼吸起伏) -1 ~ -3
中幅(奔跑颠簸) -5
蹲下蓄力 5 ~ 6(正值 = 下沉)
腾空跳跃 -25 ~ -30(负值 = 上升)
duration 参考范围:
快速动作(攻击) 0.3 ~ 0.5s
中速动作(跳跃) 0.6 ~ 0.8s
慢速循环(呼吸) 2.0 ~ 3.0s
移动循环(行走) 0.6 ~ 0.9s
移动循环(奔跑) 0.4 ~ 0.6s
左右交替动画编写口诀:
· L 和 R 同一属性的起始值互为反相(L=0.4 则 R=-0.4)
· 半周期位置值互换
· 小腿/前臂弯曲通常同向(都是正值弯曲)
================================================================================
七、当前人形角色骨骼数据
================================================================================
7.1 骨骼层级树(16 骨骼: 11 体 + 2 手 + 3 面)
────────────────────────────────────────────────────────────────────────────
root(腰部中心)
├── chest(胸部)
│ ├── head(头部)
│ │ ├── left_eye(左眼)
│ │ ├── right_eye(右眼)
│ │ └── mouth(嘴巴)
│ ├── arm_upper_L(左上臂)
│ │ └── arm_lower_L(左前臂)
│ │ └── hand_L(左手)
│ └── arm_upper_R(右上臂)
│ └── arm_lower_R(右前臂)
│ └── hand_R(右手)
├── leg_upper_L(左大腿)
│ └── leg_lower_L(左小腿)
└── leg_upper_R(右大腿)
└── leg_lower_R(右小腿)
7.2 骨骼数值表
────────────────────────────────────────────────────────────────────────────
骨骼名 父骨骼 局部X 局部Y 附件尺寸 附件偏移Y
──────────────────────────────────────────────────────────────────
root (根) 0 0 48×44 2
chest root 0 -10 60×56 -22
head chest 0 -40 56×56 -22
arm_upper_L chest -28 -38 24×48 20
arm_lower_L arm_upper_L 0 42 20×42 18
arm_upper_R chest 28 -38 24×48 20
arm_lower_R arm_upper_R 0 42 20×42 18
leg_upper_L root -12 18 30×56 26
leg_lower_L leg_upper_L 0 50 24×56 26
leg_upper_R root 12 18 30×56 26
leg_lower_R leg_upper_R 0 50 24×56 26
hand_L arm_lower_L 0 34 14×16 7
hand_R arm_lower_R 0 34 14×16 7
left_eye head -9 -30 6×6 0
right_eye head 9 -30 6×6 0
mouth head 0 -16 10×4 0
7.3 上半身骨骼集合(叠加层影响范围)
────────────────────────────────────────────────────────────────────────────
chest, head,
arm_upper_L, arm_lower_L, hand_L,
arm_upper_R, arm_lower_R, hand_R
7.4 绘制顺序
────────────────────────────────────────────────────────────────────────────
正面(后绘制 = 在上层):
后侧臂 → 后侧腿 → root → chest → 前侧腿 → 前侧臂 → head → 面部
背面:
head → 前侧臂 → 前侧腿 → root → chest → 后侧腿 → 后侧臂
================================================================================
八、已实现动画总览
================================================================================
身体动画(7 个):
──────────────────────────────────────────────
名称 时长 循环 说明
idle 2.4s 是 待机呼吸,胸部微升降 + 手臂微摆
walk 0.8s 是 行走,四肢交替 + 躯干起伏
run 0.5s 是 奔跑,大幅摆臂迈腿 + 身体前倾颠簸
attack 0.5s 否 右臂挥击 + 躯干扭转(支持上半身叠加)
jump 0.7s 否 蹲→蹬地→腾空→落地
spin 0.5s 否 原地整圈旋转(2π)+ 手臂离心展开
jump_slash 0.8s 否 跳起 + 空中右臂劈砍
面部动画(3 个):
──────────────────────────────────────────────
blink 0.25s 否 双眼快速闭合再睁开
talk 0.5s 是 嘴巴开合波动
surprised 0.8s 否 眼睛放大 + 嘴巴大张
手部状态(2 个):
──────────────────────────────────────────────
open -- -- 张开手掌(默认)
fist -- -- 握拳(附件替换)
叠加组合(已验证):
──────────────────────────────────────────────
walk + attack 行走中上半身攻击
run + attack 奔跑中上半身攻击
任意 + blink 随时眨眼
任意 + talk 随时说话
任意 + surprised 随时惊讶
================================================================================
九、Demo 操作说明
================================================================================
键盘快捷键:
1 = 待机 2 = 行走 3 = 奔跑 4 = 攻击
5 = 跳跃 6 = 旋转 7 = 跳劈
Q = 手张开 W = 握拳
Z = 眨眼 X = 说话 C = 惊讶
D = 正/背面切换 F = 水平翻转 J = 关节显隐
UI 按钮:
第一行: 待机 / 行走 / 奔跑 / 攻击 / 跳跃 / 旋转 / 跳劈
第二行: 张开 / 握拳 / 眨眼 / 说话 / 惊讶 / 正背 / 翻转 / 关节
自动行为:
· 随机眨眼: 无面部动画时,每 2~5 秒自动触发
· 自适应缩放: 根据屏幕尺寸自动调整角色大小
· 行走/奔跑中点攻击 → 自动切换为上半身叠加攻击
================================================================================
十、扩展指南
================================================================================
10.1 添加新身体动画
────────────────────────────────────────────────────────────────────────────
步骤:
1. 在 ANIMATIONS 表中添加新条目
2. 参考第六节参数表设定初始值
3. 在 bodyAnims / loopAnims 查找表中注册
4. 添加 UI 按钮和键盘快捷键
5. 构建测试
示例(防御动作):
{
name = "defend",
duration = 0.3,
bones = {
arm_upper_L = {
{ time = 0.0, rotation = 0 },
{ time = 0.15, rotation = -0.8 }, -- 举臂格挡
{ time = 0.3, rotation = -0.8 }, -- 保持
},
chest = {
{ time = 0.0, rotation = 0 },
{ time = 0.15, rotation = -0.05 }, -- 微后仰
{ time = 0.3, rotation = -0.05 },
},
}
}
10.2 创建不同角色(复用同一引擎)
────────────────────────────────────────────────────────────────────────────
只需提供不同的骨骼数据和动画数据:
local warrior = Skeleton.new(WARRIOR_BONES, WARRIOR_ANIMS)
local mage = Skeleton.new(MAGE_BONES, MAGE_ANIMS)
local goblin = Skeleton.new(GOBLIN_BONES, GOBLIN_ANIMS)
不同角色可共用相同骨骼结构(只改尺寸/颜色),
也可使用完全不同的骨骼结构。
10.3 创建非人形骨骼(怪物/生物)
────────────────────────────────────────────────────────────────────────────
引擎支持任意骨骼树结构,非人形示例:
【史莱姆】极简骨骼 + scaleX/scaleY 挤压拉伸
root → body
动画: scaleX 1.2→0.8 + scaleY 0.8→1.2 = 果冻弹跳
【巨龙】多分支骨骼树
spine → chest → neck → head → jaw
→ wing_L_1 → wing_L_2 → wing_L_3
→ wing_R_1 → wing_R_2 → wing_R_3
→ tail_1 → tail_2 → tail_3
→ leg_FL / leg_FR / leg_BL / leg_BR
【蛇】链式骨骼
head → spine1 → spine2 → spine3 → spine4 → tail
动画: 每节 rotation 依次递增相位差 = 波浪扭动
关键: Skeleton.lua 不关心骨骼叫什么名字、有多少根,
只要数据格式正确就能驱动。
10.4 武器挂载(待实现)
────────────────────────────────────────────────────────────────────────────
方案: 在手部骨骼上添加"装备槽"附件
思路:
· 武器作为特殊附件挂载到 hand_R 或 hand_L
· 武器附件的 offsetX/offsetY 控制握持位置
· 武器附件的绘制在手部附件之后(在上层)
· 拔剑/收剑 = 切换手部附件状态(类似 open/fist 切换)
需要扩展:
· 附件系统支持多个附件同时显示(手 + 武器)
· 或使用子骨骼方案(hand_R → weapon_R)
10.5 贴图渲染(替换纯色矩形)
────────────────────────────────────────────────────────────────────────────
当前: drawBoneRect() 用纯色圆角矩形
目标: 替换为 nvgImagePattern 贴图填充
步骤:
1. 为每个骨骼准备贴图文件(按部位切图)
2. 在 Start() 中用 nvgCreateImage() 加载(只调用一次)
3. 在 drawBoneRect() 中判断是否有贴图:
有 → nvgImagePattern 填充
无 → 退回纯色矩形
注意:
· 建议先生成全身参考图,再分部位切图,确保风格一致
· 正面/背面需要两套贴图
================================================================================
十一、项目方向
================================================================================
类型: 横版冒险格斗游戏
画风: 二次元动漫卡通 Q 版 / RPG 冒险风格
头身比: 3 头身(已确定 2026-03-04)
屏幕: 锁定横屏
3 头身特点:
· 头部占整体高度的 1/3
· 经典 Q 版比例,可爱但四肢有足够长度表现格斗动作
· 适合横版冒险格斗的动作表现力需求
待确定事项:
· 角色基础尺寸和骨骼比例(根据 3 头身调整)
· 战斗系统核心机制
· 具体角色设计(主角、怪物等)
===============================================================



