// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #pragma once #include "BehaviorKnowledge.h" #include "BehaviorTreeNode.h" #include "BehaviorKnowledgeSelector.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/BitArray.h" #include "Engine/Core/Math/Vector3.h" #include "Engine/Scripting/ScriptingObjectReference.h" #include "Engine/Content/AssetReference.h" #include "Engine/Level/Tags.h" #include "Engine/Navigation/NavMeshRuntime.h" class Actor; /// /// Base class for compound Behavior Tree nodes that composite child nodes. /// API_CLASS(Abstract) class FLAXENGINE_API BehaviorTreeCompoundNode : public BehaviorTreeNode { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeCompoundNode, BehaviorTreeNode); /// /// List with all child nodes. /// API_FIELD(Readonly) Array> Children; public: // [BehaviorTreeNode] void Init(BehaviorTree* tree) override; BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override; protected: // [BehaviorTreeNode] void BecomeIrrelevant(const BehaviorUpdateContext& context) override; }; /// /// Sequence node updates all its children (from left to right) as long as they return success. If any child fails, the sequence is failed. /// API_CLASS() class FLAXENGINE_API BehaviorTreeSequenceNode : public BehaviorTreeCompoundNode { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeSequenceNode, BehaviorTreeCompoundNode); public: // [BehaviorTreeNode] int32 GetStateSize() const override; void InitState(const BehaviorUpdateContext& context) override; BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override; private: struct State { int32 CurrentChildIndex = 0; }; }; /// /// Selector node updates all its children (from left to right) until one of them succeeds. If all children fail, the selector fails. /// API_CLASS() class FLAXENGINE_API BehaviorTreeSelectorNode : public BehaviorTreeCompoundNode { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeSelectorNode, BehaviorTreeCompoundNode); public: // [BehaviorTreeNode] int32 GetStateSize() const override; void InitState(const BehaviorUpdateContext& context) override; BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override; private: struct State { int32 CurrentChildIndex = 0; }; }; /// /// Root node of the behavior tree. Contains logic properties and definitions for the runtime. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeRootNode : public BehaviorTreeSequenceNode { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeRootNode, BehaviorTreeSequenceNode); API_AUTO_SERIALIZATION(); // Full typename of the blackboard data type (structure or class). Spawned for each instance of the behavior. API_FIELD(Attributes="EditorOrder(0), TypeReference(\"\", \"IsValidBlackboardType\"), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.TypeNameEditor\")") StringAnsi BlackboardType; // List of full typenames of the behavior goals (structure or class). API_FIELD(Attributes="EditorOrder(10), Collection(OverrideEditorTypeName=\"FlaxEditor.CustomEditors.Editors.TypeNameEditor\")") Array GoalTypes; // The target amount of the behavior logic updates per second. API_FIELD(Attributes="EditorOrder(100)") float UpdateFPS = 10.0f; }; /// /// Delay node that waits a specific amount of time while executed. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeDelayNode : public BehaviorTreeNode { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeDelayNode, BehaviorTreeNode); API_AUTO_SERIALIZATION(); // Time in seconds to wait when node gets activated. Unused if WaitTimeSelector is used. API_FIELD(Attributes="EditorOrder(10), Limit(0)") float WaitTime = 3.0f; // Wait time randomization range to deviate original value. API_FIELD(Attributes="EditorOrder(20), Limit(0)") float RandomDeviation = 0.0f; // Wait time from behavior's knowledge (blackboard, goal or sensor). If set, overrides WaitTime but still uses RandomDeviation. API_FIELD(Attributes="EditorOrder(30)") BehaviorKnowledgeSelector WaitTimeSelector; public: // [BehaviorTreeNode] int32 GetStateSize() const override; void InitState(const BehaviorUpdateContext& context) override; BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override; #if USE_EDITOR String GetDebugInfo(const BehaviorUpdateContext& context) const override; #endif private: struct State { float TimeLeft; }; }; /// /// Sub-tree node runs a nested Behavior Tree within this tree. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeSubTreeNode : public BehaviorTreeNode { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeSubTreeNode, BehaviorTreeNode); API_AUTO_SERIALIZATION(); // Nested behavior tree to execute within this node. API_FIELD(Attributes="EditorOrder(10), Limit(0)") AssetReference Tree; public: // [BehaviorTreeNode] int32 GetStateSize() const override; void InitState(const BehaviorUpdateContext& context) override; void ReleaseState(const BehaviorUpdateContext& context) override; BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override; private: struct State { Array Memory; BitArray<> RelevantNodes; }; }; /// /// Forces behavior execution end with a specific result (eg. force fail). /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeForceFinishNode : public BehaviorTreeNode { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeForceFinishNode, BehaviorTreeNode); API_AUTO_SERIALIZATION(); // Execution result. API_FIELD() BehaviorUpdateResult Result = BehaviorUpdateResult::Success; public: // [BehaviorTreeNode] BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override; }; /// /// Moves an actor to the specific target location. Uses pathfinding on navmesh. /// API_CLASS() class FLAXENGINE_API BehaviorTreeMoveToNode : public BehaviorTreeNode { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeMoveToNode, BehaviorTreeNode); API_AUTO_SERIALIZATION(); // The agent actor to move. If not set, uses Behavior's parent actor. API_FIELD(Attributes="EditorOrder(10)") BehaviorKnowledgeSelector Agent; // The agent movement speed. Default value is 100 units/second. API_FIELD(Attributes="EditorOrder(15)") BehaviorKnowledgeSelector MovementSpeed; // The target movement object. API_FIELD(Attributes="EditorOrder(30)") BehaviorKnowledgeSelector Target; // The target movement location. API_FIELD(Attributes="EditorOrder(35)") BehaviorKnowledgeSelector TargetLocation; // Threshold value between Agent and Target goal location for destination reach test. API_FIELD(Attributes="EditorOrder(100), Limit(0)") float AcceptableRadius = 5.0f; // Threshold value for the Target actor location offset that will trigger re-pathing to find a new path. API_FIELD(Attributes="EditorOrder(110), Limit(0)") float TargetGoalUpdateTolerance = 4.0f; // If checked, the movement will use navigation system pathfinding, otherwise direct motion to the target location will happen. API_FIELD(Attributes="EditorOrder(120)") bool UsePathfinding = true; // If checked, the movement will start even if there is no direct path to the target (only partial). API_FIELD(Attributes="EditorOrder(130)") bool UsePartialPath = true; // If checked, the target goal location will be updated while Target actor moves. API_FIELD(Attributes="EditorOrder(140)") bool UseTargetGoalUpdate = true; public: // Applies the movement to the agent. Returns true if cannot move. API_FUNCTION() virtual bool Move(Actor* agent, const Vector3& move) const; // Returns the navmesh to use for the path-finding. Can query nav agent properties from the agent actor to select navmesh. API_FUNCTION() virtual NavMeshRuntime* GetNavMesh(Actor* agent) const; // Returns the agent dimensions used for path following (eg. goal reachability test). API_FUNCTION() virtual void GetAgentSize(Actor* agent, API_PARAM(Out) float& outRadius, API_PARAM(Out) float& outHeight) const; public: // [BehaviorTreeNode] int32 GetStateSize() const override; void InitState(const BehaviorUpdateContext& context) override; void ReleaseState(const BehaviorUpdateContext& context) override; BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override; #if USE_EDITOR String GetDebugInfo(const BehaviorUpdateContext& context) const override; #endif protected: struct State { bool HasPath = false; // True if has Path computed bool HasTick = false; // True if OnUpdate is binded Vector3 GoalLocation; BehaviorTreeMoveToNode* Node; BehaviorKnowledge* Knowledge; ScriptingObjectReference Agent; Array Path; Vector3 AgentOffset; // Offset between agent position and path position (aka feet offset) float NavAgentRadius; // Size of the agent used to compute navmesh Float3 UpVector; // Path Up vector (from navmesh orientation) int32 TargetPathIndex; // Index of the next path point to go to BehaviorUpdateResult Result; // Current result of the OnUpdate void OnUpdate(); }; }; /// /// Inverts node's result - fails if node succeeded or succeeds if node failed. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeInvertDecorator : public BehaviorTreeDecorator { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeInvertDecorator, BehaviorTreeDecorator); public: // [BehaviorTreeNode] void PostUpdate(const BehaviorUpdateContext& context, BehaviorUpdateResult& result) override; }; /// /// Forces node to success - even if it failed. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeForceSuccessDecorator : public BehaviorTreeDecorator { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeForceSuccessDecorator, BehaviorTreeDecorator); public: // [BehaviorTreeNode] void PostUpdate(const BehaviorUpdateContext& context, BehaviorUpdateResult& result) override; }; /// /// Forces node to fail - even if it succeeded. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeForceFailedDecorator : public BehaviorTreeDecorator { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeForceFailedDecorator, BehaviorTreeDecorator); public: // [BehaviorTreeNode] void PostUpdate(const BehaviorUpdateContext& context, BehaviorUpdateResult& result) override; }; /// /// Loops node execution multiple times as long as it doesn't fail. Returns the last iteration result. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeLoopDecorator : public BehaviorTreeDecorator { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeLoopDecorator, BehaviorTreeDecorator); API_AUTO_SERIALIZATION(); // Amount of times to execute the node. Unused if LoopCountSelector is used. API_FIELD(Attributes="EditorOrder(10), Limit(0)") int32 LoopCount = 3; // Amount of times to execute the node from behavior's knowledge (blackboard, goal or sensor). If set, overrides LoopCount. API_FIELD(Attributes="EditorOrder(20)") BehaviorKnowledgeSelector LoopCountSelector; public: // [BehaviorTreeNode] int32 GetStateSize() const override; void InitState(const BehaviorUpdateContext& context) override; void PostUpdate(const BehaviorUpdateContext& context, BehaviorUpdateResult& result) override; struct State { int32 Loops; }; }; /// /// Limits maximum duration of the node execution time (in seconds). Node will fail if it runs out of time. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeTimeLimitDecorator : public BehaviorTreeDecorator { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeTimeLimitDecorator, BehaviorTreeDecorator); API_AUTO_SERIALIZATION(); // Maximum node execution time (in seconds). Unused if MaxDurationSelector is used. API_FIELD(Attributes="EditorOrder(10), Limit(0)") float MaxDuration = 3.0; // Duration time randomization range to deviate original value. API_FIELD(Attributes="EditorOrder(20), Limit(0)") float RandomDeviation = 0.0f; // Maximum node execution time (in seconds) from behavior's knowledge (blackboard, goal or sensor). If set, overrides MaxDuration but still uses RandomDeviation. API_FIELD(Attributes="EditorOrder(20)") BehaviorKnowledgeSelector MaxDurationSelector; public: // [BehaviorTreeNode] int32 GetStateSize() const override; void InitState(const BehaviorUpdateContext& context) override; BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override; struct State { float TimeLeft; }; }; /// /// Adds cooldown in between node executions. Blocks node execution for a given duration after last run. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeCooldownDecorator : public BehaviorTreeDecorator { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeCooldownDecorator, BehaviorTreeDecorator); API_AUTO_SERIALIZATION(); // Minimum cooldown time (in seconds). Unused if MinDurationSelector is used. API_FIELD(Attributes="EditorOrder(10), Limit(0)") float MinDuration = 3.0; // Duration time randomization range to deviate original value. API_FIELD(Attributes="EditorOrder(20), Limit(0)") float RandomDeviation = 0.0f; // Minimum cooldown time (in seconds) from behavior's knowledge (blackboard, goal or sensor). If set, overrides MinDuration but still uses RandomDeviation. API_FIELD(Attributes="EditorOrder(20)") BehaviorKnowledgeSelector MinDurationSelector; public: // [BehaviorTreeNode] int32 GetStateSize() const override; void InitState(const BehaviorUpdateContext& context) override; void ReleaseState(const BehaviorUpdateContext& context) override; bool CanUpdate(const BehaviorUpdateContext& context) override; void PostUpdate(const BehaviorUpdateContext& context, BehaviorUpdateResult& result) override; struct State { float EndTime; }; }; /// /// Checks certain knowledge value to conditionally enter the node. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeKnowledgeConditionalDecorator : public BehaviorTreeDecorator { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeKnowledgeConditionalDecorator, BehaviorTreeDecorator); API_AUTO_SERIALIZATION(); // The first value from behavior's knowledge (blackboard, goal or sensor) to use for comparision. API_FIELD(Attributes="EditorOrder(0)") BehaviorKnowledgeSelectorAny ValueA; // The second value to use for comparision (constant). API_FIELD(Attributes="EditorOrder(10)") float ValueB = 0.0f; // Values comparision mode. API_FIELD(Attributes="EditorOrder(20)") BehaviorValueComparison Comparison = BehaviorValueComparison::Equal; public: // [BehaviorTreeNode] bool CanUpdate(const BehaviorUpdateContext& context) override; }; /// /// Checks certain knowledge value to conditionally enter the node. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeKnowledgeValuesConditionalDecorator : public BehaviorTreeDecorator { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeKnowledgeValuesConditionalDecorator, BehaviorTreeDecorator); API_AUTO_SERIALIZATION(); // The first value from behavior's knowledge (blackboard, goal or sensor) to use for comparision. API_FIELD(Attributes="EditorOrder(0)") BehaviorKnowledgeSelectorAny ValueA; // The second value from behavior's knowledge (blackboard, goal or sensor) to use for comparision. API_FIELD(Attributes="EditorOrder(10)") BehaviorKnowledgeSelectorAny ValueB; // Values comparision mode. API_FIELD(Attributes="EditorOrder(20)") BehaviorValueComparison Comparison = BehaviorValueComparison::Equal; public: // [BehaviorTreeNode] bool CanUpdate(const BehaviorUpdateContext& context) override; }; /// /// Checks if certain actor (from knowledge) has a given tag assigned. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeHasTagDecorator : public BehaviorTreeDecorator { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeHasTagDecorator, BehaviorTreeDecorator); API_AUTO_SERIALIZATION(); // The actor value from behavior's knowledge (blackboard, goal or sensor) to check against tag ownership. API_FIELD(Attributes="EditorOrder(0)") BehaviorKnowledgeSelector Actor; // The tag to check. API_FIELD(Attributes="EditorOrder(10)") Tag Tag; // If checked, inverts condition - checks if actor doesn't have a tag. API_FIELD(Attributes="EditorOrder(20)") bool Invert = false; public: // [BehaviorTreeNode] bool CanUpdate(const BehaviorUpdateContext& context) override; }; /// /// Checks if certain goal has been added to Behavior knowledge. /// API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeHasGoalDecorator : public BehaviorTreeDecorator { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeHasGoalDecorator, BehaviorTreeDecorator); API_AUTO_SERIALIZATION(); // The goal type to check. API_FIELD(Attributes="EditorOrder(0), BehaviorKnowledgeSelector(IsGoalSelector = true)") BehaviorKnowledgeSelectorAny Goal; public: // [BehaviorTreeNode] bool CanUpdate(const BehaviorUpdateContext& context) override; };