虚幻引擎5 GAS开发俯视角RPG游戏 P05-05 游戏效果委托

该技术文档详细介绍了如何在Unreal Engine中实现游戏效果委托绑定系统。核心实现包括:

  1. 在UCC_AbilitySystemComponent中声明并绑定多播委托EffectApplied()到OnGameplayEffectAppliedDelegateToSelf,用于监听游戏效果应用事件。

  2. 通过AbilityActorInfoSet()方法初始化委托绑定,在角色初始化时调用。

  3. 实现效果处理逻辑:当效果被应用时,从FGameplayEffectSpec获取所有AssetTag并打印到屏幕。

  4. 在英雄角色(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中的多播委托声明,用于游戏效果应用时的事件通知。以下是关键点解析:

  1. 委托类型:DECLARE_MULTICAST_DELEGATE_ThreeParams声明了一个可绑定多个回调函数的多播委托
  2. 参数结构:委托接收三个参数:
    • UAbilitySystemComponent*:指向能力系统组件的指针
    • const FGameplayEffectSpec&:游戏效果规格的常量引用
    • FActiveGameplayEffectHandle:活动游戏效果的句柄
  3. 典型用途:常用于游戏技能系统,当效果被施加到目标时触发相关事件
  4. 观察者模式:允许多个对象订阅此事件,实现解耦的事件通知机制

这种委托在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);
}
相关推荐
杨小码不BUG4 小时前
Davor的北极探险资金筹集:数学建模与算法优化(洛谷P4956)
c++·算法·数学建模·信奥赛·csp-j/s
mit6.8244 小时前
10.5 数位dp
c++·算法
初圣魔门首席弟子4 小时前
C++ STL 向量(vector)学习笔记:从基础到实战
c++·笔记·学习
青草地溪水旁4 小时前
Visual Studio Code中launch.json深度解析:C++调试的艺术
c++·vscode·json
m0_552200825 小时前
《UE5_C++多人TPS完整教程》学习笔记62 ——《P63 多人游戏中的开火特效(Fire Effects in Multiplayer)》
c++·游戏·ue5
liu****5 小时前
基于websocket的多用户网页五子棋(九)
服务器·网络·数据库·c++·websocket·网络协议·个人开发
liu****5 小时前
基于websocket的多用户网页五子棋(八)
服务器·前端·javascript·数据库·c++·websocket·个人开发
ajassi20005 小时前
开源 C++ QT QML 开发(十二)通讯--TCP客户端
c++·qt·开源
进击的圆儿6 小时前
【学习笔记05】C++11新特性学习总结(下)
c++·笔记·学习