六一的部落格


关关难过关关过,前路漫漫亦灿灿。




说明

  1. 装备武器时, 播放动画, 使用动画剪辑 AnimMontage 资产实现
  2. 播放装备武器动画时, 锁定其他操作, 使用动画提醒 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);

创建动画剪辑资产

虚幻编辑器

  1. 选中Content/ExternalContent/Animation/Animations/TTP_Animations/Equip, 右键, 选择 Create AnimMontage

  2. 命名为AM_Equip

  3. 移动到Content/Player/Animations

  4. 打开BP_STUBaseCharacter, 设置EquipAnimMontage为AM_Equip



当前问题

  1. 频繁快速切换武器, 持续调用PlayAnimMontage, 表现为最后一次才完整播放装备武器动画
  2. 播放动画时, 可以射击

引入动画提醒资产, 设想是, 在动画剪辑播放将近结束之前, 锁定其他操作


提醒机制

Notify

在动画剪辑资产的时间线上添加Notify事件; 动画提醒的实现基于把事件和特定帧绑定在一起


蓝图

在动画蓝图 > EventGraph中对事件进行处理


C++

Decorator

覆写Notify函数, 添加处理


查看动画资产

  1. 双击打开AM_Equip, 点击暂停


  2. 移动滑杆, 可以查看时间线上对应的帧

  3. 时间线右侧有Notifies栏


Notifies栏

有Track按钮, 可以添加其他轨道, 用于成组的事件 Grouping Events


在蓝图中测试提醒机制


添加Notify事件

  1. 双击打开AM_Equip

  2. 将滑杆拖动到动画较后位置

  3. 在轨道1右键, 选择 Add Notify > New Notify


  4. 命名为 EquipFinished


  5. 查看



在动画蓝图中处理事件

  1. 打开ABP_BaseCharacter, 去到EventGraph

  2. 在空白处右键, 选择 Add Anim Notify Event > Event AnimNotify_EquipFinished


  3. 执行字符串打印


  4. 切换武器时, 屏幕有输出



在C++中实现Notify

  1. 创建AnimNotify的派生类, 作为委托服务的服务端
  2. 武器组件注册委托服务, 搭建好框架, 之后再实现播放装备武器动画的锁定和解锁逻辑
  3. 在装备武器动画剪辑中添加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

  1. 事件发生时, 会调用UAnimNotify::Notify函数

    UE_5.1/Engine/Source/Runtime/Engine/Classes/Animation/AnimNotifies/AnimNotify.h
  2. 定义委托类型, 使STUEquipFinishedAnimNotify作为服务端
  3. 覆写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}

武器组件中, 对装备武器事件进行订阅


实现处理函数, 事件发生时打印

  1. 定义日志类型

    ShootThemUp: Components/STUWeaponComponent.cpp
    1DEFINE_LOG_CATEGORY_STATIC(LogWeaponComponent, All, All);
  2. ShootThemUp: Components/STUWeaponComponent.h

    private
    1void OnEquipFinished();            
    
  3. 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对象的指针
    • UAnimSequenceBase还有一个属性AnimNotifyTracks, 即在动画资产Notify一栏添加的Tracks, 只能在开发时访问
    1if (!EquipAnimMontage) return;
    2
    3const auto NotifyEvents = EquipAnimMontage->Notifies;
  • 遍历所有事件, 注册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添加动画提醒事件

  1. 打开AM_Equip

  2. 在时间线最后添加Notify: STUEquipFinishedAnimNotify


  3. 可在细节面板进行设置: 如修改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}

查看


装备武器动画



说明

  1. 装备武器时, 播放动画, 使用动画剪辑 AnimMontage 资产实现
  2. 播放装备武器动画时, 锁定其他操作, 使用动画提醒 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);

创建动画剪辑资产

虚幻编辑器

  1. 选中Content/ExternalContent/Animation/Animations/TTP_Animations/Equip, 右键, 选择 Create AnimMontage

  2. 命名为AM_Equip

  3. 移动到Content/Player/Animations

  4. 打开BP_STUBaseCharacter, 设置EquipAnimMontage为AM_Equip



当前问题

  1. 频繁快速切换武器, 持续调用PlayAnimMontage, 表现为最后一次才完整播放装备武器动画
  2. 播放动画时, 可以射击

引入动画提醒资产, 设想是, 在动画剪辑播放将近结束之前, 锁定其他操作


提醒机制

Notify

在动画剪辑资产的时间线上添加Notify事件; 动画提醒的实现基于把事件和特定帧绑定在一起


蓝图

在动画蓝图 > EventGraph中对事件进行处理


C++

Decorator

覆写Notify函数, 添加处理


查看动画资产

  1. 双击打开AM_Equip, 点击暂停


  2. 移动滑杆, 可以查看时间线上对应的帧

  3. 时间线右侧有Notifies栏


Notifies栏

有Track按钮, 可以添加其他轨道, 用于成组的事件 Grouping Events


在蓝图中测试提醒机制


添加Notify事件

  1. 双击打开AM_Equip

  2. 将滑杆拖动到动画较后位置

  3. 在轨道1右键, 选择 Add Notify > New Notify


  4. 命名为 EquipFinished


  5. 查看



在动画蓝图中处理事件

  1. 打开ABP_BaseCharacter, 去到EventGraph

  2. 在空白处右键, 选择 Add Anim Notify Event > Event AnimNotify_EquipFinished


  3. 执行字符串打印


  4. 切换武器时, 屏幕有输出



在C++中实现Notify

  1. 创建AnimNotify的派生类, 作为委托服务的服务端
  2. 武器组件注册委托服务, 搭建好框架, 之后再实现播放装备武器动画的锁定和解锁逻辑
  3. 在装备武器动画剪辑中添加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

  1. 事件发生时, 会调用UAnimNotify::Notify函数

    UE_5.1/Engine/Source/Runtime/Engine/Classes/Animation/AnimNotifies/AnimNotify.h
  2. 定义委托类型, 使STUEquipFinishedAnimNotify作为服务端
  3. 覆写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}

武器组件中, 对装备武器事件进行订阅


实现处理函数, 事件发生时打印

  1. 定义日志类型

    ShootThemUp: Components/STUWeaponComponent.cpp
    1DEFINE_LOG_CATEGORY_STATIC(LogWeaponComponent, All, All);
  2. ShootThemUp: Components/STUWeaponComponent.h

    private
    1void OnEquipFinished();            
    
  3. 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对象的指针
    • UAnimSequenceBase还有一个属性AnimNotifyTracks, 即在动画资产Notify一栏添加的Tracks, 只能在开发时访问
    1if (!EquipAnimMontage) return;
    2
    3const auto NotifyEvents = EquipAnimMontage->Notifies;
  • 遍历所有事件, 注册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添加动画提醒事件

  1. 打开AM_Equip

  2. 在时间线最后添加Notify: STUEquipFinishedAnimNotify


  3. 可在细节面板进行设置: 如修改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}

查看