该技术文档详细介绍了如何在Unreal Engine中实现游戏效果委托绑定系统。核心实现包括:
-
在UCC_AbilitySystemComponent中声明并绑定多播委托EffectApplied()到OnGameplayEffectAppliedDelegateToSelf,用于监听游戏效果应用事件。
-
通过AbilityActorInfoSet()方法初始化委托绑定,在角色初始化时调用。
-
实现效果处理逻辑:当效果被应用时,从FGameplayEffectSpec获取所有AssetTag并打印到屏幕。
-
在英雄角色(CC_HeroCharacter)和敌人角色(CC_EnemyCharacter)中分别实现初始化逻辑,确保服务器和客户端都能正确初始化能力系统组件并绑定委托。
该方案实现了游戏效果系统的解耦通信机制,为后续效果处理和UI反馈奠定了基础。
目标:我们要实现的就是绑定委托,在给自身添加GE时,打印GE附加的Asset Tag
1.在效果中,添加此效果拥有的资产标签:

2.在Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/AbilitySystemComponent.h
中:有相应的委托代码
cpp
/** Delegate for when an effect is applied */
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameplayEffectAppliedDelegate, UAbilitySystemComponent*, const FGameplayEffectSpec&, FActiveGameplayEffectHandle);
这段代码是Unreal Engine中的多播委托声明,用于游戏效果应用时的事件通知。以下是关键点解析:
- 委托类型:DECLARE_MULTICAST_DELEGATE_ThreeParams声明了一个可绑定多个回调函数的多播委托
- 参数结构:委托接收三个参数:
- UAbilitySystemComponent*:指向能力系统组件的指针
- const FGameplayEffectSpec&:游戏效果规格的常量引用
- FActiveGameplayEffectHandle:活动游戏效果的句柄
- 典型用途:常用于游戏技能系统,当效果被施加到目标时触发相关事件
- 观察者模式:允许多个对象订阅此事件,实现解耦的事件通知机制
这种委托在UE的GameplayAbilitySystem中广泛使用,用于处理技能效果、状态变化等游戏逻辑的通信需求。
接着往下看,会发现基于这个委托宏,创建了多个委托属性,有添加GE给自身触发的,有添加给目标触发的,有持续时间的GE添加给自身触发的,还有周期性触发的GE的委托等等。
cpp
/** Called on server whenever a GE is applied to self. This includes instant and duration based GEs. */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToSelf;
/** Called on server whenever a GE is applied to someone else. This includes instant and duration based GEs. */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToTarget;
/** Called on both client and server whenever a duration based GE is added (E.g., instant GEs do not trigger this). */
FOnGameplayEffectAppliedDelegate OnActiveGameplayEffectAddedDelegateToSelf;
/** Called on server whenever a periodic GE executes on self */
FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnSelf;
/** Called on server whenever a periodic GE executes on target */
FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnTarget;
Unreal Engine GameplayEffect 委托技术文档
委托用途与区别
1. OnGameplayEffectAppliedDelegateToSelf
用途 :当任意GameplayEffect(GE)被应用到自身时触发(服务器端)
特点:
- 包含瞬时效果和持续效果
- 仅在服务器端触发
- 适用于需要立即响应效果应用的逻辑
2. OnGameplayEffectAppliedDelegateToTarget
用途 :当任意GameplayEffect被应用到其他目标时触发(服务器端)
特点:
- 包含瞬时效果和持续效果
- 仅在服务器端触发
- 适用于技能施放者监听目标效果
3. OnActiveGameplayEffectAddedDelegateToSelf
用途 :当持续型GE被添加到自身时触发(客户端和服务器)
特点:
- 仅持续效果触发(瞬时效果不触发)
- 双端触发(客户端和服务器)
- 适用于需要同步显示的状态效果
4. OnPeriodicGameplayEffectExecuteDelegateOnSelf
用途 :当周期性GE在自身执行时触发(服务器端)
特点:
- 仅周期性效果触发(如每2秒造成伤害)
- 仅在服务器端触发
- 适用于处理周期性伤害/治疗逻辑
5. OnPeriodicGameplayEffectExecuteDelegateOnTarget
用途 :当周期性GE在目标执行时触发(服务器端)
特点:
- 仅周期性效果触发
- 仅在服务器端触发
- 适用于技能施放者监听目标的周期性效果
3.在Source/CC_Aura/Public/AbilitySystem/CC_AbilitySystemComponent.h
里面,添加一个多播委托:EffectApplied()
cpp
protected:
void EffectApplied(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayEffectSpec& EffectSpec, FActiveGameplayEffectHandle ActiveEffectHandle);
(2)将EffectApplied()绑定到
cpp
/** Called on server whenever a GE is applied to self. This includes instant and duration based GEs. */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToSelf;
(3)创建一个函数,当我们初始化了能力信息时,绑定它:
cpp
public:
void AbilityActorInfoSet();
(4)在演员能力信息初始化时,调用:
cpp
private:
//初始化能力演员信息
void InitCharacterAbilityInfo();
在Source/CC_Aura/Public/Characters/CC_CharacterBase.h
cpp
protected:
//初始化能力演员信息
virtual void InitCharacterASC();
Source/CC_Aura/Public/Characters/CC_HeroCharacter.h:
cpp
protected:
//初始化能力演员信息
virtual void InitCharacterASC() override;

