角色动画蓝图 Animation Blueprint

773 阅读5分钟

写在前面

现在,我们的角色已经可以自由移动了,但是我们希望角色可以在我们按下控制键的时候有相应的动作。

我还记得之前在写网页端的弹框变化时,我们需要根据用户的选择步骤来变换弹框的样式和显示的内容,逻辑很简单,设定多个状态参数对应不同的弹框样式,然后在某个契机改变我们的状态参数,让相应的弹框样式显示出来即可。

UE5的动画的原理也是类似这样,只不过我们从简单的参数和静态的css样式变成了多个参数和动漫序列。设置state machine(离散性状态,如idle/jump)和blend space(连续性状态,比如从慢跑过渡到快跑,比如从向右跑过渡到向左跑),在不同的状态,我们播放相应的animation。

接下来我们要开始实现角色的基本移动动画,有了这些动画后,我们就可以用一个移动的小人来模拟玩家测试游戏了。

在C++类中建立状态

建立一个新的animinstance类,该类可以作为AnimationBluePrint的基类来使用。

// header
class BLASTER_API UBlasterAnimInstance : public UAnimInstance
{
	GENERATED_BODY()

public:
	// 在动画刚建立时调用
	virtual void NativeInitializeAnimation() override;
	// 在更新动画时调用,相当于Tick
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

private:
	UPROPERTY(BlueprintReadOnly, Category = Character, meta = (AllowPrivateAccess = "true")) // 因为这是private的,所以必须写上AllowPrivateAccess才能在蓝图中访问
	class ABlasterCharacter *BlasterCharacter;

	UPROPERTY(BlueprintReadOnly, Category = Character, meta = (AllowPrivateAccess = "true"))
	float Speed; // 根据速度,来运动的快慢

	UPROPERTY(BlueprintReadOnly, Category = Character, meta = (AllowPrivateAccess = "true"))
	bool bIsInAir; // 是否在空中,用来判断是否跳跃

	UPROPERTY(BlueprintReadOnly, Category = Character, meta = (AllowPrivateAccess = "true"))
	bool bIsAccelerating; // 是否在加速
};
void UBlasterAnimInstance::NativeInitializeAnimation()
{
    Super::NativeInitializeAnimation();

    BlasterCharacter = Cast<ABlasterCharacter>(TryGetPawnOwner());
}

void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
    Super::NativeUpdateAnimation(DeltaSeconds);

    if (BlasterCharacter)
    {
        BlasterCharacter = Cast<ABlasterCharacter>(TryGetPawnOwner());
    }

    if (!BlasterCharacter)
    {
        return;
    }

    // 获取速度
    auto charVelocity = BlasterCharacter->GetVelocity();
    charVelocity.Z = 0;             // 只要平地上的运动
    Speed = charVelocity.Size();    // 获取速度
    bIsInAir = BlasterCharacter->GetMovementComponent()->IsFalling(); // 是否在下坠 以此判断是否在空中
    bIsAccelerating = BlasterCharacter->GetCharacterMovement()->GetCurrentAcceleration().Size() > 0 ? true : false; // 并非是物理学上的加速度,而是指玩家的输入
}

基于C++类建立动画蓝图

首先,在要建立的地方单击右键,选择动画蓝图, 建立动画蓝图。

在这里我们需要选择一个父类(选择我们自己刚刚已经写好的)和一个skeleton(你的动画绑的是哪个那就用哪个,注意和skeleton mesh文件区别)。

image.png

image.png

接下来我们进入动画蓝图编辑界面的AnimeGraphtab,右键,建立第一个状态机来处理角色的常规动画。

image.png

点击进入Unequiped状态机,建立其以下状态,状态的数量大体和你有多少动画序列成正比。以模拟角色在普通状态下的动画转变。

image.png

观察上图,每个状态节点用同心矩形来表示,在上面我们定义动画的展示;每个定义了一个状态改变的行为(往往是一个bool的检查)。总体而言,整个状态机是一个有向图,每个状态之间只要你想,就可以互相转换。

image.png

首先我们得从双脚着地双脚离体,很简单,判断是否IsInAir中即可。

之后我们从Jump直接过渡到下坠状态。

image.png

勾上这个选项后,代表Jump的动画放完后,直接进入Falling开始播放Falling动画.

那么接下来,我们要着陆了,那就简单了,直接判断是否在空中即可。

image.png

因为我们的着陆动画只播放一次,因此我们记得把loop给取消。

接着,我们使用TimeRemainingRatio <= 0.1作为触发条件,确保在着陆动画的最后10%的时间内,才开始准备过渡到idle状态。这通常是动画的“安静期”或“收尾动作”,这样可以避免中断动画的关键部分,使得动作看起来不自然或突兀。

image.png

接着,我们给每个state加上合适的动画。

然后,对于idle状态,因为对应着原地小跑涉及到了多个状态之间的平滑过渡,所以我们要将这些动画blend在一起,所以会用到混合空间。因为我们只想通过速度来控制跑步,因此,我们只需要1D。

image.png

首先,我们先看这个过度条组件: image.png 我们可以在不同的过度点添加动画节点。按住ctrl拖动X查看过度。

接着我们可以通过设置Horizontal Axis的设置来改变这个进度条属性。

image.png

在完成blend space之后,我们发现ue非常智能得替我们创建了多个动画之间的补间动画,动画之间变得很自然。(^_^) 简直是加强版Live2D。

好了,这样一来我们的idle state也有着落了。

注意

  • 如果发现角色在落地时缩小或消失,可能是这个问题:

Character shrinks and disappears when jumping - Development / Character & Animation - Epic Developer Community Forums (unrealengine.com)

  • 暂未解决的问题,角色下落会滑翔,但是如果在跳跃时没有按住按钮就不会滑翔。

经过一下午的测试,我发现之所以在滑翔是因为角色在做下落动作还无法进入到idle奔跑状态,感觉可以加个逻辑,只有等动画放完后,角色的移动才能够继续进行,这本质是一个异步问题,打算再研究下。