Add Decorators support to BT graph
This commit is contained in:
@@ -65,7 +65,7 @@ void BehaviorTreeGraph::Clear()
|
||||
|
||||
bool BehaviorTreeGraph::onNodeLoaded(Node* n)
|
||||
{
|
||||
if (n->GroupID == 19 && (n->TypeID == 1 || n->TypeID == 2))
|
||||
if (n->GroupID == 19 && (n->TypeID == 1 || n->TypeID == 2 || n->TypeID == 3))
|
||||
{
|
||||
// Create node instance object
|
||||
ScriptingTypeHandle type = Scripting::FindScriptingType((StringAnsiView)n->Values[0]);
|
||||
@@ -102,6 +102,26 @@ void BehaviorTreeGraph::LoadRecursive(Node& node)
|
||||
NodesStatesSize += node.Instance->GetStateSize();
|
||||
NodesCount++;
|
||||
|
||||
if (node.TypeID == 1 && node.Values.Count() >= 3)
|
||||
{
|
||||
// Load node decorators
|
||||
const auto& decoratorIds = node.Values[2];
|
||||
if (decoratorIds.Type.Type == VariantType::Blob && decoratorIds.AsBlob.Length)
|
||||
{
|
||||
const Span<uint32> ids((uint32*)decoratorIds.AsBlob.Data, decoratorIds.AsBlob.Length / sizeof(uint32));
|
||||
for (int32 i = 0; i < ids.Length(); i++)
|
||||
{
|
||||
Node* decorator = GetNode(ids[i]);
|
||||
if (decorator && decorator->Instance)
|
||||
{
|
||||
ASSERT_LOW_LAYER(decorator->Instance->Is<BehaviorTreeDecorator>());
|
||||
node.Instance->_decorators.Add((BehaviorTreeDecorator*)decorator->Instance);
|
||||
decorator->Instance->_parent = node.Instance;
|
||||
LoadRecursive(*decorator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (auto* nodeCompound = ScriptingObject::Cast<BehaviorTreeCompoundNode>(node.Instance))
|
||||
{
|
||||
auto& children = node.Boxes[1].Connections;
|
||||
@@ -116,6 +136,7 @@ void BehaviorTreeGraph::LoadRecursive(Node& node)
|
||||
if (child && child->Instance)
|
||||
{
|
||||
nodeCompound->Children.Add(child->Instance);
|
||||
child->Instance->_parent = nodeCompound;
|
||||
LoadRecursive(*child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Scripting/SerializableScriptingObject.h"
|
||||
#include "BehaviorTypes.h"
|
||||
|
||||
@@ -21,6 +22,11 @@ protected:
|
||||
API_FIELD(ReadOnly) int32 _memoryOffset = 0;
|
||||
// Execution index of the node within tree.
|
||||
API_FIELD(ReadOnly) int32 _executionIndex = -1;
|
||||
// Parent node that owns this node (parent composite or decorator attachment node).
|
||||
API_FIELD(ReadOnly) BehaviorTreeNode* _parent = nullptr;
|
||||
|
||||
private:
|
||||
Array<class BehaviorTreeDecorator*, InlinedAllocation<8>> _decorators;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
@@ -74,6 +80,10 @@ public:
|
||||
|
||||
// Helper utility to update node with state creation/cleanup depending on node relevancy.
|
||||
BehaviorUpdateResult InvokeUpdate(const BehaviorUpdateContext& context);
|
||||
// Helper utility to make node relevant and init it state.
|
||||
void BecomeRelevant(const BehaviorUpdateContext& context);
|
||||
// Helper utility to make node irrelevant and release its state (including any nested nodes).
|
||||
virtual void BecomeIrrelevant(const BehaviorUpdateContext& context, bool nodeOnly = false);
|
||||
|
||||
// [SerializableScriptingObject]
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
@@ -87,8 +97,31 @@ public:
|
||||
ASSERT((int32)sizeof(T) <= GetStateSize());
|
||||
return reinterpret_cast<T*>((byte*)memory + _memoryOffset);
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual void InvokeReleaseState(const BehaviorUpdateContext& context);
|
||||
};
|
||||
/// <summary>
|
||||
/// Base class for Behavior Tree node decorators. Decorators can implement conditional filtering or override node logic and execution flow.
|
||||
/// </summary>
|
||||
API_CLASS(Abstract) class FLAXENGINE_API BehaviorTreeDecorator : public BehaviorTreeNode
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeDecorator, BehaviorTreeNode);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the node can be updated (eg. decorator can block it depending on the gameplay conditions or its state).
|
||||
/// </summary>
|
||||
/// <param name="context">Behavior update context data.</param>
|
||||
/// <returns>True if can update, otherwise false to block it.</returns>
|
||||
API_FUNCTION() virtual bool CanUpdate(BehaviorUpdateContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after node update to post-process result or perform additional action.
|
||||
/// </summary>
|
||||
/// <param name="context">Behavior update context data.</param>
|
||||
/// <param name="result">The node update result. Can be modified by the decorator (eg. to force success).</param>
|
||||
API_FUNCTION() virtual void PostUpdate(BehaviorUpdateContext context, API_PARAM(ref) BehaviorUpdateResult& result)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,26 +42,72 @@ bool IsAssignableFrom(const StringAnsiView& to, const StringAnsiView& from)
|
||||
BehaviorUpdateResult BehaviorTreeNode::InvokeUpdate(const BehaviorUpdateContext& context)
|
||||
{
|
||||
ASSERT_LOW_LAYER(_executionIndex != -1);
|
||||
BitArray<>& relevantNodes = *(BitArray<>*)context.RelevantNodes;
|
||||
if (relevantNodes.Get(_executionIndex) == false)
|
||||
const BitArray<>& relevantNodes = *(const BitArray<>*)context.RelevantNodes;
|
||||
|
||||
// Check decorators if node can be executed
|
||||
for (BehaviorTreeDecorator* decorator : _decorators)
|
||||
{
|
||||
// Node becomes relevant so initialize it's state
|
||||
relevantNodes.Set(_executionIndex, true);
|
||||
InitState(context.Behavior, context.Memory);
|
||||
ASSERT_LOW_LAYER(decorator->_executionIndex != -1);
|
||||
if (relevantNodes.Get(decorator->_executionIndex) == false)
|
||||
decorator->BecomeRelevant(context);
|
||||
if (!decorator->CanUpdate(context))
|
||||
{
|
||||
return BehaviorUpdateResult::Failed;
|
||||
}
|
||||
}
|
||||
|
||||
// Make node relevant before update
|
||||
if (relevantNodes.Get(_executionIndex) == false)
|
||||
BecomeRelevant(context);
|
||||
|
||||
// Node-specific update
|
||||
const BehaviorUpdateResult result = Update(context);
|
||||
for (BehaviorTreeDecorator* decorator : _decorators)
|
||||
{
|
||||
decorator->Update(context);
|
||||
}
|
||||
BehaviorUpdateResult result = Update(context);
|
||||
for (BehaviorTreeDecorator* decorator : _decorators)
|
||||
{
|
||||
decorator->PostUpdate(context, result);
|
||||
}
|
||||
|
||||
// Check if node is not relevant anymore
|
||||
if (result != BehaviorUpdateResult::Running)
|
||||
{
|
||||
InvokeReleaseState(context);
|
||||
}
|
||||
BecomeIrrelevant(context);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void BehaviorTreeNode::BecomeRelevant(const BehaviorUpdateContext& context)
|
||||
{
|
||||
// Initialize state
|
||||
BitArray<>& relevantNodes = *(BitArray<>*)context.RelevantNodes;
|
||||
ASSERT_LOW_LAYER(relevantNodes.Get(_executionIndex) == false);
|
||||
relevantNodes.Set(_executionIndex, true);
|
||||
InitState(context.Behavior, context.Memory);
|
||||
}
|
||||
|
||||
void BehaviorTreeNode::BecomeIrrelevant(const BehaviorUpdateContext& context, bool nodeOnly)
|
||||
{
|
||||
// Release state
|
||||
BitArray<>& relevantNodes = *(BitArray<>*)context.RelevantNodes;
|
||||
ASSERT_LOW_LAYER(relevantNodes.Get(_executionIndex) == true);
|
||||
relevantNodes.Set(_executionIndex, false);
|
||||
ReleaseState(context.Behavior, context.Memory);
|
||||
|
||||
if (nodeOnly)
|
||||
return;
|
||||
|
||||
// Release decorators
|
||||
for (BehaviorTreeDecorator* decorator : _decorators)
|
||||
{
|
||||
if (relevantNodes.Get(decorator->_executionIndex) == true)
|
||||
{
|
||||
decorator->BecomeIrrelevant(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BehaviorTreeNode::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
{
|
||||
SerializableScriptingObject::Serialize(stream, otherObj);
|
||||
@@ -78,13 +124,6 @@ void BehaviorTreeNode::Deserialize(DeserializeStream& stream, ISerializeModifier
|
||||
DESERIALIZE(Name);
|
||||
}
|
||||
|
||||
void BehaviorTreeNode::InvokeReleaseState(const BehaviorUpdateContext& context)
|
||||
{
|
||||
BitArray<>& relevantNodes = *(BitArray<>*)context.RelevantNodes;
|
||||
relevantNodes.Set(_executionIndex, false);
|
||||
ReleaseState(context.Behavior, context.Memory);
|
||||
}
|
||||
|
||||
void BehaviorTreeCompoundNode::Init(BehaviorTree* tree)
|
||||
{
|
||||
for (BehaviorTreeNode* child : Children)
|
||||
@@ -102,18 +141,19 @@ BehaviorUpdateResult BehaviorTreeCompoundNode::Update(BehaviorUpdateContext cont
|
||||
return result;
|
||||
}
|
||||
|
||||
void BehaviorTreeCompoundNode::InvokeReleaseState(const BehaviorUpdateContext& context)
|
||||
void BehaviorTreeCompoundNode::BecomeIrrelevant(const BehaviorUpdateContext& context, bool nodeOnly)
|
||||
{
|
||||
// Make any nested nodes irrelevant as well
|
||||
const BitArray<>& relevantNodes = *(const BitArray<>*)context.RelevantNodes;
|
||||
for (BehaviorTreeNode* child : Children)
|
||||
{
|
||||
if (relevantNodes.Get(child->_executionIndex) == true)
|
||||
{
|
||||
child->InvokeReleaseState(context);
|
||||
child->BecomeIrrelevant(context);
|
||||
}
|
||||
}
|
||||
|
||||
BehaviorTreeNode::InvokeReleaseState(context);
|
||||
BehaviorTreeNode::BecomeIrrelevant(context, nodeOnly);
|
||||
}
|
||||
|
||||
int32 BehaviorTreeSequenceNode::GetStateSize() const
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
|
||||
protected:
|
||||
// [BehaviorTreeNode]
|
||||
void InvokeReleaseState(const BehaviorUpdateContext& context) override;
|
||||
void BecomeIrrelevant(const BehaviorUpdateContext& context, bool nodeOnly) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user