前の章では、GameplayEffect を介して血液量やマナ量などの属性値の変更を実装しましたが、誰もが最大値と最小値を制限する最大血液量と最大マナ量を持っています。ヘルス量とマナ量は同じではありません。ゼロ未満になります。これまで関連する制限を実装していなかったので、次に、AttributeSet 関数で実際の値の範囲制限を実装する必要があります。
成し遂げる
まず、PreAttributeChange() 関数で親クラス関数をオーバーライドします。この関数は、AttributeSet の監視対象の値が変更される前にコールバックをトリガーします。
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
コールバックは 2 つのパラメータを返します。1 つは属性です。この値を使用して、どの属性が変更されたかを判断できます。もう 1 つは変更される値です。次に、値を出力して結果を確認します。
if(Attribute == GetHealthAttribute())
{
UE_LOG(LogTemp, Warning, TEXT("Health: %f"), NewValue);
}
if(Attribute == GetMaxHealthAttribute())
{
UE_LOG(LogTemp, Warning, TEXT("MaxHealth: %f"), NewValue);
}
if(Attribute == GetManaAttribute())
{
UE_LOG(LogTemp, Warning, TEXT("Mana: %f"), NewValue);
}
if(Attribute == GetMaxManaAttribute())
{
UE_LOG(LogTemp, Warning, TEXT("MaxMana: %f"), NewValue);
}
UE をコンパイルして開き、シーンの左下隅にある出力ログをクリックします。
レイアウトにドッキングすることを選択し
、キャラクターに薬瓶、クリスタルを食べさせ、火を踏ませて属性の変化を確認します。すべてのことがわかります。属性の変更は上記を印刷し
てからクランプ関数を使用してヘルスとマナの値を 0 から最大ヘルスとマナの範囲に制限することで正しく反映できます。
NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth());
UEを実行して再度確認すると、値が範囲内に制限されていることがわかります。
ゲームプレイ後エフェクト実行
PostGameplayEffectExecute() 関数は、値の変更後にトリガーされますが、通常、Instant タイプの GameplayEffect によってのみトリガーできます (Period が設定されている場合は、Duration および Infinite クラスの GameplayEffect もトリガーできます)。
この関数には多くの応用シナリオがあり、死亡、無敵、血液控除なしなどの論理演算を行うことができます。
これを使用するには、まず親クラスをオーバーライドする必要があります
virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
戻りパラメータは Data のみですが、Data には多くのコンテンツが含まれています。
if(Data.EvaluatedData.Attribute == GetHealthAttribute())
{
UE_LOG(LogTemp, Warning, TEXT("Health: %f"), GetHealth());
UE_LOG(LogTemp, Warning, TEXT("Magnitude: %f"), Data.EvaluatedData.Magnitude);
}
UE を開いて、現在の血液量とこのエフェクトによって引き起こされるダメージ値を確認します。
ブレークポイントにヒットすると、
Data に 3 つのデータがあることがわかります。EffectSpec
は、大量のデータを含むエフェクトのインスタンスです。これを使用して、どのアクターがこの GE をターゲットに適用したかを取得できます
。EvaluatedData は、変更されたデータに関連するコンテンツ 現在の値、変更された値の数、変更された属性など
Target はターゲットの ASC
次に、Data から必要なものを取得してカプセル化します後で使用するための構造。
まず、GE のキャストに関連するオブジェクトとターゲットに関連するオブジェクトを格納する FEffectProperties 構造体を作成します。この構造体は、GE コンテキストと、キャスターとターゲットの ASC AvatarActor コントローラー キャラクターを保存します。
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;
};
次に、生成された構造属性の値を処理するプライベート関数を作成します。この関数は 2 つの値を受け取ります。1 つは PostGameplayEffectExecute() 関数によって返されるデータで、もう 1 つは入力する必要がある構造です。
static void SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props);
次に、関数を実装し、関数内でプロパティを設定します。前述したように、関連するプロパティは Data を通じて取得できます。
void UAttributeSetBase::SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props)
{
//Source 效果的所有者 Target 效果应用的目标
Props.EffectContextHandle = Data.EffectSpec.GetContext();
Props.SourceASC = Props.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent(); //获取效果所有者的ASC
//获取效果所有者的相关对象
if(IsValid(Props.SourceASC) && Props.SourceASC->AbilityActorInfo.IsValid() && Props.SourceASC->AbilityActorInfo->AvatarActor.IsValid())
{
Props.SourceAvatarActor = Props.SourceASC->AbilityActorInfo->AvatarActor.Get(); //获取Actor
Props.SourceController = Props.SourceASC->AbilityActorInfo->PlayerController.Get(); //获取PlayerController
if(Props.SourceController == nullptr && Props.SourceAvatarActor != nullptr)
{
if(const APawn* Pawn = Cast<APawn>(Props.SourceAvatarActor))
{
Props.SourceController = Pawn->GetController();
}
}
if(Props.SourceController)
{
Props.SourceCharacter = Cast<ACharacter>(Props.SourceController->GetPawn());
}
}
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);
}
}
次に、PostGameplayEffectExecute() 内で構造体を作成し、関数を呼び出してコンテンツを生成するだけです。
void UAttributeSetBase::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
FEffectProperties Props;
SetEffectProperties(Data, Props);
}
これを使用すると、構造体を通じて対応するコンテンツを取得でき、ロジックがよりすっきりします。
ソースコード
属性セットベース.h
// 版权归暮志未晚所有。
#pragma once
#include "CoreMinimal.h"
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"
#include "AttributeSetBase.generated.h"
// Uses macros from AttributeSet.h
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
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;
};
/**
* 技能系统属性集
*/
UCLASS()
class AURA_API UAttributeSetBase : public UAttributeSet
{
GENERATED_BODY()
public:
UAttributeSetBase();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
UPROPERTY(BlueprintReadOnly,ReplicatedUsing = OnRep_Health, Category="Vital Attributes")
FGameplayAttributeData Health;
ATTRIBUTE_ACCESSORS(UAttributeSetBase, Health);
UPROPERTY(BlueprintReadOnly,ReplicatedUsing = OnRep_MaxHealth, Category="Vital Attributes")
FGameplayAttributeData MaxHealth;
ATTRIBUTE_ACCESSORS(UAttributeSetBase, MaxHealth);
UPROPERTY(BlueprintReadOnly,ReplicatedUsing = OnRep_Mana, Category="Vital Attributes")
FGameplayAttributeData Mana;
ATTRIBUTE_ACCESSORS(UAttributeSetBase, Mana);
UPROPERTY(BlueprintReadOnly,ReplicatedUsing = OnRep_MaxMana, Category="Vital Attributes")
FGameplayAttributeData MaxMana;
ATTRIBUTE_ACCESSORS(UAttributeSetBase, 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:
static void SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props);
};
属性セットベース.cpp
// 版权归暮志未晚所有。
#include "AbilitySystem/AttributeSetBase.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "GameplayEffectExtension.h"
#include "GameFramework/Character.h"
#include "Net/UnrealNetwork.h"
UAttributeSetBase::UAttributeSetBase()
{
InitHealth(30.f);
InitMaxHealth(100.f);
InitMana(30.f);
InitMaxMana(100.f);
}
void UAttributeSetBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Health, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, MaxHealth, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Mana, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, MaxMana, COND_None, REPNOTIFY_Always);
}
void UAttributeSetBase::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
Super::PreAttributeChange(Attribute, NewValue);
if(Attribute == GetHealthAttribute())
{
NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth());
// UE_LOG(LogTemp, Warning, TEXT("Health: %f"), NewValue);
}
if(Attribute == GetManaAttribute())
{
NewValue = FMath::Clamp(NewValue, 0.f, GetMaxMana());
}
}
void UAttributeSetBase::SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props)
{
//Source 效果的所有者 Target 效果应用的目标
Props.EffectContextHandle = Data.EffectSpec.GetContext();
Props.SourceASC = Props.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent(); //获取效果所有者的ASC
//获取效果所有者的相关对象
if(IsValid(Props.SourceASC) && Props.SourceASC->AbilityActorInfo.IsValid() && Props.SourceASC->AbilityActorInfo->AvatarActor.IsValid())
{
Props.SourceAvatarActor = Props.SourceASC->AbilityActorInfo->AvatarActor.Get(); //获取Actor
Props.SourceController = Props.SourceASC->AbilityActorInfo->PlayerController.Get(); //获取PlayerController
if(Props.SourceController == nullptr && Props.SourceAvatarActor != nullptr)
{
if(const APawn* Pawn = Cast<APawn>(Props.SourceAvatarActor))
{
Props.SourceController = Pawn->GetController();
}
}
if(Props.SourceController)
{
Props.SourceCharacter = Cast<ACharacter>(Props.SourceController->GetPawn());
}
}
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);
}
}
void UAttributeSetBase::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
FEffectProperties Props;
SetEffectProperties(Data, Props);
}
void UAttributeSetBase::OnRep_Health(const FGameplayAttributeData& OldHealth) const
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, Health, OldHealth);
}
void UAttributeSetBase::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, MaxHealth, OldMaxHealth);
}
void UAttributeSetBase::OnRep_Mana(const FGameplayAttributeData& OldMana) const
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, MaxHealth, OldMana);
}
void UAttributeSetBase::OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) const
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, MaxHealth, OldMaxMana);
}