0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UE5 C++でGameplayEffectを定義して適用してみるメモ

Posted at

概要

UnrealEngine のプラグインGameplayAbilitySystemで使われる GameplayEffect をC++で定義して適用してみたメモ書きです。
ゲームプレイエフェクトアセットが多くなると管理が大変になるので少ない設定のGEをC++のみで定義したほうが扱いやすいのではないかと思って試してみました。
ただモディファイアの値だけなら SetByCallerCustomCalculationClass を使うべきです。

更新履歴

日付 内容
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アセットを作成して適用します。

▼ゲームプレイエフェクトアセットの作成

▼ゲームプレイエフェクトアセット
GameplayEffectBP.png

▼BPでのゲームプレイエフェクト適用例
ApplyGE.png

ですが、C++のみで定義と適用もできますし、適用だけC++を使ったりも可能です。

実装例

C++ のみでゲームプレイエフェクトクラスを定義する

HasDurationタイプで、タグを付与するコンポーネントとアトリビュートを変化させるモディファイアを定義しています。
以下、コード例。

MyGameplayEffect.h
#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から基本設定以外の機能はコンポーネント化されていますので、コンポーネントを作成して追加する必要があります。

MyGameplayEffect.cpp
#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 を設定した場合のサンプルコード。

MyGameplayEffect_SetByCaller.cpp
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の指定が必要です。以下サンプルコード。

MyGameplayEffect_SetByCaller.cpp
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 はゲーム仕様に応じたパラメータ計算をする場合にクラスを継承して実装メソッドに計算コードを書きます。以下サンプルコード。

MyGameplayMMC.h
#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;
};
MyGameplayMMC.cpp
#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へ渡すクラスを直接指定しています。
以下サンプルコード。

.cpp
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++のみで作成したゲームプレイエフェクトを設定してから適用もできます。
以下コード例

MyActor.h
UCLASS()
class AMyActor : public AActor
{
	GENERATED_BODY()
//..省略..

	// ゲームプレイエフェクト
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	TSubclassOf<UGameplayEffect> GE_Test;
};
MyActor.cpp
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());
		}
	}
}

ゲームプレイエフェクトの動的実装

ローカルでゲームプレイエフェクトを作成して適用

DurationPolicyInstant なゲームプレイエフェクトのケースではローカルコードで作成して適用できます。Instant 以外のHasDuration 等でも作成はできますがレプリケーション時に問題がでるようです(シングルプレイで試したところ特に問題はでませんでした)。
以下サンプルコード。

.cpp
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ほど気軽ではないですね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?