概要
UnrealEngine のプラグインGameplayAbilitySystemで使われる GameplayEffect をC++で定義して適用してみたメモ書きです。
ゲームプレイエフェクトアセットが多くなると管理が大変になるので少ない設定のGEをC++のみで定義したほうが扱いやすいのではないかと思って試してみました。
ただモディファイアの値だけなら SetByCaller
やCustomCalculationClass
を使うべきです。
更新履歴
日付 | 内容 |
---|---|
2025/04/24 | 初版 |
参考
以下を参考にさせて頂きました、ありがとうございます。
UE公式:GameplayEffects
tranek /
GASDocumentation
関連過去記事
UE5 GameplayEffectの適用に関するメモ
UE5 GameplayEffectComponentについてのメモ
環境
Windows10
Visual Studio 2022
UnrealEngine 5.5.1
関連ソース
"Engine\Plugins\Runtime\GameplayAbilities\Source\GameplayAbilities\Public\GameplayEffect.h"
"Engine\Plugins\Runtime\GameplayAbilities\Source\GameplayAbilities\Public\GameplayEffectTypes.h"
GameplayEffectについて
プラグイン GameplayAbilitySystem
の機能で Attributes
として設定される値を一時的or恒常的に変化させたり、GameplayTag
を付与/削除などができるシステムです。主にゲーム中で使用される バフ/デバフや状態の変化の情報付与といったことに使われます。
ゲームプレイエフェクトの作成(BPの場合)
通常 GameplayEffect を継承したBPアセットを作成して適用します。
ですが、C++のみで定義と適用もできますし、適用だけC++を使ったりも可能です。
実装例
C++ のみでゲームプレイエフェクトクラスを定義する
HasDurationタイプで、タグを付与するコンポーネントとアトリビュートを変化させるモディファイアを定義しています。
以下、コード例。
#pragma once
#include "CoreMinimal.h"
#include "GameplayEffect.h"
#include "MyGameplayEffect.generated.h"
UCLASS()
class SAMPLE00_API UGameplayEffect_Test : public UGameplayEffect
{
GENERATED_BODY()
public:
UGameplayEffect_Test(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
};
UE5.3から基本設定以外の機能はコンポーネント化されていますので、コンポーネントを作成して追加する必要があります。
#include "MyGameplayEffect.h"
#include "GameplayEffectComponents/TargetTagsGameplayEffectComponent.h"
#include "GameplayTagContainer.h"
UGameplayEffect_Test::UGameplayEffect_Test(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// GameplayEffectの基本設定
DurationPolicy = EGameplayEffectDurationType::HasDuration;
DurationMagnitude = FScalableFloat(1.0f);
// タグを付与するゲームプレイコンポーネントを作成
UTargetTagsGameplayEffectComponent* _Component = CreateDefaultSubobject<UTargetTagsGameplayEffectComponent>(TEXT("GEComponent0"));
// コンポーネントに追加するタグを設定
FInheritedTagContainer _Tags;
_Tags.AddTag(FGameplayTag::RequestGameplayTag("State.Tag"));
_Component->SetAndApplyTargetTagChanges(_Tags);
// コンポーネント追加
GEComponents.Add(_Component);
// モディファイアの設定
FGameplayModifierInfo Modifier;
Modifier.Attribute = UMyAttributeSet::GetHealthAttribute(); // アトリビュートを設定
Modifier.ModifierOp = EGameplayModOp::Additive;
Modifier.ModifierMagnitude = FScalableFloat(50.0f); // 50の値を加算
// モディファイア追加
Modifiers.Add(Modifier);
}
SetByCallerタイプのモディファイア設定
直接指定ではないモディファイア設定の場合、設定が少し複雑になります。以下、SetByCaller
でゲームプレイタグ Data.Value
を設定した場合のサンプルコード。
UGameplayEffect_Test::UGameplayEffect_Test(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// GameplayEffectの設定
DurationPolicy = EGameplayEffectDurationType::Infinite;
// モディファイア設定
{
FGameplayModifierInfo _Modifier;
_Modifier.Attribute = UMyAttributeSet::GetMoveSpeedAttribute();
_Modifier.ModifierOp = EGameplayModOp::AddBase;
// SetByCallerでの設定
FSetByCallerFloat _SetByCallerData;
_SetByCallerData.DataTag = FGameplayTag::RequestGameplayTag("Data.Value");
_Modifier.ModifierMagnitude = _SetByCallerData;
Modifiers.Add(_Modifier);
}
}
CustomCalculationタイプでのモディファイア設定
CustomCalculation
での設定も可能で、カスタム計算を行うクラスUGameplayModMagnitudeCalculation
の指定が必要です。以下サンプルコード。
UGameplayEffect_Test2::UGameplayEffect_Test2(const FObjectInitializer& _ObjectInitializer)
: Super(_ObjectInitializer)
{
// GameplayEffectの設定
DurationPolicy = EGameplayEffectDurationType::Infinite;
// モディファイア設定
{
FGameplayModifierInfo _Modifier;
_Modifier.Attribute = UMyAttributeSet::GetMoveSpeedAttribute();
_Modifier.ModifierOp = EGameplayModOp::AddBase;
// CustomCalculationでの設定
FCustomCalculationBasedFloat _CustomCalc;
_CustomCalc.CalculationClassMagnitude = UMyGameplayMMC_MoveSpeed::StaticClass();
_Modifier.ModifierMagnitude = _CustomCalc;
Modifiers.Add(_Modifier);
}
}
UGameplayModMagnitudeCalculation
はゲーム仕様に応じたパラメータ計算をする場合にクラスを継承して実装メソッドに計算コードを書きます。以下サンプルコード。
#pragma once
#include "CoreMinimal.h"
#include "GameplayModMagnitudeCalculation.h"
#include "MyGameplayMMC.generated.h"
UCLASS()
class SAMPLE_API UMyGameplayMMC_MoveSpeed : public UGameplayModMagnitudeCalculation
{
GENERATED_BODY()
float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& _Spec) const override;
};
#include "MyGameplayMMC.h"
float UMyGameplayMMC_MoveSpeed::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& _Spec) const
{
const auto _Owner = Cast<AMyCharacter>(_Spec.GetContext().GetEffectCauser());
// ゲーム仕様に応じたパラメータ計算のコード
float _Speed = _Owner->GetSpeed();
return( _Speed );
}
C++で作成したゲームプレイエフェクトを適用する
適用する処理はあまり変わりません。ここではMakeOutgoingSpec
へ渡すクラスを直接指定しています。
以下サンプルコード。
auto _ASC = GetAbilitySystemComponent();
// ゲームプレイエフェクトを作成する
FGameplayEffectSpecHandle _SpecHandle = _ASC->MakeOutgoingSpec(UGameplayEffect_Test::StaticClass(), 1.0f, _ASC->MakeEffectContext());
if(_SpecHandle.IsValid()){
// 自分に適用する
_ASC->ApplyGameplayEffectSpecToSelf(*_SpecHandle.Data.Get());
}
C++で作成したゲームプレイエフェクトをBPで設定して適用する
C++でアクタークラス等を定義しそれを継承したBP側で C++のみで作成したゲームプレイエフェクトを設定してから適用もできます。
以下コード例
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
//..省略..
// ゲームプレイエフェクト
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<UGameplayEffect> GE_Test;
};
void AMyActor::BeginPlay()
{
if( ::IsValid(GE_Test) ){
FGameplayEffectSpecHandle _SpecHandle = _ASC->MakeOutgoingSpec(GE_Test, 1.0f, _ASC->MakeEffectContext());
if(_SpecHandle.IsValid()){
// 自分に適用する
_ASC->ApplyGameplayEffectSpecToSelf(*_SpecHandle.Data.Get());
}
}
}
ゲームプレイエフェクトの動的実装
ローカルでゲームプレイエフェクトを作成して適用
DurationPolicy
が Instant
なゲームプレイエフェクトのケースではローカルコードで作成して適用できます。Instant
以外のHasDuration
等でも作成はできますがレプリケーション時に問題がでるようです(シングルプレイで試したところ特に問題はでませんでした)。
以下サンプルコード。
auto _ASC = GetAbilitySystemComponent();
// アトリビュート MoveSpeed を変更する GEを作成して適用する
UGameplayEffect* _GETest = NewObject<UGameplayEffect>(GetTransientPackage(), FName(TEXT("DynamicGETest")));
_GETest->DurationPolicy = EGameplayEffectDurationType::Instant;
int32 _Idx = _GETest->Modifiers.Num();
_GETest->Modifiers.SetNum(_Idx + 1);
FGameplayModifierInfo& _Info = _GETest->Modifiers[_Idx];
_Info.ModifierMagnitude = FScalableFloat(1000.0f);
_Info.ModifierOp = EGameplayModOp::Additive;
_Info.Attribute = UMyAttributeSet::GetMoveSpeedAttribute(); // 変更するアトリビュート
// 自身に適用する
_ASC->ApplyGameplayEffectToSelf(_GETest, 1.0f, _ASC->MakeEffectContext());
まとめ
C++のみで作成したゲームプレイエフェクトはパラメータの変更が気軽にできないことや参照している先を調べるのが難しいという問題点があります。参照についてはアセットレジストリ等を駆使すれば調べられそうですが、BPほど気軽ではないですね。