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 头身调整)
    · 战斗系统核心机制
    · 具体角色设计(主角、怪物等)
===============================================================
猜你想搜
taptap 制造 skeletonlua 使用指南
16
10
6