第5章---GameplayEffect的使用
由于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 Effects
- Deny Overflow Application
- Clear Stack on Overflow
-
Expiration
- Premature Expiration Effect Classes
- Routine Expiraction Effect Classes
-
Immunity
-
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意为在一段时间内应用GE
- Duration Magnitude : 意为GE的持续时间
- Magnitude Calculation Type : 有四个选项,Scalable Float, Attribute Based, Custom Calculation Class, SetbyCallse
- Scalable 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, SetbyCallse
- Scalable Float Magnitude : 根据Magnitude Calculation Type进行改变,Scalable Float时填入float数据或数据表格,意为直接使用float内的数据或表格数据,此时float数据为乘数
- Source Tags
- Require Tags
- Ignore Tags
- Target Tags
- Require Tags
- Ignore Tags
- Executions
- 数组
- Calculations Class
- Conditional Gameplay Effects
- 数组
- Conditional Gameplay Effects
- 数组
-
Application
- Chance to Apply to Target
- Application Requirement
-
显示
- Require Modifiers Success to Trigger Cues
- Supress Stacking Cuse
- Gameplay Cues
- UIData
-
标签
- Gameplay Effect Asset Tag : 与父类相关,不会将其中拥有的Tag赋予给施加GE的Actor的ASC上
- Combined Tags : Added中提及以及Removed没有提及的所有Tag
- Added : 添加的Tag
- Removed : 移除的Tag
- Granted Tags : 在其中的Tag将会被附加到Actor的ASC上
- Granted Bloked Ability Tags
- Ongoing Tag Requirements
- Application Tag Requirements
- Removal Tag Requirements
- Remove Gameplay Effect Query
- Remove Gameplay Effect with Tags
- Gameplay Effect Asset Tag : 与父类相关,不会将其中拥有的Tag赋予给施加GE的Actor的ASC上
文件结构
Source
- Private
- AbilitySystemComponen
- RPGAbilitySystemComponent.cpp
- RPGAttributeSet.cpp
- Character
- PGGameCharacterBase.cpp
- RPGGameEnemy.cpp
- RPGGamePlayerCharacter.cpp
- Game
- RPGGameModeBase.cpp
- Interaction
- EnemyInterface.cpp
- Player
- RPGPlayerController.cpp
- RPGPlayerState.cpp
- Actor
- RPGEffectActor.cpp
- UI
- HUD
- RPGHUD.cpp
- WidgetController
- OverlayWidgetController.cpp
- RPGWidgetController.cpp
- Widgets
- RPGUserWidget.cpp
- HUD
- AbilitySystemComponen
- Public
- AbilitySystemComponent
- RPGAbilitySystemComponent.h
- RPGAttributeSet.h
- Character
- RPGGameCharacterBase.h
- RPGGameEnemy.h
- RPGGamePlayerCharacter.h
- Game
- RPGGameModeBase.h
- Interaction
- EnemyInterface.h
- Player
- RPGPlayerController.h
- RPGPlayerState.h
- Actor
- RPGEffectActor.h
- UI
- HUD
- RPGHUD.h
- WidgetController
- OverlayWidgetController.h
- RPGWidgetController.h
- Widgets
- RPGUserWidget.h
- HUD
- AbilitySystemComponent
文件概述
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);
}
}
}