Add Nested node to BT
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user