装备武器动画
2023年8月28日 2024年1月11日
说明
- 装备武器时, 播放动画, 使用动画剪辑
AnimMontage
资产实现 - 播放装备武器动画时, 锁定其他操作, 使用动画提醒
AnimNotify
资产实现
概览
- 使用动画剪辑播放装备武器动画
- 创建动画剪辑资产
- 在蓝图中实现Notify
- 创建UAnimNotify派生类
- 在C++中实现Notify
- 在委托处理函数中判断骨骼网格体所属
使用动画剪辑资产播放装备武器动画
添加数据成员, 指向装备武器动画剪辑
protected
ShootThemUp: Components/STUWeaponComponent.h
1class UAnimMontage; 2 3UPROPERTY(EditDefaultsOnly) 4UAnimMontage *EquipAnimMontage;
添加函数, 播放动画剪辑
定义
ShootThemUp: Components/STUWeaponComponent.h
private
1void PlayAnimMontage(UAnimMontage *Animation);
ShootThemUp: Components/STUWeaponComponent.cpp
1#include "Animation/AnimMontage.h" 2 3void USTUWeaponComponent::PlayAnimMontage(UAnimMontage *Animation) 4{ 5 ACharacter *Character = Cast<ACharacter>(GetOwner()); 6 if (!Character) return; 7 8 Character->PlayAnimMontage(Animation); 9}
调用
ShootThemUp: Components/STUWeaponComponent.cpp
1// EquipWeapon 2PlayAnimMontage(EquipAnimMontage);
创建动画剪辑资产
虚幻编辑器
-
选中Content/ExternalContent/Animation/Animations/TTP_Animations/Equip, 右键, 选择
Create AnimMontage
-
命名为AM_Equip
-
移动到Content/Player/Animations
-
打开BP_STUBaseCharacter, 设置EquipAnimMontage为AM_Equip
当前问题
- 频繁快速切换武器, 持续调用PlayAnimMontage, 表现为最后一次才完整播放装备武器动画
- 播放动画时, 可以射击
引入动画提醒资产, 设想是, 在动画剪辑播放将近结束之前, 锁定其他操作
提醒机制
Notify
在动画剪辑资产的时间线上添加Notify事件; 动画提醒的实现基于把事件和特定帧绑定在一起
蓝图
在动画蓝图 > EventGraph中对事件进行处理
C++
Decorator
覆写Notify函数, 添加处理
查看动画资产
-
双击打开AM_Equip, 点击暂停
-
移动滑杆, 可以查看时间线上对应的帧
-
时间线右侧有Notifies栏
Notifies栏
有Track按钮, 可以添加其他轨道, 用于成组的事件 Grouping Events
在蓝图中测试提醒机制
添加Notify事件
-
双击打开AM_Equip
-
将滑杆拖动到动画较后位置
-
在轨道1右键, 选择
Add Notify > New Notify
-
命名为
EquipFinished
-
查看
在动画蓝图中处理事件
-
打开ABP_BaseCharacter, 去到EventGraph
-
在空白处右键, 选择
Add Anim Notify Event > Event AnimNotify_EquipFinished
-
执行字符串打印
-
切换武器时, 屏幕有输出
在C++中实现Notify
- 创建AnimNotify的派生类, 作为委托服务的服务端
- 武器组件注册委托服务, 搭建好框架, 之后再实现播放装备武器动画的锁定和解锁逻辑
- 在装备武器动画剪辑中添加AnimNotify派生类事件
新建AnimNotify的派生类
虚幻编辑器
- | |
---|---|
基类 | AnimNotify |
上级文件夹 | Animations |
访问属性 | Public |
名称 | STUEquipFinishedAnimNotify |
添加头文件搜索路径
ShootThemUp: ShootThemUp.Build.cs
1PublicIncludePaths.AddRange(new string[] 2{ 3 "ShootThemUp/Public/Player", 4 "ShootThemUp/Public/Components", 5 "ShootThemUp/Public/Dev", 6 "ShootThemUp/Public/Weapon", 7 "ShootThemUp/Public/UI", 8 "ShootThemUp/Public/Animations" 9});
查看UAnimNotify派生类
ShootThemUp: Animations/STUEquipFinishedAnimNotify.h
- 事件发生时, 会调用UAnimNotify::Notify函数
UE_5.1/Engine/Source/Runtime/Engine/Classes/Animation/AnimNotifies/AnimNotify.h
- 定义委托类型, 使STUEquipFinishedAnimNotify作为服务端
- 覆写Notify函数, 通知客户端
实现STUEquipFinishedAnimNotify
定义委托类型, 并添加数据成员
public
ShootThemUp: Animations/STUEquipFinishedAnimNotify.h
1DECLARE_MULTICAST_DELEGATE(FOnNotifiedSignature); 2 3FOnNotifiedSignature OnNotified;
覆写Notify函数, 通知客户端
-
ShootThemUp: Animations/STUEquipFinishedAnimNotify.h
public
1virtual void Notify(USkeletalMeshComponent *MeshComp, UAnimSequenceBase *Animation, const FAnimNotifyEventReference& EventReference) override;
-
ShootThemUp: Animations/STUEquipFinishedAnimNotify.cpp
- Decorator
为已存在函数添加额外逻辑 1void USTUEquipFinishedAnimNotify::Notify(USkeletalMeshComponent *MeshComp, UAnimSequenceBase *Animation, const FAnimNotifyEventReference& EventReference) 2{ 3 OnNotified.Broadcast(); 4 Super::Notify(MeshComp, Animation, EventReference); 5}
武器组件中, 对装备武器事件进行订阅
实现处理函数, 事件发生时打印
- 定义日志类型
ShootThemUp: Components/STUWeaponComponent.cpp
1DEFINE_LOG_CATEGORY_STATIC(LogWeaponComponent, All, All);
ShootThemUp: Components/STUWeaponComponent.h
private
1void OnEquipFinished();
ShootThemUp: Components/STUWeaponComponent.cpp
1void USTUWeaponComponent::OnEquipFinished() 2{ 3 UE_LOG(LogWeaponComponent, Display, TEXT("Equip Finished!")); 4}
订阅委托
-
声明
private
ShootThemUp: Components/STUWeaponComponent.h
1void InitAnimations();
-
获取AM_Equip中所有的提醒事件
- UAnimMontage派生自UAnimCompositeBase, UAnimCompositeBase派生自UAnimSequnceBase
- UAnimSequnceBase是动画基础类
Base Animation Class
- UAnimSequnceBase有数据成员Notifies数组, 存放FAnimNotifyEvent
- FAnimNotifyEvent存放Notify事件信息; 该结构体有一个指向UAnimNotify对象的指针
- FAnimNotifyEvent存放Notify事件信息; 该结构体有一个指向UAnimNotify对象的指针
- UAnimSequenceBase还有一个属性AnimNotifyTracks, 即在动画资产Notify一栏添加的Tracks, 只能在开发时访问
1if (!EquipAnimMontage) return; 2 3const auto NotifyEvents = EquipAnimMontage->Notifies;
- UAnimMontage派生自UAnimCompositeBase, UAnimCompositeBase派生自UAnimSequnceBase
-
遍历所有事件, 注册STUEquipFinishedAnimNotify
1#include "Animations/STUEquipFinishedAnimNotify.h" 2 3for (auto NotifyEvent : NotifyEvents) 4{ 5 auto EquipFinishedNotify = Cast<USTUEquipFinishedAnimNotify>(NotifyEvent.Notify); 6 if (EquipFinishedNotify) 7 { 8 EquipFinishedNotify->OnNotified.AddUObject(this, &USTUWeaponComponent::OnEquipFinished); 9 break; 10 } 11}
-
完整实现
ShootThemUp: Components/STUWeaponComponent.cpp
1#include "Animations/STUEquipFinishedAnimNotify.h" 2 3void USTUWeaponComponent::InitAnimations() 4{ 5 if (!EquipAnimMontage) return; 6 7 const auto NotifyEvents = EquipAnimMontage->Notifies; 8 for (auto NotifyEvent : NotifyEvents) 9 { 10 auto EquipFinishedNotify = Cast<USTUEquipFinishedAnimNotify>(NotifyEvent.Notify); 11 if (EquipFinishedNotify) 12 { 13 EquipFinishedNotify->OnNotified.AddUObject(this, &USTUWeaponComponent::OnEquipFinished); 14 break; 15 } 16 } 17}
-
在BeginPlay中调用
ShootThemUp: Components/STUWeaponComponent.cpp
在AM_Equip添加动画提醒事件
-
打开AM_Equip
-
在时间线最后添加Notify: STUEquipFinishedAnimNotify
-
可在细节面板进行设置: 如修改Notify Color
查看
装备武器时, 日志成对打印: 所有的游戏角色均收到通知
武器组件收到通知时, 判断装备武器的骨骼网格体所属
修改委托定义, 增加一个参数, 指向骨骼网格体
ShootThemUp: Animations/STUEquipFinishedAnimNotify.h
1// DECLARE_MULTICAST_DELEGATE(FOnNotifiedSignature); 2DECLARE_MULTICAST_DELEGATE_OneParam(FOnNotifiedSignature, USkeletalMeshComponent*);
通知客户端时, 传参骨骼网格体
ShootThemUp: Animations/STUEquipFinishedAnimNotify.cpp
1// Notify 2OnNotified.Broadcast(MeshComp);
修改客户端处理函数声明
ShootThemUp: Components/STUWeaponComponent.h
1void OnEquipFinished(USkeletalMeshComponent *MeshComp);
修改客户端处理函数定义, 判断骨骼网格体所属
ShootThemUp: Components/STUWeaponComponent.cpp
1void USTUWeaponComponent::OnEquipFinished(USkeletalMeshComponent *MeshComp) 2{ 3 ACharacter *Character = Cast<ACharacter>(GetOwner()); 4 if (!Character || (Character->GetMesh() != MeshComp)) return; 5 6 UE_LOG(LogWeaponComponent, Display, TEXT("Equip Finished!")); 7}