Source/CC_Aura/Public/Characters/CC_EnemyCharacter.h:
cpp
protected:
//初始化能力演员信息
virtual void InitCharacterASC() override;
cpp
void ACC_EnemyCharacter::BeginPlay()
{
Super::BeginPlay();
InitCharacterASC();
}
void ACC_EnemyCharacter::InitCharacterASC()
{ // 为敌人类初始化演员能力信息
AbilitySystemComponent->InitAbilityActorInfo(this,this);
Cast<UCC_AbilitySystemComponent>(AbilitySystemComponent)->AbilityActorInfoSet();
}
(5)Source/CC_Aura/Private/AbilitySystem/CC_AbilitySystemComponent.cpp


效果:

源码:
Source/CC_Aura/Public/AbilitySystem/CC_AbilitySystemComponent.h
cpp
// 版权归陈超所有
#pragma once
#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "CC_AbilitySystemComponent.generated.h"
/**
*
*/
UCLASS()
class CC_AURA_API UCC_AbilitySystemComponent : public UAbilitySystemComponent
{
GENERATED_BODY()
public:
void AbilityActorInfoSet();
protected:
void EffectApplied(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayEffectSpec& EffectSpec, FActiveGameplayEffectHandle ActiveEffectHandle);
};
Source/CC_Aura/Private/AbilitySystem/CC_AbilitySystemComponent.cpp;
cpp
// 版权归陈超所有
#include "AbilitySystem/CC_AbilitySystemComponent.h"
void UCC_AbilitySystemComponent::AbilityActorInfoSet()
{
//多播委托,绑定回调函数
OnGameplayEffectAppliedDelegateToSelf.AddUObject(this, &UCC_AbilitySystemComponent::EffectApplied);
}
void UCC_AbilitySystemComponent::EffectApplied(UAbilitySystemComponent* AbilitySystemComponent,
const FGameplayEffectSpec& EffectSpec, FActiveGameplayEffectHandle ActiveEffectHandle)
{
// GEngine->AddOnScreenDebugMessage(-1, 8.f, FColor::Blue, FString("Effect Applied!"));
FGameplayTagContainer TagContainer;
EffectSpec.GetAllAssetTags(TagContainer);
for(const FGameplayTag& Tag : TagContainer)
{
//TODO: 将tag广播给Widget Controller
const FString Msg = FString::Printf(TEXT("GE Tag: %s"), *Tag.ToString()); //获取Asset Tag
GEngine->AddOnScreenDebugMessage(-1, 8.f, FColor::Cyan, Msg); //打印到屏幕上 -1 不会被覆盖
}
}
Source/CC_Aura/Public/Characters/CC_CharacterBase.h:
cpp
// 版权归陈超所有
#pragma once
#include "CoreMinimal.h"
#include "AbilitySystemInterface.h"
#include "GameFramework/Character.h"
#include "CC_CharacterBase.generated.h"
class UAbilitySystemComponent;
class UAttributeSet;
class USkeletalMeshComponent;
UCLASS(Abstract)
class CC_AURA_API ACC_CharacterBase : public ACharacter,public IAbilitySystemInterface
{
GENERATED_BODY()
public:
// 设置此角色属性的默认值
ACC_CharacterBase();
//重写接口函数,获取能力系统组件
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
//获取属性集
virtual UAttributeSet* GetAttributeSet() const {return AttributeSet;};
protected:
// 在游戏开始或生成时调用
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere,Category="Combat")
TObjectPtr<USkeletalMeshComponent> Weapon;
UPROPERTY()
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
UPROPERTY()
TObjectPtr<UAttributeSet> AttributeSet;
protected:
//初始化能力演员信息
virtual void InitCharacterASC();
};
Source/CC_Aura/Private/Characters/CC_CharacterBase.cpp:
cpp
// 版权归陈超所有
#include "Characters/CC_CharacterBase.h"
#include "AbilitySystem/CC_AbilitySystemComponent.h"
// #include "AttributeSet.h"
// 设置默认值
ACC_CharacterBase::ACC_CharacterBase()
{
// 将此字符设置为每帧调用Tick()。如果你不需要它,你可以关闭它来提高性能。
PrimaryActorTick.bCanEverTick = false;
//将武器添加到角色身上,并且武器跟随某个骨骼节点移动,形成手握武器效果。
Weapon = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Weapon")); //创建默认子对象
Weapon->SetupAttachment(GetMesh(), FName("WeaponHandSocket")); //添加到父节点
Weapon->SetCollisionEnabled(ECollisionEnabled::NoCollision); //关闭骨骼网格体碰撞
}
//重写接口函数,获取能力系统组件
UAbilitySystemComponent* ACC_CharacterBase::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
// 在游戏开始或生成时调用
void ACC_CharacterBase::BeginPlay()
{
Super::BeginPlay();
}
void ACC_CharacterBase::InitCharacterASC()
{
}
Source/CC_Aura/Public/Characters/CC_HeroCharacter.h:
cpp
// 版权归陈超所有
#pragma once
#include "CoreMinimal.h"
#include "Characters/CC_CharacterBase.h"
#include "CC_HeroCharacter.generated.h"
class UCameraComponent;
class USpringArmComponent;
/**
*
*/
UCLASS()
class CC_AURA_API ACC_HeroCharacter : public ACC_CharacterBase
{
GENERATED_BODY()
public:
//构造函数
ACC_HeroCharacter();
//服务器端初始化ASC
virtual void PossessedBy(AController* NewController) override;;
//客户端初始化ASC
virtual void OnRep_PlayerState() override;
protected:
//初始化能力演员信息
virtual void InitCharacterASC() override;
#pragma region Components
//弹簧臂组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", meta = (AllowPrivateAccess = "true"))
TObjectPtr<USpringArmComponent> SpringArm;
//跟随摄像机组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", meta = (AllowPrivateAccess = "true"))
TObjectPtr<UCameraComponent> FollowCamera;
#pragma endregion
};
Source/CC_Aura/Private/Characters/CC_HeroCharacter.cpp:
cpp
// 版权归陈超所有
#include "Characters/CC_HeroCharacter.h"
// #include "AbilitySystemComponent.h"
#include "AbilitySystem/CC_AbilitySystemComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Player/CC_PlayerController.h"
#include "Player/CC_PlayerState.h"
#include "UI/HUD/CC_HUD.h"
ACC_HeroCharacter::ACC_HeroCharacter()
{
//角色本身的设置(俯视角游戏)
GetCharacterMovement()->bOrientRotationToMovement = true; //设置为true,角色将朝移动的方向旋转
GetCharacterMovement()->RotationRate = FRotator(0.f, 400.f, 0.f); //旋转速率
GetCharacterMovement()->bConstrainToPlane = true; //约束到平面
GetCharacterMovement()->bSnapToPlaneAtStart = true; //设置了上面一项为true,且此项设置为true,则在开始时与地面对齐
bUseControllerRotationPitch = false; //同时需要确保角色本身不跟随控制器旋转:
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
//弹簧臂组件
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
SpringArm->SetupAttachment(RootComponent); //设置附着点(跟组件)
SpringArm->TargetArmLength = 900;
SpringArm->SetRelativeRotation(FRotator(-45.f, 0, 0)); //设置相对旋转 设置俯视角度(Pitch负值)
SpringArm->bUsePawnControlRotation = false; //确保不随角色转向而转动
SpringArm->bInheritPitch = false; //取消继承俯仰
SpringArm->bInheritRoll = false; //取消继承翻滚
SpringArm->bInheritYaw = false; //取消继承摇摆
SpringArm->bEnableCameraLag = true; //相机延迟效果
//跟随摄像机组件
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(SpringArm, USpringArmComponent::SocketName); //设置附着点(弹簧臂组件接口名称)
FollowCamera->bUsePawnControlRotation = false;
}
void ACC_HeroCharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
//服务器端初始化ASC
InitCharacterASC();
}
void ACC_HeroCharacter::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
//客户端初始化ASC
InitCharacterASC();
}
void ACC_HeroCharacter::InitCharacterASC()
{
ACC_PlayerState* CCPlayerState = GetPlayerState<ACC_PlayerState>(); //获取玩家状态
// check(CCPlayerState);
if (CCPlayerState == nullptr)
{
UE_LOG(LogTemp, Warning, TEXT("CCPlayerState: %s - CCPlayerState is nullptl"), *GetName());
return;
}
//获取玩家角色的ASC、AS
if (AbilitySystemComponent == nullptr || AttributeSet == nullptr)
{
AbilitySystemComponent = CCPlayerState->GetAbilitySystemComponent();
AttributeSet = CCPlayerState->GetAttributeSet();
}
//初始化ASC
AbilitySystemComponent->InitAbilityActorInfo(CCPlayerState, this);
Cast<UCC_AbilitySystemComponent>(AbilitySystemComponent)->AbilityActorInfoSet();
//获取玩家控制器
ACC_PlayerController* CCPlayerController = Cast<ACC_PlayerController>(GetController());
/*服务器中,拥有所有玩家控制器
* 客户端只拥有自己的玩家控制器,当控制器为空是,游戏不能崩溃
*/
if (CCPlayerController)
{
//获取HUD
ACC_HUD* CCHUD = Cast<ACC_HUD>(CCPlayerController->GetHUD());
if (CCHUD) //仅对本地控制的玩家有效
{
//初始化HUD
CCHUD ->InitOverlay(CCPlayerController, CCPlayerState, AbilitySystemComponent, AttributeSet);
}
}
}
Source/CC_Aura/Public/Characters/CC_EnemyCharacter.h:
cpp
// 版权归陈超所有
#pragma once
#include "CoreMinimal.h"
#include "Characters/CC_CharacterBase.h"
#include "Interations/CC_EnemyInterface.h"
#include "CC_EnemyCharacter.generated.h"
/**
*
*/
UCLASS()
class CC_AURA_API ACC_EnemyCharacter : public ACC_CharacterBase ,public ICC_EnemyInterface
{
GENERATED_BODY()
public:
ACC_EnemyCharacter();
// ~Begin IEnemyInterface重写函数
virtual void HighlightActor() override;
virtual void UnHighlightActor() override;
// ~End IEnemyInterface重写函数
protected:
virtual void BeginPlay() override;
protected:
//初始化能力演员信息
virtual void InitCharacterASC() override;
};
Source/CC_Aura/Private/Characters/CC_EnemyCharacter.cpp:
cpp
// 版权归陈超所有
#include "Characters/CC_EnemyCharacter.h"
#include "AbilitySystem/CC_AbilitySystemComponent.h"
#include "AbilitySystem/CC_AttributeSet.h"
#include "CC_Aura/CC_Aura.h"
//构造函数
ACC_EnemyCharacter::ACC_EnemyCharacter()
{
//为网格设置碰撞响应
GetMesh()->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); //自定义碰撞,块碰撞
//添加能力系统组件
AbilitySystemComponent = CreateDefaultSubobject<UCC_AbilitySystemComponent>("AbilitySystemComponent");
AbilitySystemComponent->SetIsReplicated(true); //设置组件用于在网络上复制
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Minimal); //复制模式
//添加属性集
AttributeSet = CreateDefaultSubobject<UCC_AttributeSet>("AttributeSet");
}
void ACC_EnemyCharacter::BeginPlay()
{
Super::BeginPlay();
InitCharacterASC();
}
void ACC_EnemyCharacter::InitCharacterASC()
{ // 为敌人类初始化演员能力信息
AbilitySystemComponent->InitAbilityActorInfo(this,this);
Cast<UCC_AbilitySystemComponent>(AbilitySystemComponent)->AbilityActorInfoSet();
}
//高亮显示角色
void ACC_EnemyCharacter::HighlightActor()
{
GetMesh()->SetRenderCustomDepth(true); //是否渲染自定义深度
GetMesh()->SetCustomDepthStencilValue(CUSTOM_DEPTH_RED); //自定义深度模板值
Weapon->SetRenderCustomDepth(true);
Weapon->SetCustomDepthStencilValue(CUSTOM_DEPTH_RED);
}
//取消高亮显示角色
void ACC_EnemyCharacter::UnHighlightActor()
{
GetMesh()->SetRenderCustomDepth(false);
Weapon->SetRenderCustomDepth(false);
}