为什么要自定义 AnimNotify
默认的 AnimNotify
只能触发“硬编码”逻辑,无法在编辑器中为同一个 Notify 设置不同数值或名字。自定义 Notify 蓝图后,就可以在不同动画关键帧上配置不同的参数。比如 "攻击伤害值"、"特效名称"、"声音" 等。
使用蓝图实现
1. 创建自定义 Notify(瞬时通知)
-
新建蓝图类
-
右键内容浏览器 → Blueprint Class → All Classes → AnimNotify
-
命名如
BP_AnimNotify_Attack
-
-
添加并暴露参数
-
在 Variables 面板中添加:
-
DamageAmount : Float
,勾选 Instance Editable -
SocketName : Name
,勾选 Instance Editable
-
-
-
实现 Received_Notify
-
在蓝图图表中重载
Received Notify
节点:
-
Event Received Notify (MeshComp, Animation)
→ Cast To YourCharacter
→ Call Character.TriggerAttack(DamageAmount, SocketName)
-
使用方式
-
打开动画序列或蒙太奇
-
在 Notify Track 上添加
BP_AnimNotify_Attack
-
在 Details 面板填入各帧的
DamageAmount
、SocketName
等
-
2. 创建自定义 NotifyState(持续通知)
-
新建 Blueprint Class → All Classes → AnimNotifyState
-
添加变量(同上,勾 Instance Editable)
-
重载
NotifyBegin
/NotifyEnd
,在开始/结束时 Spawn 或 Destroy 特效、切换状态
使用C++实现
1. 自定义瞬时 Notify:UAnimNotify
头文件:MyAnimNotify_Attack.h
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "MyAnimNotify_Attack.generated.h"
UCLASS()
class YOURGAME_API UMyAnimNotify_Attack : public UAnimNotify
{
GENERATED_BODY()
public:
// 可在动画轨道面板中编辑
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Attack")
float Damage = 10.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Attack")
FName SocketName = NAME_None;
virtual bool Received_Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
};
实现文件:MyAnimNotify_Attack.cpp
#include "MyAnimNotify_Attack.h"
#include "YourGame/MyCharacterBase.h"
bool UMyAnimNotify_Attack::Received_Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
if (auto* Char = Cast<AMyCharacterBase>(MeshComp->GetOwner()))
{
Char->TriggerAttack(Damage, SocketName);
return true;
}
return false;
}
2. 自定义持续 Notify:UAnimNotifyState
头文件:MyAnimNotifyState_Trail.h
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimNotifies/AnimNotifyState.h"
#include "MyAnimNotifyState_Trail.generated.h"
UCLASS()
class YOURGAME_API UMyAnimNotifyState_Trail : public UAnimNotifyState
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Trail")
FName SocketName = NAME_None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Trail")
UParticleSystem* TrailFX = nullptr;
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) override;
virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
};
实现文件:MyAnimNotifyState_Trail.cpp
#include "MyAnimNotifyState_Trail.h"
#include "Kismet/GameplayStatics.h"
void UMyAnimNotifyState_Trail::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration)
{
if (TrailFX && MeshComp)
{
UGameplayStatics::SpawnEmitterAttached(
TrailFX,
MeshComp,
SocketName,
FVector::ZeroVector,
FRotator::ZeroRotator,
EAttachLocation::SnapToTarget, true);
}
}
void UMyAnimNotifyState_Trail::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
// 可在此清理或停止特效
}
3. 使用方式
-
编译完成后,打开动画或蒙太奇
- 在 Notify Track 上添加对应的 C++ Notify 或 NotifyState
- 在 Details 面板中填写你的 Damage、SocketName、TrailFX 等参数