Add Nested node to BT

This commit is contained in:
Wojtek Figat
2023-08-21 17:38:48 +02:00
parent a6e503d21b
commit cc5cde5bc7
5 changed files with 132 additions and 4 deletions

View File

@@ -4,15 +4,49 @@
#include "Behavior.h"
#include "BehaviorKnowledge.h"
#include "Engine/Core/Random.h"
#include "Engine/Scripting/Scripting.h"
#if USE_CSHARP
#include "Engine/Scripting/ManagedCLR/MClass.h"
#endif
#include "Engine/Serialization/Serialization.h"
bool IsAssignableFrom(const StringAnsiView& to, const StringAnsiView& from)
{
// Special case of null
if (to.IsEmpty())
return from.IsEmpty();
if (from.IsEmpty())
return false;
// Exact typename math
if (to == from)
return true;
// Scripting Type match
const ScriptingTypeHandle typeHandleTo = Scripting::FindScriptingType(to);
const ScriptingTypeHandle typeHandleFrom = Scripting::FindScriptingType(from);
if (typeHandleTo && typeHandleFrom)
return typeHandleTo.IsAssignableFrom(typeHandleFrom);
#if USE_CSHARP
// MClass match
const auto mclassTo = Scripting::FindClass(to);
const auto mclassFrom = Scripting::FindClass(from);
if (mclassTo && mclassFrom)
return mclassTo == mclassFrom || mclassFrom->IsSubClassOf(mclassTo);
#endif
return false;
}
BehaviorUpdateResult BehaviorTreeNode::InvokeUpdate(const BehaviorUpdateContext& context)
{
ASSERT_LOW_LAYER(_executionIndex != -1);
if (context.Knowledge->RelevantNodes.Get(_executionIndex) == false)
BitArray<>& relevantNodes = *(BitArray<>*)context.RelevantNodes;
if (relevantNodes.Get(_executionIndex) == false)
{
// Node becomes relevant so initialize it's state
context.Knowledge->RelevantNodes.Set(_executionIndex, true);
relevantNodes.Set(_executionIndex, true);
InitState(context.Behavior, context.Memory);
}
@@ -22,7 +56,7 @@ BehaviorUpdateResult BehaviorTreeNode::InvokeUpdate(const BehaviorUpdateContext&
// Check if node is not relevant anymore
if (result != BehaviorUpdateResult::Running)
{
context.Knowledge->RelevantNodes.Set(_executionIndex, false);
relevantNodes.Set(_executionIndex, false);
ReleaseState(context.Behavior, context.Memory);
}
@@ -152,3 +186,63 @@ BehaviorUpdateResult BehaviorTreeDelayNode::Update(BehaviorUpdateContext context
state->TimeLeft -= context.DeltaTime;
return state->TimeLeft <= 0.0f ? BehaviorUpdateResult::Success : BehaviorUpdateResult::Running;
}
int32 BehaviorTreeSubTreeNode::GetStateSize() const
{
return sizeof(State);
}
void BehaviorTreeSubTreeNode::InitState(Behavior* behavior, void* memory)
{
auto state = GetState<State>(memory);
new(state)State();
const BehaviorTree* tree = Tree.Get();
if (!tree || tree->WaitForLoaded())
return;
state->Memory.Resize(tree->Graph.NodesStatesSize);
state->RelevantNodes.Resize(tree->Graph.NodesCount, false);
state->RelevantNodes.SetAll(false);
}
void BehaviorTreeSubTreeNode::ReleaseState(Behavior* behavior, void* memory)
{
auto state = GetState<State>(memory);
const BehaviorTree* tree = Tree.Get();
if (tree && tree->IsLoaded())
{
for (const auto& node : tree->Graph.Nodes)
{
if (node.Instance && node.Instance->_executionIndex != -1 && state->RelevantNodes[node.Instance->_executionIndex])
node.Instance->ReleaseState(behavior, state->Memory.Get());
}
}
state->~State();
}
BehaviorUpdateResult BehaviorTreeSubTreeNode::Update(BehaviorUpdateContext context)
{
const BehaviorTree* tree = Tree.Get();
if (!tree || !tree->Graph.Root)
return BehaviorUpdateResult::Failed;
const StringAnsiView treeBlackboardType = tree->Graph.Root->BlackboardType;
if (treeBlackboardType.HasChars())
{
// Validate if nested tree blackboard data matches (the same type or base type)
const VariantType& blackboardType = context.Knowledge->Blackboard.Type;
if (IsAssignableFrom(treeBlackboardType, StringAnsiView(blackboardType.GetTypeName())))
{
LOG(Error, "Cannot use nested '{}' with Blackboard of type '{}' inside '{}' with Blackboard of type '{}'",
tree->ToString(), String(treeBlackboardType),
context.Knowledge->Tree->ToString(), blackboardType.ToString());
return BehaviorUpdateResult::Failed;
}
}
// Override memory with custom one for the subtree
auto state = GetState<State>(context.Memory);
context.Memory = state->Memory.Get();
context.RelevantNodes = &state->RelevantNodes;
// Run nested tree
return tree->Graph.Root->InvokeUpdate(context);
}