5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UE4 GameplayTasksについてのメモ

Last updated at Posted at 2020-06-23

概要

UnrealEngine のゲームプレイタスクについてのメモです。

環境

Windows10
Visual Studio 2017
UnrealEngine 4.24, 5.3.2

更新履歴

日付 内容
2020/06/23 初版
2024/09/02 タスクの停止について追記

参考

以下を参考にさせて頂きました、ありがとうございます。

UnrealEngine : GameplayTasks
GameplayTasks API – GameplayTasksを使用して実現する

関連コード

"Engine\Source\Runtime\GameplayTasks\Classes\GameplayTask.h"

モジュール準備

Build.cs に GameplayTasksモジュールを追加します。

Build.cs
PublicDependencyModuleNames.AddRange(
	new string[]
	{
		"Core",
		"InputCore",
		"GameplayAbilities",
		"GameplayTags",
		"GameplayTasks"		// ←追加
	}

GameplayTasksの使用

GameplayTaskComponent の準備

ゲームプレイタスクを管理するコンポーネントを用意する必要があります。
また、コンポーネントを用意せずに、UGameplayTaskOwnerInterface をアクターなどが継承して使用することもできるようです。

BP

GameplayTasksComponent.jpg

C++

MyActor.h
UPROPERTY(Category = "Component", EditAnywhere, BlueprintReadWrite)
class UGameplayTasksComponent* GameplayTasksComponent;
MyActor.cpp
#include "GameplayTasksComponent.h"

// コンストラクタ
GameplayTasksComponent = CreateDefaultSubobject<UGameplayTasksComponent>(TEXT("GameplayTasks0"));

GameplayTaskクラスの作成

"Engine\Source\Runtime\GameplayTasks\Classes\Tasks" フォルダのクラスを参考にタスクが呼ばれている最中にTickが走るようなタスククラスを作成してみます。

MyGameplayTask.h
#include "CoreMinimal.h"
#include "GameplayTask.h"
#include "MyGameplayTask.generated.h"

UCLASS()
class TEST_API UMyGameplayTask : public UGameplayTask
{
	GENERATED_BODY()
	
	DECLARE_DYNAMIC_MULTICAST_DELEGATE(FTaskFinishDelegate);

public:
	UMyGameplayTask(const FObjectInitializer& ObjectInitializer);

	// 終了時に呼ばれるデリゲート
	UPROPERTY(BlueprintAssignable)
	FTaskFinishDelegate OnFinished;


	/** デリゲートがセットアップされたら、実際のタスクをトリガーするために呼び出されます
	 * デフォルトの実装は何もせず、呼び出す必要がないことに注意してください */
	virtual void Activate() override;
	
	// Tick処理
	virtual void TickTask(float DeltaTime) override;

	// デバッグ文字列
	virtual FString GetDebugString() const override;

	// タスク生成
	UFUNCTION(BlueprintCallable, Category = "GameplayTasks", meta = (AdvancedDisplay = "TaskOwner, Priority", DefaultToSelf = "TaskOwner", BlueprintInternalUseOnly = "TRUE"))
	static UMyGameplayTask* MyTestTask(TScriptInterface<IGameplayTaskOwnerInterface> TaskOwner, float Time, const uint8 Priority = 192);

	static UMyGameplayTask* MyTestTask(IGameplayTaskOwnerInterface& InTaskOwner, float Time, const uint8 Priority = FGameplayTasks::DefaultPriority, const FName InInstanceName = FName());


private:
	float Time;
	float TimeStarted;

	uint32 bIsFinish : 1;
};

MyGameplayTask.cpp

#include "MyGameplayTask.h"
#include "Engine/EngineTypes.h"
#include "VisualLogger/VisualLogger.h"
#include "GameplayTasksComponent.h"
#include "Engine/World.h"

// コンストラクタ
UMyGameplayTask::UMyGameplayTask(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	Time = 0.f;
	TimeStarted = 0.f;

	bIsFinish = false;
	bTickingTask = true;
}

// タスク生成
UMyGameplayTask* UMyGameplayTask::MyTestTask(TScriptInterface<IGameplayTaskOwnerInterface> TaskOwner, float Time, const uint8 Priority)
{
	UMyGameplayTask* MyTask = NewTaskUninitialized<UMyGameplayTask>();
	if (MyTask && TaskOwner.GetInterface() != nullptr)
	{
		MyTask->InitTask(*TaskOwner, Priority);
		MyTask->Time = Time;
	}
	
	return MyTask;
}

// タスク生成
UMyGameplayTask* UMyGameplayTask::MyTestTask(IGameplayTaskOwnerInterface& InTaskOwner, float Time, const uint8 Priority, const FName InInstanceName)
{
	UMyGameplayTask* MyTask = NewTaskUninitialized<UMyGameplayTask>();
	if (MyTask)
	{
		MyTask->InstanceName = InInstanceName;
		MyTask->InitTask(InTaskOwner, Priority);
		MyTask->Time = Time;
	}
	return MyTask;
}

// 実行開始
void UMyGameplayTask::Activate()
{
	UWorld* World = GetWorld();
	TimeStarted = World->GetTimeSeconds();

}

// Tick処理
void UMyGameplayTask::TickTask(float DeltaTime)
{
	FColor _Col = FColor::White;
	FVector2D _Scl(1.5f, 1.5f);
	GEngine->AddOnScreenDebugMessage(-1, 0.0f, _Col, GetDebugString(), true, _Scl);



	const float _TimeLeft = Time - GetWorld()->TimeSince(TimeStarted);
	if(_TimeLeft <= 0.0f){
		
		if(!bIsFinish){
			OnFinished.Broadcast();
		}
		bIsFinish = true;
		EndTask();
	}

}

// デバッグ文字列
FString UMyGameplayTask::GetDebugString() const
{
	const float _TimeLeft = Time - GetWorld()->TimeSince(TimeStarted);
	return FString::Printf(TEXT("MyTask TimeLimit for %s. Time: %.2f. TimeLeft: %.2f"), *GetNameSafe(GetChildTask()), Time, _TimeLeft);
}

Taskの生成、呼び出し

作成したゲームプレイタスクを生成し、タスク終了時にも処理を1つ呼び出します。
引数となる時間設定は2秒にします。

BP

UseMyGameplayTask.jpg

C++

MyActor.h
// タスク終了時に呼ぶ処理
UFUNCTION()
	void FinishFunc(){ UE_LOG(LogTemp, Log, TEXT("Finish")); }
MyActor.cpp
#include "MyGameplayTask.h"

// 2秒設定でタスク呼び出し
UMyGameplayTask* _Task = UMyGameplayTask::MyTestTask(GameplayTasksComponent, 2.0f);
if( _Task){
	// タスク終了時に呼ぶ処理を追加
	_Task->OnFinished.AddDynamic(this, &AMyActor::FinishFunc);
	// アクティベーション
	_Task->ReadyForActivation();
}

実行結果

TickTaskが呼び出され、[MyTask TimeLimit for %s. Time: %.2f. TimeLeft: %.2f]が2秒間出力されます。

Result.jpg

タスク終了後、終了時処理が呼び出され、[Finish]の文字列が出力されます。

タスクの停止

起動後のタスクを停止するには EndTask()メソッドを呼ぶことできます。

.cpp
if( _Task){
	// タスクを停止する
	_Task->EndTask();
}

途中終了であっても処理したい終了処理があるならば OnDestroy を継承してそこに終了処理を書くと良いです。
以下コード例。

MyTask.cpp

// タスクを終了してクリーンアップする処理(直接呼び出してはならない)
void UMyGameplayTask::OnDestroy(bool bInOwnerFinished)
{
	// EndTask() または TaskOwnerEnded() から呼ばれる
	
	UE_LOG(LogTemp, Log, TEXT("必ず呼ばれる終了処理"));
	
	// 基底処理(必ず最後に呼ぶ)
	Super::OnDestroy(bInOwnerFinished);
}


補足

コンポーネントからのタスクの呼び出しについて

タスク呼び出しコードにて、コンポーネントに直接タスクを追加する以下のような呼び出しをした場合、エラーがアウトプットログに出力されます。

.cpp

// ゲームプレイタスクを作成
UMyGameplayTask* _Task = UMyGameplayTask::MyTestTask(GameplayTasksComponent, 2.0f);
if( _Task){
	// アクティベーション(直接コンポーネントに追加する)
	GameplayTasksComponent->AddTaskReadyForActivation(*_Task);
}

GameplayTaskComponent の以下のマクロでエラーになるようです。

GameplayTaskComponent.cpp
ensure(NewTask.RequiresPriorityOrResourceManagement() == true);

どうやらTaskを起動するために必要なリソースのチェックが行われているようです。
コンポーネントからは RunGameplayTask でリソースを指定して起動させることができます。

.cpp
// ゲームプレイタスクを作成
UMyGameplayTask* _Task = UMyGameplayTask::MyTestTask(*GameplayTasksComponent, 2.0f);
if( _Task){
	// タスク起動に必要なリソースの設定
	FGameplayResourceSet _AdditionalRequiredResources = FGameplayResourceSet::NoResources();
	FGameplayResourceSet _AdditionalClaimedResources = FGameplayResourceSet::NoResources();

	// ゲームプレイタスクを走らせる
	GameplayTasksComponent->RunGameplayTask(*GameplayTasksComponent, *_Task, 0, _AdditionalRequiredResources, _AdditionalClaimedResources);
}

まとめ

GameplayTasks についての記事がほとんど見当たらないので、間違いがある可能性があります。問題がありましたらご指摘をお願いいたします。:pray:

5
3
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
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?