虚幻引擎5 GAS开发俯视角RPG游戏 P05-05 游戏效果委托
该技术文档详细介绍了如何在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
中:有相应的委托代码
/** 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的委托等等。
/** 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()
protected:void EffectApplied(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayEffectSpec& EffectSpec, FActiveGameplayEffectHandle ActiveEffectHandle);
(2)将EffectApplied()绑定到
/** Called on server whenever a GE is applied to self. This includes instant and duration based GEs. */FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToSelf;
(3)创建一个函数,当我们初始化了能力信息时,绑定它:
public:void AbilityActorInfoSet();
(4)在演员能力信息初始化时,调用:
private://初始化能力演员信息void InitCharacterAbilityInfo();
在Source/CC_Aura/Public/Characters/CC_CharacterBase.h
protected://初始化能力演员信息virtual void InitCharacterASC();
Source/CC_Aura/Public/Characters/CC_HeroCharacter.h:
protected://初始化能力演员信息virtual void InitCharacterASC() override;
Source/CC_Aura/Public/Characters/CC_EnemyCharacter.h:
protected://初始化能力演员信息virtual void InitCharacterASC() override;
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
// 版权归陈超所有#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;
// 版权归陈超所有#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 Controllerconst FString Msg = FString::Printf(TEXT("GE Tag: %s"), *Tag.ToString()); //获取Asset TagGEngine->AddOnScreenDebugMessage(-1, 8.f, FColor::Cyan, Msg); //打印到屏幕上 -1 不会被覆盖}
}
Source/CC_Aura/Public/Characters/CC_CharacterBase.h:
// 版权归陈超所有#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:
// 版权归陈超所有#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:
// 版权归陈超所有#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();//服务器端初始化ASCvirtual void PossessedBy(AController* NewController) override;;//客户端初始化ASCvirtual 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:
// 版权归陈超所有#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);//服务器端初始化ASCInitCharacterASC();
}void ACC_HeroCharacter::OnRep_PlayerState()
{Super::OnRep_PlayerState();//客户端初始化ASCInitCharacterASC();
}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、ASif (AbilitySystemComponent == nullptr || AttributeSet == nullptr){AbilitySystemComponent = CCPlayerState->GetAbilitySystemComponent();AttributeSet = CCPlayerState->GetAttributeSet();}//初始化ASCAbilitySystemComponent->InitAbilityActorInfo(CCPlayerState, this);Cast<UCC_AbilitySystemComponent>(AbilitySystemComponent)->AbilityActorInfoSet();//获取玩家控制器ACC_PlayerController* CCPlayerController = Cast<ACC_PlayerController>(GetController());/*服务器中,拥有所有玩家控制器* 客户端只拥有自己的玩家控制器,当控制器为空是,游戏不能崩溃*/if (CCPlayerController){//获取HUDACC_HUD* CCHUD = Cast<ACC_HUD>(CCPlayerController->GetHUD());if (CCHUD) //仅对本地控制的玩家有效{//初始化HUDCCHUD ->InitOverlay(CCPlayerController, CCPlayerState, AbilitySystemComponent, AttributeSet);}}
}
Source/CC_Aura/Public/Characters/CC_EnemyCharacter.h:
// 版权归陈超所有#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:
// 版权归陈超所有#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);
}