运动匹配
Motion Matching
运动匹配 (Motion Matching) 是 Pose Search 插件提供的动画蓝图节点,可以作为状态机和混合空间的动态替代:每次用 Schema 定义的通道(骨骼位置/速度、轨迹采样点等)构造查询,在数据库里的大量动画中找出代价最低的姿势,而不需要手工编写过渡和混合逻辑。增加动画数据就能提高保真度;代价是内存和查询开销会随通道、采样数和采样率增加。
原理
- Motion Matching 节点通过 Schema 从角色身上查询各个通道(例如某根骨骼的位置和速度),然后从 Database 资产中的动画数据里选出与查询最匹配的姿势。与传统系统不同,它无需在动画序列之间设置过渡或混合逻辑,结果比传统状态机或混合逻辑响应更快、更能跟上玩法;要提高保真度,只需往数据库里加动画,不必改过渡逻辑。(Motion Matching in Unreal Engine)传统方案见 动画蓝图与动画系统。
- 代价函数:Schema 把一次查询的代价拆成若干通道,例如采样骨骼的位置、速度或肢体相位;一次查询对数据库中某个已索引姿势的总代价,是各通道代价之和,每个通道有一个权重。(Motion Matching in Unreal Engine)
用式子表示(由官方「总代价 = 各通道代价之和、每个通道有权重」直接写出): 其中 是通道 的权重, 是查询与候选姿势在该通道上的差异;选择代价最小的姿势。
flowchart LR
CH["角色当前状态<br/>+ 运动轨迹组件预测的轨迹"] --> Q["按 Schema 构造查询<br/>Trajectory 通道 + Pose 通道"]
Q --> S["在 Database 中搜索<br/>加权代价最小的姿势"]
DB[("Pose Search Database<br/>大量动画序列")] --> S
S --> O["从该姿势开始播放并混合"]图中角色的状态和预测轨迹被组织成查询,在数据库里搜索代价最小的帧并从那里播放。
资产与配置
| 资产/通道 | 作用 |
|---|---|
| Pose Search Schema | 保存运动匹配配置和查询设置,把数据库和查询系统连到 Motion Matching 节点;新建时默认是运动配置:Trajectory 和 Pose 两个通道 |
| Trajectory 通道 | 在 Motion Trajectory 组件上设置采样点,每个点用 Flags 决定查询哪类数据 |
| Pose 通道 | 用任意骨骼定义姿势匹配的参考点 |
| Database | 存放候选动画 |
| Mirror Data Table | 提供镜像动画,扩大候选集 |
(Motion Matching in Unreal Engine)还可以选 Use Continuing Pose:把角色当前正在播放的「延续姿势」作为查询输入,或者与延续姿势插值后作为输入。
取舍与坑
- 采样点越多,数据越精确,但官方提醒也可能扭曲结果;通道和采样越多,内存和性能开销越大;Sample Rate 越高,搜索越精细,但内存占用越大。(Motion Matching in Unreal Engine)
- Add Data Padding 会把数据按 16 字节对齐以提升性能,代价是额外内存;权重会归一化,也可以选择只按偏差归一化。(Motion Matching in Unreal Engine)
- 运动匹配的质量依赖动画数据的覆盖范围,以及轨迹预测与实际移动是否一致(移动见 角色移动组件与 Mover;这一关联是归纳)。
GEA 的视角:运动匹配的动机、前身与实现(卷 II 13.12)
为什么需要:GEA 认为真实游戏的动作状态机 (ASM) 很快会变得庞大复杂:片段要满足核心姿势匹配、循环相位匹配等约束,过渡窗口要手工标注,大量不直观的控制参数要暴露给引擎,还要写时序精确的控制逻辑。即使做完,仍可能有跳变、滑步和僵硬(卷 II 13.12.1)。传统做法见 动画管线与动作状态机。
- 例:让奔跑的角色停下,要等跑步循环的姿势对上"跑到停"过渡片段的首帧才能开始混合,同时减速,最后混入待机;如果还要停在梯子底部,就得接受滑步"作弊",或预测性地提前减速。弯道奔跑时真人会向内倾、脚甩出重心之外,用混合很难复现(卷 II 13.12.1.1)。
- 逻辑 ASM 与物理 ASM:玩法要求(如冲刺到静止不超过 T 秒、x 米)和生物力学的自然性互相冲突。一种办法是建两套状态机:逻辑 ASM 只有"移动、跳、落、攀、死"等粗粒度状态,由玩家输入或 AI 驱动;每个逻辑状态对应一个复杂得多的物理 ASM,负责生物力学上正确的运动以及逻辑状态之间的混合。运动匹配就是构建物理 ASM 的更好方法:搭建更省力、不易出错,更符合生物力学,响应快,还能满足沿路径行进、精确停点等玩法约束(卷 II 13.12.1.2)。
前身(卷 II 13.12.2):
| 技术 | 思路 | 问题 |
|---|---|---|
| 运动图 (Motion Graphs,Kovar、Gleicher、Pighin,SIGGRAPH 2002) | 片段放进数据库,手工标注到其他片段的过渡(常受时间窗口限制,如左/右脚着地窗口),在图上"行走" | 仍要等过渡窗口,响应慢;标注工作量大 |
| 运动场 (Motion Fields,Lee 等,2010) | 高维向量场:每点是一种身体构形(姿势 + 关节速度),向量表示可行的运动方向,沿场中路径移动,可随时转向相邻点 | 响应快,但数学复杂、不直观 |
起源:运动匹配继承运动场的核心思想,但更直观、数学更少。由育碧多伦多团队(Michael Buttner、Mike Wasilewski、Alex Bereznyak、Betina Marquis、Kristjan Zadziuk)开发,Kristjan 在 GDC 2016 介绍;首个商业应用是育碧蒙特利尔的《荣耀战魂》(Simon Clavet 同在 GDC 2016 介绍),之后包括 Naughty Dog 的《最后生还者 第二部》(GDC 2021)。UE5 提供运动匹配,Unity 也有扩展(卷 II 13.12.3)。
数据库的特征向量:每个片段的每一帧存一个较小的向量:根关节的局部位置与速度;脚踝、大腿、手腕、脊柱等少数关键关节的局部位置与速度;局部原点过去和未来一小段(如前后各一秒)的轨迹;可选的标签(片段用途、何时可播)。每帧用当前构形和期望的未来轨迹(玩家输入或 AI 路径)搜索整个数据库,若命中当前片段就继续播,否则立即混入新片段的选中帧,不必等过渡窗口或手工搭过渡图(卷 II 13.12.3)。这与本页 UE5 的 Trajectory 通道 + Pose 通道、加权通道代价求和是同一套思路。
舞蹈卡 (dance card):数据库仍要包含手搭物理 ASM 所需的各类动作(待机、走、跑、横移、转身、各种过渡),想支持疲惫或受伤就要再来一整套。常在动捕棚让演员按精心设计的动作序列表演,保证每种需要的动作至少有一个样例,可以录成一长段或许多短段(卷 II 13.12.4)。
算法(GEA 依据 Clavet 的 GDC 演讲给出伪代码):先正常推进当前片段并取当前姿势;遍历数据库中每一帧(即每个姿势)计算代价,保留最低者;若最优姿势不是当前片段的当前帧,就混合过去。代价 = 当前代价 + responsiveness × 未来代价:
float ComputeCost(Pose curPose, Pose candidatePose, Goal goal, float responsiveness) {
return ComputeCurrentCost(curPose, candidatePose)
+ responsiveness * ComputeFutureCost(candidatePose, goal);
}
struct TrajectoryPoint { Point m_position; Quat m_orientation; float m_timeOffset; }; // 正为未来、负为过去
class Goal {
static constexpr int kTrajCapacity = 4; // 任意,通常 < 8
TrajectoryPoint m_desiredTrajectory[kTrajCapacity];
Vector m_desiredVelocity; // 其他期望条件
};
(上面的代码按书中意图整理了几处笔误,见下。)
实践中的问题(卷 II 13.12.5):
- 很难让算法稳定地沿时间单调播放同一片段,它有选回已播过的帧的倾向,需要人为降低当前片段后续帧的代价(加偏置)。
- 对动画师不友好:大量数据倒进一个"黑箱",选择依据的关节位置、速度和轨迹难以可视化和理解。
- 动画、匹配特征和代价函数都需要细致调校。GEA 推荐 GDC 2016 的两场演讲和 GDC 2021 的《最后生还者 第二部》演讲。
GEA 原书中的问题:13.12.4 的伪代码有几处不一致:类成员是 m_pClipDatabase,循环却遍历未声明的 m_poseDatabase;ComputeCost 定义为 4 个参数(含 responsiveness),调用时只传 3 个;函数体中 curPos 应为 curPose,cost 未声明类型;constexpr kTrajCapacity 缺少类型(而且非静态数据成员不能声明为 constexpr,需写成 static constexpr int,见 cppreference “constexpr specifier”)。(此前本页另列过“IsSameClipAndFrame 判断被排到 MMUpdate 大括号之外”一条,复核原文的大括号结构后该判断确在函数内,已撤回。)
出处
- Motion Matching in Unreal Engine UE 5.8 文档
- Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
- Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 13 章 13.12 节