UE动画学习之路
动画蓝图与处理角色死亡
1.在UE界面输入Ctrl + P 查看资产
2.打开角色的动作蓝图中可以看到为角色设置动画的各种蓝图已经被封装起来了
3.使用SavedPose可以将动作保存起来,实现反复利用
- Blend Poses by bool 可以实现通过条件来展示不同的动画
- 创建名为bIsAlive的变量,使用这个Boolean变量 可以使动画根据不同的条件展示出来
- 使用Get Component by Class
- 完成其他节点的连接
- 补充代码
//SAttributeComponent.h
UFUNCTION(BlueprintCallable)
bool IsAlive() const;
//SAttributeComponent.cpp
bool USAttributeComponent::IsAlive() const
{
return Health>0.0f;
}
//SCharacter.h
UFUNCTION()
void OnHealthChanged(AActor* InstigatorActor, USAttributeComponent* OwningComp, float NewHealth,float Delta);
virtual void PostInitializeComponents() override;
//SCharacter.cpp
void ASCharacter::OnHealthChanged(AActor* InstigatorActor, USAttributeComponent* OwningComp, float NewHealth,
float Delta)
{
if(NewHealth <= 0.0f && Delta <0.0f)
{
APlayerController* PC = Cast<APlayerController>(GetController());
DisableInput(PC);
}
}
void ASCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
AttributeComp->OnHealthChanged.AddDynamic(this,&ASCharacter::OnHealthChanged);
}
- 最后的成品,当我们的生命值归0时,我们无法进行任何操作
添加攻击动画
1. 添加动画
我们在之前的课程中实现了从角色右手发射魔法粒子,但角色并没有播放相应的动画,导致角色攻击看起来十分生硬。
在我们引入的资产Gideon角色中,开发者已经做了充分的工作,使我们可以十分简单地调用函数来直接播放动画。要让骨骼体动起来,就需要使用动画序列,它是可在骨架网格体上播放的单个动画资源。这些序列包含各个关键帧,依次回放这些关键帧(相互合成)就可以实现骨骼体动画。而UE中控制动画播放的系统称为 蒙太奇(Montage),它可以分别通过蓝图或C++来控制动画序列播放。
添加下列代码
// SCharacter.cpp -> PrimaryAttack()
PlayAnimMontage(AttackAnim);
// SCharacter.h
UPROPERTY(EditAnywhere,Category = "Attack")
UAnimMontage* AttackAnim;
UPROPERTY(EditAnywhere,Category = "Attack")
TSubclassOf<AActor> ProjectileClass;
然后在UE中给AttackAnim选择任意动画即可。需要注意的是,我们在使用UPROPERTY宏时加上Category,可以使相关的变量在蓝图编辑器中显示在Attack类别下。
运行测试,可以发现动画可以正常播放,不过魔法粒子发射的位置还是角色在静止状态下右手的位置,而没有从角色抬手后的手掌中发出来。
产生这个问题的原因,在于计算粒子起点的时候,即执行 FVector RightHandLoc = GetMesh()->GetSocketLocation(“Muzzle_01”); 这条语句时,角色才刚刚开始播放动画,得到的位置自然在下方。解决方法也很简单,根据动画的速度在这条语句前设置相应延迟即可,所以我们利用UE中的计时器来实现这个效果:
void ASCharacter::PrimaryAttack()
{
PlayAnimMontage(AttackAnim);
GetWorldTimerManager().SetTimer(TimerHandle_PrimaryAttack,this,&ASCharacter::PrimaryAttack_TimeElasped,0.2f);
}
void ASCharacter::PrimaryAttack_TimeElasped()
{
FVector HandLocation = GetMesh()->GetSocketLocation("Muzzle_01");
FTransform SpawnTM = FTransform(GetControlRotation(),HandLocation);
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnParams.Instigator = this;
GetWorld()->SpawnActor<AActor>(ProjectileClass,SpawnTM,SpawnParams);
}
要注意传入的定时器句柄TimerHandle_PrimaryAttack是FTimerHandle类,需要在SCharacter.h文件中声明。只要定时器句柄存在,过0.2s后就会执行PrimaryAttack_TimeElapsed()中发射粒子的代码。这样做的好处是,当角色在攻击的一瞬间死亡时,其TimerHandle_PrimaryAttack将会自动取消,那之后的动画也就不会再播放。
优化宝箱蓝图
1. 优化宝箱打开动画
打开宝箱TreasureChest的蓝图,在事件图表中添加Interact接口的实现,编译保存。此时运行关卡尝试打开宝箱,发现原本能够打开的宝箱现在没有任何反应了,即使用于Debug的线条已经显示为检测到对象的绿色,
此时蓝图中Interact的实现为空,由此可以得知,我们在蓝图中做出的修改覆盖(相当于代码中的override)了C++代码的内容。若要在C++基础上进行扩展,我们在蓝图中调用父函数(相当于代码中的Super)即可,这样打开宝箱的代码就可以重新被执行:
将刚才添加的父函数节点删除,因为它仅用于展示如何调用C++的函数,我们马上会在蓝图中实现更好的宝箱打开动画。
在事件图表中添加“添加时间轴(Add Timeline)”节点,命名为AnimateLid,用于控制宝箱盖子打开的动画。双击AnimateLid节点进入动画编辑 -> 点击左上角“添加浮点型轨道”,命名为Pitch -> 鼠标右键“添加关键帧”(或者按住Shift + 左键点击)。这个轨道用于控制宝箱LidMesh的Pitch角度,即控制每个时刻宝箱打开的角度。稍后我们将微调这个轨道的数值,从而让打开宝箱的动画更有动感。
我们在其中添加(0, 0)和(0.5, 110)两个点(110即为宝箱打开后Pitch的值,这个值可随意调整),在节点超过显示范围时可以点击“缩放进行水平/垂直匹配”两个按钮来调整比例。然后勾选“使用最后一个关键帧?”,这样动画在播放到(0.5, 110)后就结束了。
然后如图中所连
编译后会得到warning:LidMesh对蓝图不可见。因此我们需要在SItemChest.h声明LidMesh变量前的宏定义修改为:UPROPERTY(VisibleAnywhere, BlueprintReadOnly),因为目前只需要读取对象而不涉及修改,所以使用ReadOnly即可。
重新编译代码和蓝图,运行关卡测试效果,可以发现宝箱打开的动画丝滑了很多。这是因为在游戏中的每一帧,蓝图系统都通过Update将Pitch轨道中对应的浮点值赋给LidMesh的Pitch,这自然会比之前在C++实现的直接更改要流畅。
2. 查看蓝图运行过程
蓝图除了方便开发外,还支持实时展现程序运行的过程,这对测试和Debug非常有用。我们只用拖动蓝图窗口,和运行窗口同时显示
(如果有双屏就将它们分别放置在两个屏幕上),运行关卡时设置调试对象,就可以实时看见蓝图的控制流:
进一步,如果想实时看到Pitch的值,还可以设置“查看此数值”:
3. 添加金块
目前我们的宝箱还空空如也,打开后并不会掉落任何宝物,所以我们将要通过粒子系统为宝箱添加爆出金币的效果。
首先,在蓝图编辑器中再添加一个静态网格体组件,分配给根节点BaseMesh,并命名为Treasure Mesh。
然后,再添加一个“粒子系统组件”,分配给Treasure Mesh
最后,在粒子系统的细节面板一直下拉,找到“细节”,并关闭“自动启用”,这是防止粒子特效一进入游戏就自动播放。
如图所示连接
4. 关闭宝箱
最后,我们为打开的宝箱添加关闭的功能。在蓝图中有一个很适合这个场景的函数:Filp Flop,这个节点可以实现在两个分支中自动依次切换,配合AnimateLid节点中的Reverse接口,可以实现动画的逆向播放,也就是关闭宝箱。
根据测试结果可知,在关闭宝箱时粒子特效仍然被触发了,只是因为宝箱盖子刚好遮挡了动画(可以把盖子设置为不可见来更加清晰地验证)。优化的方式也很简单,在触发粒子系统前判断Flip Flop节点Is A的值即可