第5章---GameplayEffect的使用
- 开源代码
- 2025-08-03 00:06:01

由于GE拥有丰富的可选项,我们甚至不需要去设置子类,直接用插件的类来创建蓝图GE类就可以了,在这里写下所有的选项对应的功能。
GE结构以及对于部分结构的解释 Period Period : 只有当Duration Policy 选项为 Infinite 或 Has Duration 时才出现,输入一个float数据或一个曲线表,表示每x秒出发一次GE效果Execute Periodic Effect on Application : 布尔值,为true则在第0秒时触发一次,为false则不触发Periodic Inhibition Policy : 有3个选项,Never Reset, Reset Period, Execute and Reset Period Overflow Overflow EffectsDeny Overflow ApplicationClear Stack on Overflow Expiration Premature Expiration Effect ClassesRoutine Expiraction Effect Classes Immunity GrantedApplicationImmunityTags Require TagsIgnore Tags Granted Application Immunity Query Custom Mach Delegate BPOwing Tag QueryEffect Tag QuerySource Tag QueryModyfing AttributeEffect SourceEffect Definition Stacking Stacking Type : 有三个选项,None, Aggregate by Source, Aggregate by Target,None意为不进行堆叠(只要触发了GE就直接应用),Aggregate by Source意为以来源为基准进行堆叠(同一个源头只能对同一个对象同时触发规定个数内的GE),Aggregate by Target意为以对象为基准进行堆叠(同一个对象只能被同时触发规定个数内的GE)Stack Limit Count : 堆叠GE的最大数量Stack Duration Refresh Policy : 有两个选项,Refresh on Successful Application, Never Refresh,Refresh on Successful Application意为在GE成功进入堆叠后重新设置GE持续事件(可以续Buff), Never Refresh意为在GE成功进入堆叠后不刷新GE的持续事件(不可以续Buff)Stack Period Reset Policy : 有两个选项,Refresh on Successful Application, Never Refresh,Refresh on Successful Application意为在GE成功进入堆叠后立即以新的GE为准触发, Never Refresh意为在GE成功进入堆叠后仍然以旧的GE为准进行触发Stack Expiration Policy : 有三个选项,Clear Entire Stack, Remove Single Stack and Refresh Duration, Refresh Duration,Clear Entire Stack意为在GE到期时移除所有的GE堆叠,Remove Single Stack and Refresh Duration意为在GE到期时移除一层堆叠并开始新一轮的触发GE效果(道具的堆叠效果),Refresh Duration意为不对堆叠做任何处理,单纯开始新一轮的触发GE效果 Granted Abilities Granted Abilities Gameplay Effect Duration Policy : 有三个选项,Instant, Infinite, Has Duration,选择Instance意为即刻应用GE,Infinite意为永远无限长时间内应用该GE, Has Duration意为在一段时间内应用GEDuration Magnitude : 意为GE的持续时间 Magnitude Calculation Type : 有四个选项,Scalable Float, Attribute Based, Custom Calculation Class, SetbyCallseScalable Float Magnitude : 根据Magnitude Calculation Type进行改变,Scalable Float时填入float数据或数据表格,意为直接使用float内的数据或表格数据,此时float数据为乘数 Modifiers 数组 Attribute : 指需要改变的Attribute对象Modifier Op : 有Add, Multiply, Divide, Override, Invalid对应加,乘,除,重写(将对应Attribute的BaseValue和CurrentValue都变成指定数值)Modifier Magnitude Magnitude Calculation Type : 有四个选项,Scalable Float, Attribute Based, Custom Calculation Class, SetbyCallseScalable Float Magnitude : 根据Magnitude Calculation Type进行改变,Scalable Float时填入float数据或数据表格,意为直接使用float内的数据或表格数据,此时float数据为乘数 Source Tags Require TagsIgnore Tags Target Tags Require TagsIgnore Tags Executions 数组 Calculations ClassConditional Gameplay Effects Conditional Gameplay Effects Application Chance to Apply to TargetApplication Requirement 显示 Require Modifiers Success to Trigger CuesSupress Stacking CuseGameplay CuesUIData 标签 Gameplay Effect Asset Tag : 与父类相关,不会将其中拥有的Tag赋予给施加GE的Actor的ASC上 Combined Tags : Added中提及以及Removed没有提及的所有TagAdded : 添加的TagRemoved : 移除的Tag Granted Tags : 在其中的Tag将会被附加到Actor的ASC上Granted Bloked Ability TagsOngoing Tag RequirementsApplication Tag RequirementsRemoval Tag RequirementsRemove Gameplay Effect QueryRemove Gameplay Effect with Tags 文件结构Source
Private AbilitySystemComponen RPGAbilitySystemComponent.cppRPGAttributeSet.cpp Character PGGameCharacterBase.cppRPGGameEnemy.cppRPGGamePlayerCharacter.cpp Game RPGGameModeBase.cpp Interaction EnemyInterface.cpp Player RPGPlayerController.cppRPGPlayerState.cpp Actor RPGEffectActor.cpp UI HUD RPGHUD.cpp WidgetController OverlayWidgetController.cppRPGWidgetController.cpp Widgets RPGUserWidget.cpp Public AbilitySystemComponent RPGAbilitySystemComponent.hRPGAttributeSet.h Character RPGGameCharacterBase.hRPGGameEnemy.hRPGGamePlayerCharacter.h Game RPGGameModeBase.h Interaction EnemyInterface.h Player RPGPlayerController.hRPGPlayerState.h Actor RPGEffectActor.h UI HUD RPGHUD.h WidgetController OverlayWidgetController.hRPGWidgetController.h Widgets RPGUserWidget.h 文件概述 RPGAttributeSet .h文件 // Copyright KimiLiu #pragma once #include "CoreMinimal.h" #include "AbilitySystemComponent.h" #include "AttributeSet.h" #include "RPGAttributeSet.generated.h" #define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) // 逻辑处理数据结构,在这个结构中能够获取目标ASC以及源头ASC,以及目标对象,源头对象,目标控制器,源头控制器 USTRUCT() struct FEffectProperties { GENERATED_BODY() FEffectProperties(){}; FGameplayEffectContextHandle EffectContextHandle; UPROPERTY() UAbilitySystemComponent* SourceASC = nullptr; UPROPERTY() AActor* SourceAvatarActor = nullptr; UPROPERTY() AController* SourceController = nullptr; UPROPERTY() ACharacter* SourceCharacter = nullptr; UPROPERTY() UAbilitySystemComponent* TargetASC = nullptr; UPROPERTY() AActor* TargetAvatarActor = nullptr; UPROPERTY() AController* TargetController = nullptr; UPROPERTY() ACharacter* TargetCharacter = nullptr; }; /** * AS拥有预测功能(Prediction)能够让多人游戏的客户端在拥有更少的延迟。客户端能够立刻改变自己维护的AS,然后通知服务端,由服务端判定这个更 * 改是否合法,如果不合法,则服务端拒绝更改AS并通知客户端回滚AS */ UCLASS() class AURA_API URPGAttributeSet : public UAttributeSet { GENERATED_BODY() public: URPGAttributeSet(); //复制变量时必须重写的函数,用于注册需要复制的变量 virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override; //在属性被更改前调用,不用来处理逻辑,只用来限制值大小 virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override; //在属性被更改后调用,用来处理逻辑 virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override; /** * 创建AS属性步骤: * 1. 声明FGameplayAttributeData类型变量 * 2. 用UPROPERTY()宏修饰 * 3. 如果是多人游戏,需要在宏内声明: ReplicatedUsing = OnRep_属性名,同时声明一个UFUNCTION()方法OnRep_属性名()。当服务端的该属性 * 值变化时,OnRep_属性名()将会被调用,我们在这个函数内处理变化事件 * 4. 实现OnRep_属性名()函数,在函数内调用GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, 属性名, 旧属性值)宏,用来保存旧值用于 * 服务端通知客户端进行回滚 * 5. 重写GetLifetimeReplicatedProps()函数,在该函数内注册属性 * 6. 使用ATTRIBUTE_ACCESSORS()宏来初始化value_getter, property_getter, value_setter, initter */ UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Health, Category="Vital Attribute") FGameplayAttributeData Health; ATTRIBUTE_ACCESSORS(URPGAttributeSet, Health); UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_MaxHealth, Category="Vital Attribute") FGameplayAttributeData MaxHealth; ATTRIBUTE_ACCESSORS(URPGAttributeSet, MaxHealth); UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Mana, Category="Vital Attribute") FGameplayAttributeData Mana; ATTRIBUTE_ACCESSORS(URPGAttributeSet, Mana); UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Mana, Category="Vital Attribute") FGameplayAttributeData MaxMana; ATTRIBUTE_ACCESSORS(URPGAttributeSet, MaxMana); UFUNCTION() void OnRep_Health(const FGameplayAttributeData& OldHealth) const; UFUNCTION() void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const ; UFUNCTION() void OnRep_Mana(const FGameplayAttributeData& OldMana) const; UFUNCTION() void OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) const; private: //设置FEffectProperties类数据,Props内有很多信息,参照上面的结构体声明 void SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props) const; }; .cpp文件 // Copyright KimiLiu #include "AbilitySytstem/RPGAttributeSet.h" #include "AbilitySystemBlueprintLibrary.h" #include "Net/UnrealNetwork.h" #include "GameplayEffectExtension.h" #include "GameFramework/Character.h" URPGAttributeSet::URPGAttributeSet() { InitHealth(50.f); InitMaxHealth(100.f); InitMana(50.f); InitMaxMana(100.f); } void URPGAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); //注册需要复制的属性Health,没有复制条件,不论复制的结果是否等于客户端原有结果,都进行复制调用 DOREPLIFETIME_CONDITION_NOTIFY(URPGAttributeSet, Health, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(URPGAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(URPGAttributeSet, Mana, COND_None, REPNOTIFY_Always); DOREPLIFETIME_CONDITION_NOTIFY(URPGAttributeSet, MaxMana, COND_None, REPNOTIFY_Always); } void URPGAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) { Super::PreAttributeChange(Attribute, NewValue); if (Attribute == GetHealthAttribute()) { NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth()); } if (Attribute == GetMaxHealthAttribute()) { } if (Attribute == GetManaAttribute()) { NewValue = FMath::Clamp(NewValue, 0.f, GetMaxMana()); } if (Attribute == GetMaxManaAttribute()) { } } void URPGAttributeSet::SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props) const { // Source = causer of the effect, Target = target of the effect (owner of this AS) // 拿到源头的上下文,上下文内拥有指向ASC的指针 Props.EffectContextHandle = Data.EffectSpec.GetContext(); Props.SourceASC = Props.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent(); if (IsValid(Props.SourceASC) && Props.SourceASC->AbilityActorInfo.IsValid() && Props.SourceASC->AbilityActorInfo->AvatarActor.IsValid()) { // 获取逻辑拥有者 Props.SourceAvatarActor = Props.SourceASC->AbilityActorInfo->AvatarActor.Get(); // 获取控制器 Props.SourceController = Props.SourceASC->AbilityActorInfo->PlayerController.Get(); // 若ASC组件没有控制器,则从逻辑拥有者那里获取控制器 if (Props.SourceController == nullptr && Props.SourceAvatarActor != nullptr) { // Source有可能没有Controller if (const APawn* Pawn = Cast<APawn>(Props.SourceAvatarActor)) { Props.SourceAvatarActor = Pawn->GetController(); } } // 获取来源角色 if (Props.SourceController) { Props.SourceCharacter = Cast<ACharacter>(Props.SourceController->GetPawn()); } } // 拿到目标Actor if (Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.IsValid()) { Props.TargetAvatarActor = Data.Target.AbilityActorInfo->AvatarActor.Get(); Props.TargetController = Data.Target.AbilityActorInfo->PlayerController.Get(); Props.TargetCharacter = Cast<ACharacter>(Props.TargetAvatarActor); Props.TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Props.TargetAvatarActor); } } // Data 很强大,我们可以从中拿到任何我们想拿到的东西,但是得注意是否是空指针,需要做判断 void URPGAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) { Super::PostGameplayEffectExecute(Data); // Source = causer of the effect, Target = target of the effect (owner of this AS) FEffectProperties Props; SetEffectProperties(Data, Props); } void URPGAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth) const { // 当Health属性被调用,此函数被调用,传入OldHealth作为旧值,该旧值将会被保存以免服务端通知客户端该属性需要回滚 GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, Health, OldHealth); } void URPGAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const { GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, Health, OldMaxHealth); } void URPGAttributeSet::OnRep_Mana(const FGameplayAttributeData& OldMana) const { GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, Mana, OldMana); } void URPGAttributeSet::OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) const { GAMEPLAYATTRIBUTE_REPNOTIFY(URPGAttributeSet, MaxMana, OldMaxMana); } RPGEffectActor .h文件 // Copyright KimiLiu #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "GameplayEffectTypes.h" #include "RPGEffectActor.generated.h" class UAbilitySystemComponent; class UGameplayEffect; // GE实施策略 UENUM(BlueprintType) enum class EEffectApplicationPolicy { ApplyOnOverlap, ApplyOnEndOverlap, DoNotApply }; // GE移除策略 UENUM(BlueprintType) enum class EEffectRemovalPolicy { RemoveOnEndOverlap, DoNotRemove }; UCLASS() class AURA_API ARPGEffectActor : public AActor { GENERATED_BODY() public: ARPGEffectActor(); protected: virtual void BeginPlay() override; // 对目标实施GE UFUNCTION(BlueprintCallable) void ApplyEffectToTarget(AActor* TargetActor , TSubclassOf<UGameplayEffect> GameplayEffectClass); // 自定义重叠开始事件 UFUNCTION(BlueprintCallable) void OnOverlap(AActor* TargetActor); // 自定义重叠结束事件 UFUNCTION(BlueprintCallable) void OnEndOverlap(AActor* TargetActor); // ~ Start GE类型以及GE的调用类型(蓝图内设置) UPROPERTY(EditAnywhere, BlueprintReadOnly, Category= "Applied Effects") TArray<TSubclassOf<UGameplayEffect>> InstantGameplayEffectClasses; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category= "Applied Effects") EEffectApplicationPolicy InstantEffectApplicationPolicy = EEffectApplicationPolicy::DoNotApply; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category= "Applied Effects") TArray<TSubclassOf<UGameplayEffect>> DurationGameplayEffectClasses; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category= "Applied Effects") EEffectApplicationPolicy DurationEffectApplicationPolicy = EEffectApplicationPolicy::DoNotApply; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category= "Applied Effects") TArray<TSubclassOf<UGameplayEffect>> InfiniteGameplayEffectClasses; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category= "Applied Effects") EEffectApplicationPolicy InfiniteEffectApplicationPolicy = EEffectApplicationPolicy::DoNotApply; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category= "Applied Effects") EEffectRemovalPolicy InfiniteEffectRemovalPolicy = EEffectRemovalPolicy::RemoveOnEndOverlap; // ~ End GE类型以及GE的调用类型(蓝图内设置) TMap<FActiveGameplayEffectHandle, UAbilitySystemComponent*> ActiveEffectHandles; // ~ Actor 等级设置 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category= "Applied Effects") float ActorLevel = 1.f; }; .cpp文件 // Copyright KimiLiu #include "Actor/RPGEffectActor.h" #include "AbilitySystemComponent.h" #include "AbilitySystemBlueprintLibrary.h" // Sets default values ARPGEffectActor::ARPGEffectActor() { PrimaryActorTick.bCanEverTick = false; SetRootComponent(CreateDefaultSubobject<USceneComponent>("SceneRoot")); } void ARPGEffectActor::BeginPlay() { Super::BeginPlay(); } void ARPGEffectActor::ApplyEffectToTarget(AActor* TargetActor, TSubclassOf<UGameplayEffect> GameplayEffectClass) { UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor); if (TargetASC == nullptr)return; check(GameplayEffectClass); //GameplayEffectContext: GE的背景,例如谁造成了影响(Instigator), 这个GE的目标是谁(TargetActor), GE描述(例如这个GE是火焰效果或是 // 雷电效果) FGameplayEffectContextHandle EffectContextHandle = TargetASC->MakeEffectContext(); EffectContextHandle.AddSourceObject(this); //创造一个FGameplayEffectSpecHandle对象出来 const FGameplayEffectSpecHandle EffectSpec = TargetASC->MakeOutgoingSpec( GameplayEffectClass, ActorLevel, EffectContextHandle); //将SpecHandle中的Spec对象传入,同时获取ActiveGameplayEffectHandle用于存储在Map中 const FActiveGameplayEffectHandle ActiveEffectHandle = TargetASC->ApplyGameplayEffectSpecToSelf(*EffectSpec.Data.Get()); //如果是Infinite类型的GE,同时会在停止重叠时移除的GE,我们会将其存储起来,等到停止重叠时直接调用函数来移除GE if(EffectSpec.Data.Get()->Def.Get()->DurationPolicy == EGameplayEffectDurationType::Infinite&& InfiniteEffectRemovalPolicy == EEffectRemovalPolicy::RemoveOnEndOverlap) { ActiveEffectHandles.Add(ActiveEffectHandle, TargetASC); } } void ARPGEffectActor::OnOverlap(AActor* TargetActor) { if (InstantEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnOverlap) { for (const TSubclassOf<UGameplayEffect> InstantGameplayEffectClass : InstantGameplayEffectClasses) { ApplyEffectToTarget(TargetActor, InstantGameplayEffectClass); } } if (DurationEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnOverlap) { for (const TSubclassOf<UGameplayEffect> DurationGameplayEffectClass : DurationGameplayEffectClasses) { ApplyEffectToTarget(TargetActor, DurationGameplayEffectClass); } } if (InfiniteEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnOverlap) { for (const TSubclassOf<UGameplayEffect> InfiniteGameplayEffectClass : InfiniteGameplayEffectClasses) { ApplyEffectToTarget(TargetActor, InfiniteGameplayEffectClass); } } } void ARPGEffectActor::OnEndOverlap(AActor* TargetActor) { if (InstantEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnEndOverlap) { for (const TSubclassOf<UGameplayEffect> InstantGameplayEffectClass : InstantGameplayEffectClasses) { ApplyEffectToTarget(TargetActor, InstantGameplayEffectClass); } } if (DurationEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnEndOverlap) { for (const TSubclassOf<UGameplayEffect> DurationGameplayEffectClass : DurationGameplayEffectClasses) { ApplyEffectToTarget(TargetActor, DurationGameplayEffectClass); } } if (InfiniteEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnEndOverlap) { for (const TSubclassOf<UGameplayEffect> InfiniteGameplayEffectClass : InfiniteGameplayEffectClasses) { ApplyEffectToTarget(TargetActor, InfiniteGameplayEffectClass); } } if (InfiniteEffectRemovalPolicy == EEffectRemovalPolicy::RemoveOnEndOverlap) { UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor); if (!IsValid(TargetASC)) return; TArray<FActiveGameplayEffectHandle> HandlesToRemove; for (TTuple<FActiveGameplayEffectHandle, UAbilitySystemComponent*> HandlePair : ActiveEffectHandles) { if (HandlePair.Value == TargetASC) { TargetASC->RemoveActiveGameplayEffect(HandlePair.Key, 1); HandlesToRemove.Add(HandlePair.Key); } } for (FActiveGameplayEffectHandle& Handle : HandlesToRemove) { ActiveEffectHandles.FindAndRemoveChecked(Handle); } } }第5章---GameplayEffect的使用由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“第5章---GameplayEffect的使用”
下一篇
SpringTest常见错误