Gameplay Ability System では、Gameplay Attributes (FGameplayAttribute
) を使用して、ゲームプレイ関連の浮動小数点値の格納、計算、修正を実行します。これらの値は、キャラクターの残りの体力ポイント、車両の最高速度、アイテムが破損するまでに使用できる回数など、オーナーの特徴を表すことができます。Gameplay Ability System のアクタは、ゲームプレイ アトリビュートを Attribute Set に格納します。アトリビュート セットは、ゲームプレイ アトリビュートとこのシステムの他の要素との間のインタラクションの管理をサポートし、アトリビュート セット自体をアクタの Ability System コンポーネントに登録します。これらのインタラクションには、値範囲のクランプ、ベース値を永続的に変更するイベントへの反応、および一時的な値の変更を適用する計算の実行などがあります。
ゲームプレイ アトリビュート
ゲームプレイ アトリビュートは、現在の値とベース値を保持します。「現在」の値は、ほとんどの計算やロジックで使用され、現在アクティブな ゲームプレイ エフェクト の影響を受ける可能性がありますが、「ベース」値は、ほとんどの場合、長期間固定されます。 例えば、「Jump Height (ジャンプの高さ)」ゲームプレイ アトリビュートのベース値は 100.0 ですが、キャラクターが「疲労で通常の高さの 70% までしかジャンプできない」というゲームプレイ エフェクトを有効にしている場合、現在の値は 70.0 になります。そのキャラクターが、レベルアップシステムなどを通じて永続的にジャンプが上達すれば、ベース値が 110.0 に上がる可能性があります。ただし、ゲームプレイ エフェクトが設定されている限り、現在の値は 77.0 として計算されます。
ゲームプレイ アトリビュートを作成するには、まずアトリビュート セットを作成します。その後、そのアトリビュート セットにゲームプレイ アトリビュートを追加できます。
ゲームプレイ アトリビュートは、アトリビュート セットなしでも存在できます。この場合は、通常、適切なタイプのゲームプレイ アトリビュートを含むアトリビュート セットを持たない Ability System コンポーネント にゲームプレイ アトリビュートが格納されていることを示しています。この状態は推奨されません。ゲームプレイ アトリビュートは、浮動小数点値を格納するほか、Gameplay Ability System のいずれかの要素と相互に作用する定義された動作を持たないためです。
アトリビュート セット
定義および設定
まず、1 つまたは複数のゲームプレイ アトリビュートを持つアトリビュート セットを作成し、Ability System コンポーネントに登録します。
ベースの Attribute Set クラス
UAttributeSet
を拡張し、ゲームプレイ アトリビュートをFGameplayAttributeData
UProperties として追加します。ゲームプレイ アトリビュートを 1 つ含むシンプルなアトリビュート セットは次のようになります。GENERATED_BODY() public: /** Sample "Health" Attribute, publicly accessible */ UPROPERTY(EditAnywhere, BlueprintReadOnly) FGameplayAttributeData Health;
このアトリビュート セットをアクタに格納して、エンジンに公開します。
const
キーワードを使用して、コードがアトリビュート セットを直接変更できないようにします。これを次のようにアクタのクラス定義に追加します。/** Sample Attribute Set. */ UPROPERTY() const UMyAttributeSet* AttributeSet;
アトリビュート セットを適切な Ability System コンポーネントに登録します。これはアトリビュート セットをインスタンス化する際に自動的に行われます。アクタのコンストラクタで実行することもできます。または、インスタンス化時点でアクタの
GetAbilitySystemComponent
関数が有効な Ability System コンポーネントを返していれば、BeginPlay
中に実行できます。また、アクタのブループリントを編集して、アトリビュート セット タイプを Ability System コンポーネントの Default Starting Data に追加することもできます。3 つ目の方法は、この例のように Ability System コンポーネントにアトリビュート セットをインスタンス化するように指示することで、自動的に登録されます。// Get the UMyAttributeSet from our Ability System Component. The Ability System Component will create and register one if needed. AttributeSet = ASC->GetSet<UMyAttributeSet>(); // We now have a pointer to the new UMyAttributeSet that we can use later. If it has an initialization function, this is a good place to call it.
最後に、Ability System コンポーネントが持っていないゲームプレイ アトリビュートを変更するゲームプレイ エフェクトを適用すると、Ability System コンポーネントは自身に適合するゲームプレイ アトリビュートを作成します。ただし、この方法ではアトリビュート セットを作成したり、既存のアトリビュート セットにゲームプレイ アトリビュートを追加することはできません。
オプションとして、ゲームプレイ アトリビュートを操作するための基本的なヘルパー関数を追加します。ゲームプレイ アトリビュート自体は protected または private に設定しておき、ゲームプレイ アトリビュートを操作する関数は public を指定することをお勧めします。Gameplay Ability System には、次のようにいくつかのデフォルト関数を設定するためのマクロを提供しています。
マクロ (パラメータを含む) |
生成される関数のシグネチャ |
動作/使用方法 |
---|---|---|
|
|
スタティック関数。エンジンのリフレクション システムから |
|
|
"Health" ゲームプレイ アトリビュートの現在の値を返す |
|
|
"Health" ゲームプレイ アトリビュートの値を |
|
|
"Health" ゲームプレイ アトリビュートの値を |
これらを追加すると、Attribute Set クラスの定義は次のようになります。
UCLASS()
class MYPROJECT_API UMyAttributeSet : public UAttributeSet
{
GENERATED_BODY()
protected:
/** Sample "Health" Attribute */
UPROPERTY(EditAnywhere, BlueprintReadOnly)
FGameplayAttributeData Health;
//~ ...Other Gameplay Attributes here ...
public:
//~ Helper functions for "Health" attributes
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UMyAttributeSet, Health);
GAMEPLAYATTRIBUTE_VALUE_GETTER(Health);
GAMEPLAYATTRIBUTE_VALUE_SETTER(Health);
GAMEPLAYATTRIBUTE_VALUE_INITTER(Health);
//~ ...Helper functions for other Gameplay Attributes here ...
};
これらのヘルパー関数は厳密には必須ではありませんが、推奨されます。
これで、1 つのゲームプレイ アトリビュートを持つ基本的なアトリビュート セットを作成できました。また、これらの値同士の相互作用、プロジェクトや開発中のアクタクラスのコンテキストでどのような意味を持つのかを把握したうえで、ゲームプレイ アトリビュートの動作を制御するコードを実装する必要があります。この関数は、ゲームプレイ アトリビュート自体へのアクセスを制御したり、アビリティ セット レベルでのゲームプレイ エフェクトの動作を制御することで構築できます。
初期化
ハードコードされた値で初期化関数を呼び出してアトリビュート セットとそのゲームプレイ アトリビュートを初期化するのではなく、「AttributeMetaData」という Gameplay Ability System の行タイプを使用して データ テーブル で初期化することができます。データ テーブルには、外部ファイルからデータをインポートしたり、エディタで手動でデータを入力したりすることができます。
データ テーブル アセットの作成時に、行タイプとして [AttributeMetaData] を選択します。
データ ターブルをインポートする
デベロッパーは、通常、次のように、.csv ファイルから自分のテーブルをインポートします。
---,BaseValue,MinValue,MaxValue,DerivedAttributeInfo,bCanStack
MyAttributeSet.Health,"100.000000","0.000000","150.000000","","False"
.csv ファイルをデータ テーブル アセットとしてインポートする際には、行タイプとして [AttributeMetaData] を選択します。
複数のゲームプレイ アトリビュートを持つアトリビュート セットをサポートするために、追加の行を追加することができます。上記のファイルでは、"Health" ゲームプレイ アトリビュート UMyAttributeSet
(リフレクション システムでは「U」というプレフィックスがつかない) が 100 という値で初期化されます。派生情報はなく、スタックもされていません。
MinValue (0.0) と MaxValue (150.0) の列がありますが、ゲームプレイ アトリビュートとアトリビュート セットにはビルトインのクランプ動作を備えていないため、これらの列の値は何の作用もありません。
データ テーブルを手動で入力する
外部のスプレッドシートやテキストエディタ プログラムではなく、Unreal Editor で値を編集するには、他のブループリント アセットと同様に、テーブルを作成して、開きます。ウィンドウの上部にある [Add (追加)] ボタンを使用して、各ゲームプレイ アトリビュートの行を追加します。なお、命名規則は「AttributeSetName.AttributeName」で、大文字と小文字が区別されます。
デフォルトの Gameplay Ability System プラグインでは、"Min Value" と "Max Value" の列は実装されておらず、これらの値には何の作用もありません。
ゲームプレイ アトリビュートへのアクセスを制御する
ゲームプレイ アトリビュートへの直接アクセスを制御することは、その値が常に設定した範囲内にあることを保証する優れた方法です。これは、FGameplayAttributeData
を拡張するのではなく、アビリティ セットを使用して実行します。FGameplayAttributeData
は、ゲームプレイ アトリビュートのデータを格納し、アクセスを提供するだけであるためです。
ゲームプレイ アトリビュートの「Health」値が 0 を下回らないようにするために、独自のゲッター関数とセッター関数を記述することができます。GAMEPLAYATTRIBUTE_VALUE_GETTER
マクロおよび GAMEPLAYATTRIBUTE_VALUE_SETTER
マクロを削除して、次の関数ヘッダーに置き換えます。
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UMyAttributeSet, Health);
float GetHealth() const;
void SetHealth(float NewVal);
GAMEPLAYATTRIBUTE_VALUE_INITTER(Health);
これらの関数をアトリビュート セットのソース ファイルで定義します。
float UMyAttributeSet::GetHealth() const
{
// Return Health's current value, but never return a value lower than zero.
// This is the value after all modifiers that affect Health have been considered.
return FMath::Max(Health.GetCurrentValue(), 0.0f);
}
void UMyAttributeSet::SetHealth(float NewVal)
{
// Do not accept values lower than zero.
NewVal = FMath::Max(NewVal, 0.0f);
// Make sure we have an Ability System Component instance. This should always be the case.
UAbilitySystemComponent* ASC = GetOwningAbilitySystemComponent();
if (ensure(ASC))
{
// Set the base value (not the current value) through the appropriate function.
// This makes sure that any modifiers we have applied will still work properly.
ASC->SetNumericAttributeBase(GetHealthAttribute(), NewVal);
}
}
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetHealthAttribute()).AddUObject(this, &AGASAbilityDemoCharacter::OnHealthChangedInternal);
ゲームプレイ エフェクトとのインタラクション
ゲームプレイ アトリビュートの値を制御する一般的な方法は、ゲームプレイ エフェクトに関連するように、[ゲームプレイ エフェクト] の処理を行うことです。
まず、アトリビュート セットのクラス定義で
PostGameplayEffectExecute
関数をオーバーライドします。この関数は public アクセス レベルである必要があります。void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data) override;
アトリビュート セットのソースファイルに関数の本体を記述し、親クラスの実装を必ず呼び出すようにします。
// Remember to call the parent's implementation. Super::PostGameplayEffectExecute(Data); // Check to see if this call affects our Health by using the Property Getter. if (Data.EvaluatedData.Attribute == GetHealthAttribute()) { // This Gameplay Effect is changing Health.Apply it, but restrict the value first. // In this case, Health's base value must be non-negative. SetHealth(FMath::Max(GetHealth(), 0.0f)); }
レプリケーション
マルチプレイヤー プロジェクトでは、他のプロパティをレプリケートする場合と同じ方法で、アトリビュート セットを使用してゲームプレイ アトリビュートをレプリケートすることができます。
まず、アトリビュート セットのヘッダ ファイルのプロパティの定義に
ReplicatedUsing
[指定子](programming-and-scripting/programming-language-implementation/cpp-in-unreal-engine/Properties/Specifiers)を追加します。これにより、リモート システムでの予測に役立つコールバック関数が設定されます。protected: /** Sample "Health" Attribute */ UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_Health) FGameplayAttributeData Health;
次のようにレプリケーション コールバック関数を宣言します。
/** Called when a new Health value arrives over the network */ UFUNCTION() virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);
アトリビュート セットのソース ファイルで、レプリケーション コールバック関数を定義します。この関数の本体は、Gameplay Ability System が定義する 1 つのマクロとして表現できます。
// Use the default Gameplay Attribute System repnotify behavior. GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, Health, OldHealth);
これがアトリビュート セットの最初のレプリケートされたプロパティである場合は、
GetLifetimeReplicatedProps
パブリック関数のオーバーライドを設定します。/** Marks the properties we wish to replicate */ virtual void GetLifetimeReplicatedProps(TArray
& OutLifetimeProps) const override; 次のように、ゲームプレイ アトリビュートをソース ファイルでアトリビュート セットの
GetLifetimeReplicatedProps
関数に追加します。// Call the parent function. Super::GetLifetimeReplicatedProps(OutLifetimeProps); // Add replication for Health. DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, Health, COND_None, REPNOTIFY_Always);