c8.GAS|Gameplay Ability System
大约 16 分钟
GAS概述
Gameplay技能系统(Gameplay Ability System) 是构建Actor可以拥有和触发的技能和交互的一种框架。
基本流程
插件引入
模块引入
PublicDependencyModuleNames.AddRange(new string[] {
"GameplayTags",
"GameplayTasks",
"GameplayAbilities",
环境搭建
AbilitySystemComponent(ASC)组件类
知识储备
AS(AttributeSet)属性集
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayAttributeData
{
GENERATED_BODY()
FGameplayAttributeData()
: BaseValue(0.f)
, CurrentValue(0.f)
{}
FGameplayAttributeData(float DefaultValue)
: BaseValue(DefaultValue)
, CurrentValue(DefaultValue)
{}
virtual ~FGameplayAttributeData()
{}
/** Returns the current value, which includes temporary buffs */
float GetCurrentValue() const;
/** Modifies current value, normally only called by ability system or during initialization */
virtual void SetCurrentValue(float NewValue);
/** Returns the base value which only includes permanent changes */
float GetBaseValue() const;
/** Modifies the permanent base value, normally only called by ability system or during initialization */
virtual void SetBaseValue(float NewValue);
protected:
UPROPERTY(BlueprintReadOnly, Category = "Attribute")
float BaseValue;
UPROPERTY(BlueprintReadOnly, Category = "Attribute")
float CurrentValue;
};
BaseValue vs. CurrentValue
ExorcistAttributeSet.h
#pragma once
#include "CoreMinimal.h"
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"
#include "ExorcistAttributeSet.generated.h"
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
UCLASS()
class EXORCIST_API UExorcistAttributeSet : public UAttributeSet
{
GENERATED_BODY()
public:
UExorcistAttributeSet();
//2.重写复制函数
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
//1.设置Health
UPROPERTY(BlueprintReadOnly, Category = "Health", ReplicatedUsing = OnRep_Health)
FGameplayAttributeData Health;
ATTRIBUTE_ACCESSORS(UExorcistAttributeSet, Health);
//3.客户端上接收属性更改
UFUNCTION()
void OnRep_Health(const FGameplayAttributeData& OldHealth);
};
ExorcistAttributeSet.cpp
#include "AbilitySystem/Attributes/ExorcistAttributeSet.h"
#include "Net/UnrealNetwork.h"
UExorcistAttributeSet::UExorcistAttributeSet()
{
InitHealth(100.f);
}
void UExorcistAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION_NOTIFY(UExorcistAttributeSet, Health, COND_None, REPNOTIFY_Always);
}
void UExorcistAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UExorcistAttributeSet, Health, OldHealth);
}
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
#define GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
static FGameplayAttribute Get##PropertyName##Attribute() \
{ \
static FProperty* Prop = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName)); \
return Prop; \
}
#define GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
FORCEINLINE float Get##PropertyName() const \
{ \
return PropertyName.GetCurrentValue(); \
}
#define GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
FORCEINLINE void Set##PropertyName(float NewVal) \
{ \
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent(); \
if (ensure(AbilityComp)) \
{ \
AbilityComp->SetNumericAttributeBase(Get##PropertyName##Attribute(), NewVal); \
}; \
}
#define GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) \
FORCEINLINE void Init##PropertyName(float NewVal) \
{ \
PropertyName.SetBaseValue(NewVal); \
PropertyName.SetCurrentValue(NewVal); \
}
//////////////////////////////////////////////////////////////////
// 玩家选择的英雄数据
public:
UFUNCTION()
const TMap<FName, int32>& GetHeroesSkinIDMap() const { return HeroesSkinIDMap; }
UFUNCTION()
void SetHeroesSkinIDMap(TMap<FName, int32>& InPlayerSelectedHero) { HeroesSkinIDMap = InPlayerSelectedHero; }
private:
/** 玩家最终确定选择的英雄映射表. */
UPROPERTY(Config)
TMap<FName, int32> HeroesSkinIDMap;
//////////////////////////////////////////////////////////////////
// end玩家选择的英雄数据
GameplayEffect
死物(Passive Entities):
- 效果生成器(Effect Generator): 死物负责产生某种效果的实体,比如道具、装备、魂玉等。
- 效果(Effect): 由效果生成器产生的具体效果,例如打折、恢复血量等。
活物(Active Entities):
- 属性承担者(Attribute Bearer): 活物是具有属性的实体,例如玩家和NPC。
- 行为触发器(Action Trigger): 活物通过行为触发器来影响自身的属性,例如装备魂玉、使用血包等。
Modifiers
组件初始化
角色基类.h
public:
virtual UYourAbilitySystemComponent* GetAbilitySystemComponent() const override;
UAttributeSet* GetAttributeSet() const{return AttributeSet;};
protected:
virtual void BeginPlay() override;
UPROPERTY()
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
UPROPERTY()
TObjectPtr<UAttributeSet> AttributeSet;
角色基类.cpp
UAbilitySystemComponent* AExorcistCharacterBase::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
ANPC::ANPC()
{
AbilitySystemComponent = CreateDefaultSubobject<UYourAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AbilitySystemComponent->SetIsReplicated(true);//复制能力
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Minimal);//适用于多人联机的NPC
AttributeSet = CreateDefaultSubobject<UYourAttributeSet>(TEXT("AttributeSet"));
}
void ANPC::BeginPlay()
{
Super::BeginPlay();
AbilitySystemComponent->InitAbilityActorInfo(this, this);
}
复制模式|同步模式
复制模式 | 使用场景 | 描述 |
---|---|---|
Full | 单人游戏 | 每个 GameplayEffect 都会被复制到每个客户端。 |
Mixed | 多人游戏,玩家控制的角色 | GameplayEffects 仅被复制到拥有它的客户端。仅 GameplayTags 和 GameplayCues 被复制给所有人。 |
Minimal | 多人游戏,AI 控制的角色 | GameplayEffects 不会被复制给任何人。仅 GameplayTags 和 GameplayCues 被复制给所有人。 |
AYourPlayerState::AYourPlayerState()
{
NetUpdateFrequency = 100.f;//网络更新速度
AbilitySystemComponent = CreateDefaultSubobject<UYourAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AbilitySystemComponent->SetIsReplicated(true);//复制能力
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);//适用于多人联机的玩家
AttributeSet = CreateDefaultSubobject<UYourAttributeSet>(TEXT("AttributeSet"));
}
UAbilitySystemComponent* AYourPlayerState::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
virtual void PossessedBy(AController* NewController) override;
virtual void OnRep_PlayerState() override;
void AHeroCharacter::InitAbilityActorInfo()
{
//拿到PlayState
AExorcistPlayerState* ExorcistPlayerState =GetPlayerState<AExorcistPlayerState>();
if(!ExorcistPlayerState)
{
return;
}
//利用PlayerState初始化组件
ExorcistPlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(ExorcistPlayerState, this);
//赋值指针
AbilitySystemComponent = ExorcistPlayerState->GetAbilitySystemComponent();
AttributeSet = ExorcistPlayerState->GetAttributeSet();
if (IsLocallyControlled())
{
//调用子系统,利用子系统转发GE委托或者能力等
UExorcistAbilitySubsystem* ExorcistAbilitySubsystem = UGameplayStatics::GetGameInstance(this)->GetSubsystem<UExorcistAbilitySubsystem>();
if(!ExorcistAbilitySubsystem)
{
return;
}
//子系统初始化组件,并将初始GE加载显示。
ExorcistAbilitySubsystem->InitOverlay(AbilitySystemComponent, AttributeSet);
ExorcistAbilitySubsystem->BroadCastInitialValues();
}
}
void AHeroCharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
//Server
InitAbilityActorInfo();
}
void AHeroCharacter::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
//Client
InitAbilityActorInfo();
}
//使用GE,传入拥有组件的Actor和目标Actor以及对应的GE。
UFUNCTION(BlueprintCallable, Category = "AbilitySubsystem")
void ApplyEffectToTarget(AActor* SourceActor,AActor* TargetActor,TSubclassOf<UGameplayEffect> GameplayEffect);
void UExorcistAbilitySubsystem::ApplyEffectToTarget(AActor* SourceActor ,AActor* TargetActor, TSubclassOf<UGameplayEffect> GameplayEffect)
{
//检查传入的对象
if(SourceActor == nullptr||TargetActor == nullptr||GameplayEffect == nullptr)
{
return;
}
//获取要使用的对象GAS组件
UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);
if(TargetASC == nullptr)
{
return;
}
FGameplayEffectContextHandle Context = TargetASC->MakeEffectContext();
Context.AddSourceObject(SourceActor);
FGameplayEffectSpecHandle SpecHandle = TargetASC->MakeOutgoingSpec(GameplayEffect, 1, Context);
TargetASC->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), TargetASC);
}
监听Attribute变化
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSetBase->GetHealthAttribute()).AddUObject(this, &AGDPlayerState::HealthChanged);
virtual void HealthChanged(const FOnAttributeChangeData& Data);
你的子系统.h
#pragma once
#include "CoreMinimal.h"
#include "GameplayEffectTypes.h"
#include "GameplayEffect.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "ExorcistAbilitySubsystem.generated.h"
class UExorcistAttributeSet;
class UAbilitySystemComponent;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChangeDelegate,float,NewHealth);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMaxHealthChangeDelegate,float,NewMaxHealth);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnArmorChangeDeleagate,float,NewArmor);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMaxArmorChangeDeleagate,float,NewMaxArmor);
UCLASS()
class EXORCIST_API UExorcistAbilitySubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
//委托
UPROPERTY(BlueprintAssignable, Category = "AbilitySubsystem|Health")
FOnHealthChangeDelegate OnHealthChanged;
UPROPERTY(BlueprintAssignable, Category = "AbilitySubsystem|MaxHealth")
FOnMaxHealthChangeDelegate OnMaxHealthChanged;
UPROPERTY(BlueprintAssignable, Category = "AbilitySubsystem|Armor")
FOnArmorChangeDeleagate OnArmorChanged;
UPROPERTY(BlueprintAssignable, Category = "AbilitySubsystem|MaxArmor")
FOnMaxArmorChangeDeleagate OnMaxArmorChanged;
//默认接口
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
//初始化组件
UFUNCTION(BlueprintCallable, Category = "AbilitySubsystem")
void InitOverlay(UAbilitySystemComponent* Asc,UAttributeSet* As);
//通过子系统转发AS属性更新
UFUNCTION(BlueprintCallable, Category = "AbilitySubsystem")
void BroadCastInitialValues();
//使用GE,传入拥有组件的Actor和目标Actor以及对应的GE。
UFUNCTION(BlueprintCallable, Category = "AbilitySubsystem")
void ApplyEffectToTarget(AActor* SourceActor,AActor* TargetActor,TSubclassOf<UGameplayEffect> GameplayEffect,int32 Level=1);
//尝试移除TargetActor身上某种GE。
UFUNCTION(BlueprintCallable, Category = "AbilitySubsystem")
void RemoveTargetEffectByClass(AActor* TargetActor, TSubclassOf<UGameplayEffect> GameplayEffect);
//使用GE,传入拥有组件的Actor和目标Actor以及对应的GE。
UFUNCTION(BlueprintCallable, Category = "AbilitySubsystem")
void UpdateAttribute( FGameplayAttribute Attribute, float NewBaseValue);
protected:
UPROPERTY(BlueprintReadOnly,Category="AbilitySubsystem")
TObjectPtr<UAttributeSet> AttributeSet;
UPROPERTY(BlueprintReadOnly,Category = "AbilitySubsystem")
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
};
你的子系统.cpp
#include "Subsystem/ExorcistAbilitySubsystem.h"
#include "AbilitySystem/Attributes/ExorcistAttributeSet.h"
#include "FrameWork/ExorcistPlayerState.h"
bool UExorcistAbilitySubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
return Super::ShouldCreateSubsystem(Outer);
}
void UExorcistAbilitySubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
}
void UExorcistAbilitySubsystem::Deinitialize()
{
Super::Deinitialize();
}
void UExorcistAbilitySubsystem::InitOverlay(UAbilitySystemComponent* Asc, UAttributeSet* As)
{
if (!Asc || !As)
{
return;
}
AbilitySystemComponent = Asc;
AttributeSet = As;
const UExorcistAttributeSet* ExorcistAttributeSet = Cast<UExorcistAttributeSet>(AttributeSet);
if (!ExorcistAttributeSet)
{
return;
}
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(ExorcistAttributeSet->GetHealthAttribute()).
AddLambda(
[this](const FOnAttributeChangeData& OnAttributeChangeData)
{
OnHealthChanged.Broadcast(OnAttributeChangeData.NewValue);
}
);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(ExorcistAttributeSet->GetMaxHealthAttribute()).
AddLambda(
[this](const FOnAttributeChangeData& OnAttributeChangeData)
{
OnMaxHealthChanged.Broadcast(OnAttributeChangeData.NewValue);
}
);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(ExorcistAttributeSet->GetArmorAttribute()).
AddLambda(
[this](const FOnAttributeChangeData& OnAttributeChangeData)
{
OnArmorChanged.Broadcast(OnAttributeChangeData.NewValue);
}
);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(ExorcistAttributeSet->GetMaxArmorAttribute()).
AddLambda(
[this](const FOnAttributeChangeData& OnAttributeChangeData)
{
OnMaxArmorChanged.Broadcast(OnAttributeChangeData.NewValue);
}
);
}
void UExorcistAbilitySubsystem::BroadCastInitialValues()
{
if (AttributeSet && AbilitySystemComponent)
{
const UExorcistAttributeSet* ExorcistAttributeSet = CastChecked<UExorcistAttributeSet>(AttributeSet);
OnHealthChanged.Broadcast(ExorcistAttributeSet->GetHealth());
OnMaxHealthChanged.Broadcast(ExorcistAttributeSet->GetMaxHealth());
OnArmorChanged.Broadcast(ExorcistAttributeSet->GetArmor());
OnMaxArmorChanged.Broadcast(ExorcistAttributeSet->GetMaxArmor());
}
}
void UExorcistAbilitySubsystem::ApplyEffectToTarget(AActor* SourceActor, AActor* TargetActor,
TSubclassOf<UGameplayEffect> GameplayEffect,int32 Level)
{
//检查传入的对象
if (SourceActor == nullptr || TargetActor == nullptr || GameplayEffect == nullptr)
{
return;
}
//获取要使用的对象GAS组件
UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);
if (TargetASC == nullptr)
{
return;
}
FGameplayEffectContextHandle Context = TargetASC->MakeEffectContext();
Context.AddSourceObject(SourceActor);
FGameplayEffectSpecHandle SpecHandle = TargetASC->MakeOutgoingSpec(GameplayEffect, Level, Context);
FGameplayTagContainer EffectTags;
TargetASC->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), TargetASC);
//这个可以返回所有目标拥有的GETag
if(TargetActor->GetNetOwningPlayer())
{
SpecHandle.Data.Get()->GetAllAssetTags(EffectTags);
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::SanitizeFloat(EffectTags.Num()));
}
}
void UExorcistAbilitySubsystem::RemoveTargetEffectByClass(AActor* TargetActor, TSubclassOf<UGameplayEffect> GameplayEffect)
{
//检查传入的对象
if ( TargetActor == nullptr || GameplayEffect == nullptr)
{
return;
}
//获取要使用的对象GAS组件
UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);
if (TargetASC == nullptr)
{
return;
}
TargetASC->RemoveActiveGameplayEffectBySourceEffect(GameplayEffect, TargetASC);
}
void UExorcistAbilitySubsystem::UpdateAttribute(FGameplayAttribute Attribute, float NewBaseValue)
{
if (AbilitySystemComponent)
{
AbilitySystemComponent->SetNumericAttributeBase(Attribute, NewBaseValue);
}
}
Debug
GameAbility
TryActivateAbility() - 尝试激活能力。调用CanActivateAbility()。输入事件可以直接调用此函数。
- 还处理执行逻辑的实例化和复制/预测调用。
CallActivateAbility() - 受保护的非虚函数。执行一些样板“预激活”工作,然后调用ActivateAbility()。
ActivateAbility() - 能力的执行函数。子类希望重写此函数。
CommitAbility() - 提交资源/冷却等。ActivateAbility() 必须调用此函数!
CancelAbility() - 中断能力(来自外部源)。
EndAbility() - 能力已结束。这是由能力自身调用以结束自身的。