Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

UE4 GameplayTasksについてのメモ

概要

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

環境

Windows10
Visual Studio 2017
UnrealEngine 4.24

参考

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

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

モジュール準備

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, &UMyGameplayTask::FinishFunc);
    // アクティベーション
    _Task->ReadyForActivation();
}

実行結果

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

Result.jpg

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

補足

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

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

.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:

unknown_ds
都内在住。 花粉飛散開始!
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away