动作融合

只要有playercontroll的游戏都会有人物动作,比如idel、walk、run等,动画师只会针对每种动作做一系列动画,一组动画称为一组clips,里面k的每帧动画称为一个clip。

当我们按下方向键时,希望看到角色是从一个动作到另一个动作自然过度的,可是动画师并没有做过度动画,这时就需要“动作融合”。

动作融合,简单来说需要2步插值,第一步是两个动作每个clip分别插值,第二步是根据deltaTime在时间线上找到融合后的2个clip插值。

以“走”和“跑”2个动作融合为例

【准备】

1.动画师准备好walk-clips和run-clips。帧数需要一样,比如都是58帧,即58个clip,且间隔比例要一直;长度可以不同,比如walk一个循环是5秒,run开一个循环是10秒。

2.物理系统返回每帧deltaTime,例如1秒30帧,返回的deltaTime大概是0.33s。

【第一步】

当玩家按下跑步键,角色从走路变成跑步的过程中,物理系统返回speed会不断发生改变。

每次speed改变,都重新计算权重。

根据权重对动作AB的每个骨骼的58个clip分别做插值,插值的属性包括骨骼的translate/scale/rotation,translate和scale用线性插值lerp,rotation用四元数插值nlerp。

 

 

 

 

void AnimationPose::blend(const AnimationPose& pose)
{
    for (int i = 0; i < m_bone_poses.size(); i++)
    {
        auto&       bone_trans_one = m_bone_poses[i];
        const auto& bone_trans_two = pose.m_bone_poses[i];

        float sum_weight = m_weight.m_blend_weight[i] + pose.m_weight.m_blend_weight[i];
        if (sum_weight != 0)
        {
            float cur_weight = pose.m_weight.m_blend_weight[i]; // 动作2权重
            m_weight.m_blend_weight[i] = 1.0f - cur_weight;     // 动作1权重
            bone_trans_one.m_position = Vector3::lerp(bone_trans_one.m_position, bone_trans_two.m_position, cur_weight);
            bone_trans_one.m_scale     = Vector3::lerp(bone_trans_one.m_scale, bone_trans_two.m_scale, cur_weight);
            bone_trans_one.m_rotation  = Quaternion::nLerp(
                cur_weight, bone_trans_one.m_rotation, bone_trans_two.m_rotation, true);
        }
    }
}

【第二步】

每帧输入deltaTime根据此刻的物理速度、权重,分别算出walk和run的deltaTime(或者算出融合后的deltaTime)。

根据deltaTime选取2个融合后的clip做插值。

 

 

 

 至此,就能自然的表现从“走”到“跑”的过度了。

 

posted @ 2022-08-07 20:35  银龙背上的骑士  阅读(247)  评论(0编辑  收藏  举报