Motion Matching
About 2 min
Motion Matching
播放动画
一段播放动画的代码:
// start
if (!walking && wantToWalk)
{
PlayAnim(StartAnim);
walking = true;
}
// walk loop
if (IsPlaying(StartAnim) && IsAtEndOfAnim())
{
PlayAnim(WalkLoopAnim);
}
// stop
if (walking && !wantToWalk)
{
PlayAnim(StopAnim);
walking = false;
}
同样可以使用状态机,可视化:
但如果动画过多就会出现下图:
当然还有混合树的情况播放动画:
if (speed > 3.0f)
{
PlayAnim(RunAnim);
}
else if (speed > 0.0f)
{
PlayAnim(WalkAnim);
}
else
{
PlayAnim(IdleAnim);
}
同样也可以使用可视化工具来处理:
多个参数控制动画
如果有多个数据都会影响着动画的播放,就是需要配表来控制标准选择动画,然后实时传递值给动画控制器。这个逻辑就是动画蓝图的功能。
改变固定动画播放路径
选择下一个动画
Reinforcement Learning Based Character Locomotion in Hitman: Absolution
Motion Fields for Interactive Character Animation
Motion Fields 性能消耗非常大,这里只使用了跳转到其他动画的任何一帧。
选择正确的开始和结束时间
- 姿势匹配
- 速度匹配
- 精确的末端位置匹配
原理简介
核心逻辑:每一帧都去寻找最合适的动画,然后跳转播放。
Mocap
先做动作捕捉
Code
下面是每帧计算匹配的简单代码:
int m_CurrentAnimIndex;
int m_CurrentAnimTime;
void AmoUpdate(Goal goal, float dt)
{
m_CurrentAnimTime += dt;
Pose currentPose = EValuateLerpedPoseFromData(m_CurrentAnimIndex, m_CurrentAnimTime);
float bestCost = 1000000;
Pose bestPose;
// loop on all mocap
for (int i = 0; i < m_Poses.Size(); i++)
{
Pose candidatePose = m_Poses[i];
// every candidate jumping point has a cost
float thisCost = ComputeCost(currentPose, candidatePose, goal);
if (thisCost < bestCost)
{
// remember the best candidate
bestCost = thisCost;
bestPose = candidatePose;
}
}
bool theWinnerIsAtTheSameLocation = m_CurrentAnimIndex == bestPose.m_AnimIndex && fabs(m_CurrentAnimTime - bestPose.m_AnimTime) < 0.2f;
if(!theWinnerIsAtTheSmaeLocation)
{
// blend to the winning location
m_CurrentAnimIndex = bestPose.m_AnimIndex;
m_CurrentAnimTime = bestPose.m_AnimTime;
PlayAnimStartingAtTime(m_CurrentAnimIndex, m_CurrentAnimTime, 0.25f);
}
}
Trick 1: Posematch only a few bones
- Local velocity
- Feet positions
- Feet velocities
- Weapon position
Trick 2: Just check where a piece of animation brings you if you play it
动画的目标,部分代码:
class TrajectoryPoint
{
Vector3 m_Position;
float m_Sight;
float m_TimeDelay;
};
// desired goal, sent by gameplay each frame
class Goal
{
Array<TrajectoryPoint> m_DesiredTrajectory;
Stance m_DesiredStance;
// ...
};
计算调整到新的姿势的花销的代码:
float ComputeCost(Pose currentPose, Pose candidatePose, Goal goal)
{
float cost = 0.0f;
// how much the candidate jumping position matches the current situation
cost += ComputeCurrentCost(currentPose, candidatePose);
// this is our responsivity slider
static float reponsivity = 1.0f;
// how much the candidate piece of motion matches the desired trajectory
cost += responsivity * ComputeFutureCost(candidatePose, goal);
return cost;
}
Match more things
- Future Stance Matching
- Elegantly find transitions when they exist
Optimizations
- LOD
- KD-Tree
- Motion Shaders
Trajectory Simulation Choices
- Displacement from Animation?
- Displacement from Simulation? 更可控,可预测
Workflow
Procedural Touchups
Reference
- https://gdcvault.com/play/1023280/Motion-Matching-and-The-Road
- https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc2016/Presentations/Clavet_Simon_MotionMatching.pdf
- https://research.cs.wisc.edu/graphics/Papers/Gleicher/Mocap/mograph.pdf