Quantcast
Channel: ぼっちプログラマのメモ
Viewing all articles
Browse latest Browse all 159

【UE5】StateTreeのParameterを実行中に変更する方法(C++版)

$
0
0

はじめに

StateTreeのParameterStateTreeComponentで初期値を設定することができ、

TaskやConditionなどで紐づけ(Binding)して利用することが可能です。

また、StateTree全体だけでなく、State毎にParameterを追加・設定することが可能です。

しかし、UE5.5時点では、「実行中にParameterの値変更すること」は C++側でしかできません。

今後のアップデートでBPからも変更可能になる可能性は高いとは思いますが、ひとまず本記事では C++を使って Parameterを変更する方法について紹介します!

FStateTreePropertyRef を使えば Parameter を変更できる!

結論から言えば、FStateTreePropertyRef を使うことでStateTree全体・State の Parameterを変更することができます。

例えば、Bool型のParameterを反転するStateTree Taskの場合は以下のように実装します。

// FStateTreeTask_ToggleBoolPropertyが実行中に扱うパラメータUSTRUCT()
struct XXX_API FStateTreeTask_ToggleBoolProperty_InstanceData
{
    GENERATED_BODY()

    // Bool型のParameterのみを扱うプロパティ参照UPROPERTY(EditAnywhere, Category = "", meta = (RefType = "Bool"))
    FStateTreePropertyRef BoolPropertyRef;
};

USTRUCT(meta = (DisplayName = "Toggle Bool"))
struct XXX_API FStateTreeTask_ToggleBoolProperty : public FStateTreeTaskCommonBase
{
    GENERATED_BODY()

    // Taskが実行中に扱うパラメータの型を指定using FInstanceDataType = FStateTreeTask_ToggleBoolProperty_InstanceData;
    virtualconst UStruct* GetInstanceDataType() constoverride { return FInstanceDataType::StaticStruct(); }

    // EnterState State開始時に呼ばれるvirtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context,
        const FStateTreeTransitionResult& Transition) constoverride;
    
};
EStateTreeRunStatus FStateTreeTask_ToggleBoolProperty::EnterState(FStateTreeExecutionContext& Context,
    const FStateTreeTransitionResult& Transition) const
{
    // FStateTreeTask_ToggleBoolProperty_InstanceData を取得
    FInstanceDataType* InstanceData = Context.GetInstanceDataPtr<FInstanceDataType>(*this);
    check(InstanceData);

    // BindingしたBoolプロパティを取得し、反転するif (bool* BoolPropertyPtr = InstanceData->BoolPropertyRef.GetMutablePtr<bool>(Context))
    {
        *BoolPropertyPtr = !(*BoolPropertyPtr);
    }

    // FinishTask を呼ぶ場合はSucceeded or Failedを返すreturn EStateTreeRunStatus::Running; 
}

「InstancedData?なんでこんな組み方してるの?」という方は、こちらをどうぞ https://qiita.com/BONTAN/items/8d78605c9d2b3d1fabde#instance-data

StateTree上では通常のプロパティと同様の操作でBindingできます。このときBinding候補になるのは、FStateTreePropertyRef の RefType で指定した型でフィルタリングされたものです。

これで、Taskが実行されるたびにBool型のParameter「IsClosed」が反転されます。やったね!

FStateTreePropertyRef の RefType について

FStateTreePropertyRef がサポートしている型や、RefTypeの記述ルールは下記コードに書かれています。 Engine\Plugins\Runtime\StateTree\Source\StateTreeModule\Public\StateTreePropertyRef.h

以下はそのコメントからの抜粋です。

  • RefType = ""
    • 参照するプロパティのタイプのリストをカンマ区切りで指定します
    • サポートされるタイプ: bool, byte, int32, int64, float, double, Name, String, Text, UObjectポインター、および Struct
    • ObjectStructは正規パス名を使用する必要があります
    • 複数のタイプを指定した場合、GetMutablePtrTupleを使用して正しいタイプにアクセスすることができます
  • IsRefToArray
    • これを指定すると、参照は TArray<RefType>になります
  • CanRefToArray
    • これを指定すると、参照は RefTypeまたは TArray<RefType>になります
  • Optional
    • これを指定すると、参照が設定されていなくてもエラーを出しません(指定されない場合はコンパイラがエラーを報告します)

例:

// float の参照UPROPERTY(EditAnywhere, meta = (RefType = "float"))
FStateTreePropertyRef RefToFloat;

// FTestStructBase の参照UPROPERTY(EditAnywhere, meta = (RefType = "/Script/ModuleName.TestStructBase"))
FStateTreePropertyRef RefToTest;

// TArray<FTestStructBase> の参照UPROPERTY(EditAnywhere, meta = (RefType = "/Script/ModuleName.TestStructBase", IsRefToArray))
FStateTreePropertyRef RefToArrayOfTests;

// Vector, TArray<FVector>, AActor*, TArray<AActor*> の参照UPROPERTY(EditAnywhere, meta = (RefType = "/Script/CoreUObject.Vector, /Script/Engine.Actor", CanRefToArray))
FStateTreePropertyRef RefToLocationLikeTypes;

そして、複数のRefType が設定されている場合は以下のようにして取得します(FStateTreeRunEnvQueryTaskより)。

UPROPERTY(EditAnywhere, Category = Out, meta = (RefType = "/Script/CoreUObject.Vector, /Script/Engine.Actor", CanRefToArray))
FStateTreePropertyRef Result;
auto [VectorPtr, ActorPtr, ArrayOfVector, ArrayOfActor] = 
    InstanceData.Result.GetMutablePtrTuple<FVector, AActor*, TArray<FVector>, TArray<AActor*>>(Context);

if (VectorPtr)
{
    //*VectorPtr = InstanceData.QueryResult->GetItemAsLocation(0);
}
elseif (ActorPtr)
{
    //*ActorPtr = InstanceData.QueryResult->GetItemAsActor(0);
}
elseif (ArrayOfVector)
{
    //InstanceData.QueryResult->GetAllAsLocations(*ArrayOfVector);
}
elseif (ArrayOfActor)
{
    //InstanceData.QueryResult->GetAllAsActors(*ArrayOfActor);
}

さいごに

これで StateTreeにおけるパラメータを C++から 変更することが可能になります!これをうまく活用することで、これまで Actor側で持っていたフラグをStateTree側に持っていくことができ、より汎用的なStateTreeを組むことが可能になる…はずです!

ただC++での段取りが少々めんどうなので、扱うパラメータが確定するまでは Context の Actor・AI Controllerが持つプロパティで代用するのがいいかも…今後に期待!

おまけ

C++の場合はFStateTreePropertyRefを使いますが、BPの場合はFStateTreeBlueprintPropertyRefになる仕様の模様。今後のBP対応状況に関しては FStateTreeBlueprintPropertyRefを中心に見ていくのが良さそうです

おしまい


Viewing all articles
Browse latest Browse all 159

Latest Images

Trending Articles

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>