// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/ObjectMacros.h" #include "UObject/Object.h" #include "BehaviorTree/BehaviorTreeTypes.h" #include "BehaviorTree/BehaviorTreeComponent.h" #include "BehaviorTree/BlackboardAssetProvider.h" #include "GameplayTaskOwnerInterface.h" #include "Tasks/AITask.h" #include #include "BTNode.generated.h" class AActor; class ISlateStyle; class UBehaviorTree; class UBlackboardData; class UBTCompositeNode; class UGameplayTasksComponent; AIMODULE_API DECLARE_LOG_CATEGORY_EXTERN(LogBehaviorTree, Display, All); class AAIController; class UWorld; class UBehaviorTree; class UBehaviorTreeComponent; class UBTCompositeNode; class UBlackboardData; struct FBehaviorTreeSearchData; struct FBTInstancedNodeMemory { int32 NodeIdx; }; struct FBehaviorTreeNodeDebugContext { /** Behavior Tree component of the behavior tree being debugged. */ UBehaviorTreeComponent& OwnerComponent; /** Combined string of the values returned by DescribeRuntimeValues of the node at the current debug execution step. */ FString RuntimeDescription; }; UCLASS(Abstract, config=Game, MinimalAPI) class UBTNode : public UObject, public IGameplayTaskOwnerInterface { GENERATED_UCLASS_BODY() AIMODULE_API virtual UWorld* GetWorld() const override; /** fill in data about tree structure */ AIMODULE_API void InitializeNode(UBTCompositeNode* InParentNode, uint16 InExecutionIndex, uint16 InMemoryOffset, uint8 InTreeDepth); /** initialize any asset related data */ AIMODULE_API virtual void InitializeFromAsset(UBehaviorTree& Asset); /** initialize memory block. InitializeNodeMemory template function is provided to help initialize the memory. */ AIMODULE_API virtual void InitializeMemory(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTMemoryInit::Type InitType) const; /** cleanup memory block. CleanupNodeMemory template function is provided to help cleanup the memory. */ AIMODULE_API virtual void CleanupMemory(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTMemoryClear::Type CleanupType) const; /** gathers description of all runtime parameters */ AIMODULE_API virtual void DescribeRuntimeValues(const UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTDescriptionVerbosity::Type Verbosity, TArray& Values) const; /** size of instance memory */ AIMODULE_API virtual uint16 GetInstanceMemorySize() const; /** called when node instance is added to tree */ AIMODULE_API virtual void OnInstanceCreated(UBehaviorTreeComponent& OwnerComp); /** called when node instance is removed from tree */ AIMODULE_API virtual void OnInstanceDestroyed(UBehaviorTreeComponent& OwnerComp); /** called on creating subtree to set up memory and instancing */ AIMODULE_API void InitializeInSubtree(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, int32& NextInstancedIndex, EBTMemoryInit::Type InitType) const; /** called on removing subtree to cleanup memory */ AIMODULE_API void CleanupInSubtree(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTMemoryClear::Type CleanupType) const; /** size of special, hidden memory block for internal mechanics */ AIMODULE_API virtual uint16 GetSpecialMemorySize() const; #if USE_BEHAVIORTREE_DEBUGGER /** fill in data about execution order */ AIMODULE_API void InitializeExecutionOrder(UBTNode* NextNode); /** @return next node in execution order */ UBTNode* GetNextNode() const; #endif template T* InitializeNodeMemory(uint8* NodeMemory, EBTMemoryInit::Type InitType) const; template void CleanupNodeMemory(uint8* NodeMemory, EBTMemoryClear::Type CleanupType) const; template T* GetNodeMemory(FBehaviorTreeSearchData& SearchData) const; template const T* GetNodeMemory(const FBehaviorTreeSearchData& SearchData) const; template T* GetNodeMemory(FBehaviorTreeInstance& BTInstance) const; template const T* GetNodeMemory(const FBehaviorTreeInstance& BTInstance) const; template T* CastInstanceNodeMemory(uint8* NodeMemory) const; /** get special memory block used for hidden shared data (e.g. node instancing) */ template T* GetSpecialNodeMemory(uint8* NodeMemory) const; /** @return parent node */ UBTCompositeNode* GetParentNode() const; /** @return name of node */ AIMODULE_API FString GetNodeName() const; /** @return execution index */ uint16 GetExecutionIndex() const; /** @return memory offset */ uint16 GetMemoryOffset() const; /** @return depth in tree */ uint8 GetTreeDepth() const; /** sets bIsInjected flag, do NOT call this function unless you really know what you are doing! */ void MarkInjectedNode(); /** @return true if node was injected by subtree */ bool IsInjected() const; /** sets bCreateNodeInstance flag, do NOT call this function on already pushed tree instance! */ void ForceInstancing(bool bEnable); /** @return true if node wants to be instanced */ bool HasInstance() const; /** @return true if this object is instanced node */ bool IsInstanced() const; /** @return tree asset */ UBehaviorTree* GetTreeAsset() const; /** @return blackboard asset */ AIMODULE_API UBlackboardData* GetBlackboardAsset() const; /** @return node instance if bCreateNodeInstance was set */ AIMODULE_API UBTNode* GetNodeInstance(const UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const; AIMODULE_API UBTNode* GetNodeInstance(FBehaviorTreeSearchData& SearchData) const; /** @return string containing description of this node instance with all relevant runtime values */ AIMODULE_API FString GetRuntimeDescription(const UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTDescriptionVerbosity::Type Verbosity) const; /** @return string containing description of this node with all setup values */ AIMODULE_API virtual FString GetStaticDescription() const; #if WITH_EDITOR /** Get the style set from which GetNodeIconName is queried */ AIMODULE_API virtual const ISlateStyle& GetNodeIconStyleSet() const; /** Get the name of the icon used to display this node in the editor */ AIMODULE_API virtual FName GetNodeIconName() const; /** Get whether this node is using a blueprint for its logic */ AIMODULE_API virtual bool UsesBlueprint() const; /** Validates this node and returns the resulting error message. Empty means no error. */ AIMODULE_API virtual FString GetErrorMessage() const; /** Called after creating new node in behavior tree editor, use for versioning */ virtual void OnNodeCreated() {} AIMODULE_API void PreSave(FObjectPreSaveContext SaveContext) override; /** @return the external asset associated with this node that should be open on double click. If set, DebugContext contains the info of the node in the current debug step. */ AIMODULE_API virtual const UObject* GetAssociatedAsset(TOptional DebugContext) const; #endif /** Gets called only for instanced nodes(bCreateNodeInstance == true). In practive overridden by BP-implemented BT nodes */ virtual void SetOwner(AActor* ActorOwner) {} // BEGIN IGameplayTaskOwnerInterface AIMODULE_API virtual UGameplayTasksComponent* GetGameplayTasksComponent(const UGameplayTask& Task) const override; AIMODULE_API virtual AActor* GetGameplayTaskOwner(const UGameplayTask* Task) const override; AIMODULE_API virtual AActor* GetGameplayTaskAvatar(const UGameplayTask* Task) const override; AIMODULE_API virtual uint8 GetGameplayTaskDefaultPriority() const override; AIMODULE_API virtual void OnGameplayTaskInitialized(UGameplayTask& Task) override; // END IGameplayTaskOwnerInterface AIMODULE_API UBehaviorTreeComponent* GetBTComponentForTask(UGameplayTask& Task) const; template T* NewBTAITask(UBehaviorTreeComponent& BTComponent) { check(BTComponent.GetAIOwner()); bOwnsGameplayTasks = true; return UAITask::NewAITask(*BTComponent.GetAIOwner(), *this, TEXT("Behavior")); } template T* NewBTAITask(const UClass& Class, UBehaviorTreeComponent& BTComponent) { check(BTComponent.GetAIOwner()); bOwnsGameplayTasks = true; return UAITask::NewAITask(Class, *BTComponent.GetAIOwner(), *this, TEXT("Behavior")); } /** node name */ UPROPERTY(Category=Description, EditAnywhere) FString NodeName; private: /** source asset */ UPROPERTY() TObjectPtr TreeAsset; /** parent node */ UPROPERTY() TObjectPtr ParentNode; #if USE_BEHAVIORTREE_DEBUGGER /** next node in execution order */ TWeakObjectPtr NextExecutionNode; #endif /** depth first index (execution order) */ UPROPERTY(transient) uint16 ExecutionIndex; /** instance memory offset */ UPROPERTY(transient) uint16 MemoryOffset; /** depth in tree */ UPROPERTY(transient) uint8 TreeDepth; /** set automatically for node instances. Should never be set manually */ UPROPERTY(transient) uint8 bIsInstanced : 1; /** if set, node is injected by subtree. Should never be set manually */ UPROPERTY(transient) uint8 bIsInjected : 1; protected: /** if set, node will be instanced instead of using memory block and template shared with all other BT components */ UPROPERTY(transient) uint8 bCreateNodeInstance : 1; /** set to true if task owns any GameplayTasks. Note this requires tasks to be created via NewBTAITask * Otherwise specific BT task node class is responsible for ending the gameplay tasks on node finish */ UPROPERTY(transient) uint8 bOwnsGameplayTasks : 1; }; ////////////////////////////////////////////////////////////////////////// // Inlines inline UBehaviorTree* UBTNode::GetTreeAsset() const { return TreeAsset; } inline UBTCompositeNode* UBTNode::GetParentNode() const { return ParentNode; } #if USE_BEHAVIORTREE_DEBUGGER inline UBTNode* UBTNode::GetNextNode() const { return NextExecutionNode.Get(); } #endif inline uint16 UBTNode::GetExecutionIndex() const { return ExecutionIndex; } inline uint16 UBTNode::GetMemoryOffset() const { return MemoryOffset; } inline uint8 UBTNode::GetTreeDepth() const { return TreeDepth; } inline void UBTNode::MarkInjectedNode() { bIsInjected = true; } inline bool UBTNode::IsInjected() const { return bIsInjected; } inline void UBTNode::ForceInstancing(bool bEnable) { // allow only in not initialized trees, side effect: root node always blocked check(ParentNode == NULL); bCreateNodeInstance = bEnable; } inline bool UBTNode::HasInstance() const { return bCreateNodeInstance; } inline bool UBTNode::IsInstanced() const { return bIsInstanced; } template T* UBTNode::InitializeNodeMemory(uint8* NodeMemory, EBTMemoryInit::Type InitType) const { if (InitType == EBTMemoryInit::Initialize) { new(NodeMemory) T(); } return CastInstanceNodeMemory(NodeMemory); } template void UBTNode::CleanupNodeMemory(uint8* NodeMemory, EBTMemoryClear::Type CleanupType) const { if constexpr (!std::is_trivially_destructible_v) { if (CleanupType == EBTMemoryClear::Destroy) { CastInstanceNodeMemory(NodeMemory)->~T(); } } } template T* UBTNode::GetNodeMemory(FBehaviorTreeSearchData& SearchData) const { return GetNodeMemory(SearchData.OwnerComp.InstanceStack[SearchData.OwnerComp.GetActiveInstanceIdx()]); } template const T* UBTNode::GetNodeMemory(const FBehaviorTreeSearchData& SearchData) const { return GetNodeMemory(SearchData.OwnerComp.InstanceStack[SearchData.OwnerComp.GetActiveInstanceIdx()]); } template T* UBTNode::GetNodeMemory(FBehaviorTreeInstance& BTInstance) const { return (T*)(BTInstance.GetInstanceMemory().GetData() + MemoryOffset); } template const T* UBTNode::GetNodeMemory(const FBehaviorTreeInstance& BTInstance) const { return (const T*)(BTInstance.GetInstanceMemory().GetData() + MemoryOffset); } template T* UBTNode::CastInstanceNodeMemory(uint8* NodeMemory) const { // using '<=' rather than '==' to allow child classes to extend parent's // memory class as well (which would make GetInstanceMemorySize return // a value equal or greater to sizeof(T)). checkf(sizeof(T) <= GetInstanceMemorySize(), TEXT("Requesting type of %zu bytes but GetInstanceMemorySize returns %u. Make sure GetInstanceMemorySize is implemented properly in %s class hierarchy."), sizeof(T), GetInstanceMemorySize(), *GetFName().ToString()); return reinterpret_cast(NodeMemory); } template T* UBTNode::GetSpecialNodeMemory(uint8* NodeMemory) const { const int32 SpecialMemorySize = GetSpecialMemorySize(); return SpecialMemorySize ? (T*)(NodeMemory - ((SpecialMemorySize + 3) & ~3)) : nullptr; }