Appearance
动画系统职责三分与行为模型总结
本文是对当前讨论中逐步沉淀出的动画系统设计思想的系统化整理,目标是:
- 明确职责边界,避免状态污染
- 区分声明、语义、运行时三个层级
- 用 BT(行为树)视角统一理解动画与行为
一、总体设计目标
我们希望构建的是一套:
- 可声明(Declarative):动画如何发生,而不是每一帧怎么改值
- 可推导(Derived):所有动态结果都应由函数计算得出
- 无污染(Stateless Runtime):运行时不维护“额外真相”
- 可解耦(Decoupled):配置、语义、执行三者相互独立
核心思想一句话概括:
状态是输入,行为是规则,结果永远是算出来的。
二、职责三分法(Three-Layer Responsibility Model)
1️⃣ Animation Config(声明层)
它是什么?
动画“意图”的描述文件,是一种 配置 / 数据结构。
它负责什么?
- 描述有哪些对象(hero / enemy / parts)
- 描述对象有哪些可被行为作用的属性声明
- 描述时间轴(timeline)与事件(events)
它不负责什么?
- ❌ 不做任何计算
- ❌ 不存储运行时数据
- ❌ 不关心逐帧变化
Config 的本质
Config 不是状态,而是“可被求值的前提条件集合”。
它只说明:
- 有哪些可能(capability)
- 在什么条件下(when)
- 用什么参数(params)
2️⃣ Animation Library(语义层)
它是什么?
一套稳定、可复用的行为语义定义集合。
它负责什么?
- 定义行为类型(move / rotate / fade ...)
- 定义参数结构(schema)
- 提供求值函数(evaluator)
它的关键特性
- 行为是纯函数语义
- 不依赖具体动画实例
- 不随动画运行而变化
Behavior Declaration 示例(概念)
- 行为类型:
move - 所需参数:
targetX, duration, easing - 求值规则:
- 输入:起始值、时间、参数
- 输出:某一时刻的结果值
Library 决定“怎么算”,而不是“什么时候算”。
3️⃣ Loader / Runtime(运行时层)
它是什么?
动画的执行引擎,负责把声明 + 语义“跑起来”。
它负责什么?
- 维护世界状态(World State)
- 根据 timeline / event 创建行为实例
- 在每一帧进行求值并渲染
World State 的原则
- 只存储最小真实状态(如 time、输入信号)
- 不存“结果性状态”(如 x 是多少)
World State 是输入源,不是结果缓存。
三、参数维度的统一理解
我们区分参数的两个正交维度:
1️⃣ 动态参数 vs 静态参数
| 类型 | 含义 |
|---|---|
| 静态参数 | 不随时间变化(或不被行为作用) |
| 动态参数 | 会被行为求值函数计算 |
是否“动态”,取决于是否由 evaluator 计算,而不是是否写在 config 里。
2️⃣ 常态参数 vs 非常态参数
| 类型 | 含义 |
|---|---|
| 常态参数 | 对该对象长期成立的能力或默认值 |
| 非常态参数 | 仅在某次行为实例中存在 |
关键结论
- 常态参数 → 放在 Config 中作为声明
- 非常态参数 → 放在 Behavior Instance 中作为一次性输入
Config 只描述“可能性”,Instance 才携带“这一次”。
四、Timeline 与 Event 的正确理解
Timeline 的本质
json
{ "at": 0, "behavior": "move", "params": { ... } }并不是“初始状态”,而是:
在某个时间点,满足条件时,创建一个行为实例。
行为的生命周期
- 到达时间点 / 触发事件
- Loader 创建 Behavior Instance(快照)
- 每一帧由 evaluator 求值
- 行为自然结束或被更高优先级行为覆盖
行为不是“切换状态”,而是“被选择执行”。
五、FSM 与 BT 的核心差异(结合动画语境)
一句话区分
FSM 管“我现在是什么状态”
BT 管“我现在要不要做这件事”
场景一:鱼的整体运动
- 默认:绕点转圈
- 点击:摆尾 → 移动 → 围绕新点
FSM 思路
- Orbit → TailWhip → Move → Orbit
- 行为被强制串成状态
BT 思路
- 如果有点击 → 执行点击序列
- 否则 → 执行默认绕圈
绕圈是 fallback 行为,而不是状态。
场景二:尾巴行为(并行维度)
- 默认慢摆
- 点击快摆
- 快速移动时不摆
FSM 的问题
- 需要额外 FSM
- 状态组合爆炸
BT 的优势
text
Priority Selector
├─ 如果正在快速移动 → 不摆尾
├─ 如果有点击 → 快速摆尾
└─ 默认 → 慢摆尾巴不是状态,而是持续决策。
六、与你当前模型的对应关系
| 你的概念 | BT 对应 |
|---|---|
| World State | 黑板(Blackboard) |
| Behavior Declaration | 行为节点定义 |
| Evaluator | 节点执行逻辑 |
| Timeline / Event | 条件节点 |
| 无污染运行时 | 每帧重新决策 |
核心共识
所有“结果”都应该是被计算出来的,而不是被维护的。
七、最终总结
Config:声明可能性
Library:定义语义与规则
Runtime:在时间中反复求值
FSM 适合阶段剧情
BT 适合持续动画与角色行为
而你正在构建的系统,本质上是:
一个用动画语言实现的行为树执行器。
只是你是从工程直觉,而不是从 AI 教科书出发走到这里的。
这不是绕路,这是更干净的那条路。