【UE5】BTDecorator_Cooldownを参考にしながらC++で自作Decoratorを作る
概要
この記事は一人アドベントカレンダー by ダリアの5日目の記事です。
今回は自作のDecoratorをC++で作る方法について、元々用意してあるCooldownというDecoratorを参考にしながら作ることが出来たので、それについてまとめた記事になります。
環境
UE5.4.4
説明
まずクラスの全体を書きます。
#pragma once
#include "CoreMinimal.h"
#include "BehaviorTree/BTDecorator.h"
#include "BTDecorator_ExampleCooldown.generated.h"
struct FBTExampleCooldownDecoratorMemory
{
double LastUseTimestamp;
uint8 bRequestedRestart : 1;
};
UCLASS()
class EXAMPLEAISYSTEMS_API UBTDecorator_ExampleCooldown : public UBTDecorator
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY(Category=Decorator, EditAnywhere)
float CoolDownTime;
virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const override;
virtual void InitializeMemory(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTMemoryInit::Type InitType) const override;
virtual void CleanupMemory(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTMemoryClear::Type CleanupType) const override;
virtual uint16 GetInstanceMemorySize() const override;
virtual void DescribeRuntimeValues(const UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTDescriptionVerbosity::Type Verbosity, TArray<FString>& Values) const override;
virtual FString GetStaticDescription() const override;
#if WITH_EDITOR
virtual FName GetNodeIconName() const override;
#endif
protected:
virtual void OnNodeDeactivation(FBehaviorTreeSearchData& SearchData, EBTNodeResult::Type NodeResult) override;
virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
private:
};
#include "BTDecorator_ExampleCooldown.h"
UBTDecorator_ExampleCooldown::UBTDecorator_ExampleCooldown(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
NodeName = "Example Cooldown";
INIT_DECORATOR_NODE_NOTIFY_FLAGS();
CoolDownTime = 5.0f;
bAllowAbortChildNodes = false;
}
bool UBTDecorator_ExampleCooldown::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp,
uint8* NodeMemory) const
{
FBTExampleCooldownDecoratorMemory* DecoratorMemory = CastInstanceNodeMemory<FBTExampleCooldownDecoratorMemory>(NodeMemory);
const double RecalcTime = (OwnerComp.GetWorld()->GetTimeSeconds() - CoolDownTime);
return RecalcTime >= DecoratorMemory->LastUseTimestamp;
}
void UBTDecorator_ExampleCooldown::InitializeMemory(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory,
EBTMemoryInit::Type InitType) const
{
FBTExampleCooldownDecoratorMemory* DecoratorMemory = InitializeNodeMemory<FBTExampleCooldownDecoratorMemory>(NodeMemory, InitType);
if (InitType == EBTMemoryInit::Initialize)
{
DecoratorMemory->LastUseTimestamp = TNumericLimits<double>::Lowest();
}
}
void UBTDecorator_ExampleCooldown::CleanupMemory(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory,
EBTMemoryClear::Type CleanupType) const
{
CleanupNodeMemory<FBTExampleCooldownDecoratorMemory>(NodeMemory,CleanupType);
}
uint16 UBTDecorator_ExampleCooldown::GetInstanceMemorySize() const
{
return sizeof(FBTExampleCooldownDecoratorMemory);
}
void UBTDecorator_ExampleCooldown::DescribeRuntimeValues(const UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory,
EBTDescriptionVerbosity::Type Verbosity, TArray<FString>& Values) const
{
Super::DescribeRuntimeValues(OwnerComp, NodeMemory, Verbosity, Values);
FBTExampleCooldownDecoratorMemory* DecoratorMemory = CastInstanceNodeMemory<FBTExampleCooldownDecoratorMemory>(NodeMemory);
const double TimePassed = OwnerComp.GetWorld()->GetTimeSeconds() - DecoratorMemory->LastUseTimestamp;
if (TimePassed < CoolDownTime)
{
Values.Add(FString::Printf(TEXT("%s in %ss"),
(FlowAbortMode == EBTFlowAbortMode::None) ? TEXT("unlock") : TEXT("restart"),
*FString::SanitizeFloat(CoolDownTime - TimePassed)));
}
}
FString UBTDecorator_ExampleCooldown::GetStaticDescription() const
{
return FString::Printf(TEXT("%s: lock for %.1fs after execution and return %s"), *Super::GetStaticDescription(),
CoolDownTime, *UBehaviorTreeTypes::DescribeNodeResult(EBTNodeResult::Failed));
}
#if WITH_EDITOR
FName UBTDecorator_ExampleCooldown::GetNodeIconName() const
{
return FName("BTEditor.Graph.BTNode.Decorator.Cooldown.Icon");
}
#endif
void UBTDecorator_ExampleCooldown::OnNodeDeactivation(FBehaviorTreeSearchData& SearchData,
EBTNodeResult::Type NodeResult)
{
FBTExampleCooldownDecoratorMemory* DecoratorMemory = GetNodeMemory<FBTExampleCooldownDecoratorMemory>(SearchData);
DecoratorMemory->LastUseTimestamp = SearchData.OwnerComp.GetWorld()->GetTimeSeconds();
DecoratorMemory->bRequestedRestart = false;
}
void UBTDecorator_ExampleCooldown::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
FBTExampleCooldownDecoratorMemory* DecoratorMemory = CastInstanceNodeMemory<FBTExampleCooldownDecoratorMemory>(NodeMemory);
if (!DecoratorMemory->bRequestedRestart)
{
const double RecalcTime = (OwnerComp.GetWorld()->GetTimeSeconds() - CoolDownTime);
if (RecalcTime >= DecoratorMemory->LastUseTimestamp)
{
DecoratorMemory->bRequestedRestart = true;
OwnerComp.RequestExecution(this);
}
}
}
ここから主要な各関数や構造体の説明をしようと思います。
FBTExampleCooldownDecoratorMemory構造体
これは参考にしたCooldownなどC++で作られたDecoratorは構造体を作ってそれを通してやりとりするみたいでした。
Cooldownの場合はTimeStampを設定して現在時刻から引いた時間を経過時間として算出してCoolDownTime
を超えていたらDecoratorの終了条件に設定してるみたいです。
NodeName
Nodeのタイトル名。BPの方に設定されたらそちらに上書きされます。
InitializeMemory
BehaviourTree起動時に1回だけ呼ばれます。
CleanupMemory
PIE終了時に呼ばれたので、BehaviourTreeが停止されたら呼ばれる関数みたいでした。
TickNode
スタックトレースで確認しましたが呼ばれなかったため詳細は不明です。
CalculateRawConditionValue
毎フレーム呼ばれて、コンディションの確認に使われてました。
trueを返すようにするとNodeが実行できるようになります。
GetInstanceMemorySize
ノード上でやり取りするデータ構造体のサイズを返す機能です。
これはsizeof
で自作した構造体FBTExampleCooldownDecoratorMemory
を返すようにします。
uint16 UBTDecorator_ExampleCooldown::GetInstanceMemorySize() const
{
return sizeof(FBTExampleCooldownDecoratorMemory);
}
DescribeRuntimeValues
この関数はノード実行時にカーソルを合わせると残り時間を表示してくれるから、ランタイム時に表示されるツールチップみたいな役割の関数なのかなと思いました。
Values
に文字列を追加すれば表示できるみたいでした。
GetStaticDescription
NodeNameの下にある説明を作る機能です。
結果
冒頭のソースコードのようにすれば、自作のDecoratorが選択できるようになります。
以下の画像のように自作したDecoratorが候補に出てれば成功です。
参考
Engine\Source\Runtime\AIModule\Classes\BehaviorTree\Decorators\BTDecorator_Cooldown.h
Engine\Source\Runtime\AIModule\Classes\BehaviorTree\Decorators\BTDecorator_Cooldown.cpp
Discussion