Add GetDebugInfo to BT nodes for debugging
This commit is contained in:
@@ -29,6 +29,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
protected const float DecoratorsMarginX = 5.0f;
|
protected const float DecoratorsMarginX = 5.0f;
|
||||||
protected const float DecoratorsMarginY = 2.0f;
|
protected const float DecoratorsMarginY = 2.0f;
|
||||||
|
|
||||||
|
protected string _debugInfo;
|
||||||
|
protected Float2 _debugInfoSize;
|
||||||
protected ScriptType _type;
|
protected ScriptType _type;
|
||||||
internal bool _isValueEditing;
|
internal bool _isValueEditing;
|
||||||
|
|
||||||
@@ -52,6 +54,21 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void UpdateDebug(Behavior behavior)
|
||||||
|
{
|
||||||
|
BehaviorTreeNode instance = null;
|
||||||
|
if (behavior)
|
||||||
|
{
|
||||||
|
// Try to use instance from the currently debugged behavior
|
||||||
|
// TODO: support nodes from nested trees
|
||||||
|
instance = behavior.Tree.GetNodeInstance(ID);
|
||||||
|
}
|
||||||
|
var size = _debugInfoSize;
|
||||||
|
UpdateDebugInfo(instance, behavior);
|
||||||
|
if (size != _debugInfoSize)
|
||||||
|
ResizeAuto();
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void UpdateTitle()
|
protected virtual void UpdateTitle()
|
||||||
{
|
{
|
||||||
string title = null;
|
string title = null;
|
||||||
@@ -69,6 +86,21 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Title = title;
|
Title = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateDebugInfo(BehaviorTreeNode instance = null, Behavior behavior = null)
|
||||||
|
{
|
||||||
|
_debugInfo = null;
|
||||||
|
_debugInfoSize = Float2.Zero;
|
||||||
|
if (!instance)
|
||||||
|
instance = Instance;
|
||||||
|
if (instance)
|
||||||
|
{
|
||||||
|
// Get debug description for the node based on the current settings
|
||||||
|
_debugInfo = Behavior.GetNodeDebugInfo(instance, behavior);
|
||||||
|
if (!string.IsNullOrEmpty(_debugInfo))
|
||||||
|
_debugInfoSize = Style.Current.FontSmall.MeasureText(_debugInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnLoaded(SurfaceNodeActions action)
|
public override void OnLoaded(SurfaceNodeActions action)
|
||||||
{
|
{
|
||||||
base.OnLoaded(action);
|
base.OnLoaded(action);
|
||||||
@@ -97,6 +129,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
Instance = null;
|
Instance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateDebugInfo();
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +162,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateDebugInfo();
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,10 +173,23 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
ResizeAuto();
|
ResizeAuto();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
base.Draw();
|
||||||
|
|
||||||
|
// Debug Info
|
||||||
|
if (!string.IsNullOrEmpty(_debugInfo))
|
||||||
|
{
|
||||||
|
var style = Style.Current;
|
||||||
|
Render2D.DrawText(style.FontSmall, _debugInfo, new Rectangle(4, _headerRect.Bottom + 4, _debugInfoSize), style.Foreground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
if (IsDisposing)
|
if (IsDisposing)
|
||||||
return;
|
return;
|
||||||
|
_debugInfo = null;
|
||||||
_type = ScriptType.Null;
|
_type = ScriptType.Null;
|
||||||
FlaxEngine.Object.Destroy(ref Instance);
|
FlaxEngine.Object.Destroy(ref Instance);
|
||||||
|
|
||||||
@@ -258,6 +305,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
// Return decorator nodes attached to this node to be moved/copied/pasted as a one
|
||||||
SurfaceNode[] result = null;
|
SurfaceNode[] result = null;
|
||||||
var ids = Values.Length >= 3 ? Values[2] as byte[] : null;
|
var ids = Values.Length >= 3 ? Values[2] as byte[] : null;
|
||||||
if (ids != null)
|
if (ids != null)
|
||||||
@@ -425,6 +473,11 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
var titleLabelFont = Style.Current.FontLarge;
|
var titleLabelFont = Style.Current.FontLarge;
|
||||||
width = Mathf.Max(width, 100.0f);
|
width = Mathf.Max(width, 100.0f);
|
||||||
width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30);
|
width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30);
|
||||||
|
if (_debugInfoSize.X > 0)
|
||||||
|
{
|
||||||
|
width = Mathf.Max(width, _debugInfoSize.X + 8.0f);
|
||||||
|
height += _debugInfoSize.Y + 8.0f;
|
||||||
|
}
|
||||||
if (_input != null && _input.Visible)
|
if (_input != null && _input.Visible)
|
||||||
height += ConnectionAreaHeight;
|
height += ConnectionAreaHeight;
|
||||||
if (_output != null && _output.Visible)
|
if (_output != null && _output.Visible)
|
||||||
@@ -463,9 +516,12 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
const float closeButtonSize = FlaxEditor.Surface.Constants.NodeCloseButtonSize;
|
const float closeButtonSize = FlaxEditor.Surface.Constants.NodeCloseButtonSize;
|
||||||
_headerRect = new Rectangle(0, bounds.Y - Y, bounds.Width, headerSize);
|
_headerRect = new Rectangle(0, bounds.Y - Y, bounds.Width, headerSize);
|
||||||
_closeButtonRect = new Rectangle(bounds.Width - closeButtonSize - closeButtonMargin, _headerRect.Y + closeButtonMargin, closeButtonSize, closeButtonSize);
|
_closeButtonRect = new Rectangle(bounds.Width - closeButtonSize - closeButtonMargin, _headerRect.Y + closeButtonMargin, closeButtonSize, closeButtonSize);
|
||||||
_footerRect = new Rectangle(0, _headerRect.Bottom, bounds.Width, footerSize);
|
_footerRect = new Rectangle(0, bounds.Height - footerSize, bounds.Width, footerSize);
|
||||||
if (_output != null && _output.Visible)
|
if (_output != null && _output.Visible)
|
||||||
|
{
|
||||||
|
_footerRect.Y -= ConnectionAreaHeight;
|
||||||
_output.Bounds = new Rectangle(ConnectionAreaMargin, bounds.Height - ConnectionAreaHeight, bounds.Width - ConnectionAreaMargin * 2, ConnectionAreaHeight);
|
_output.Bounds = new Rectangle(ConnectionAreaMargin, bounds.Height - ConnectionAreaHeight, bounds.Width - ConnectionAreaMargin * 2, ConnectionAreaHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnLocationChanged()
|
protected override void OnLocationChanged()
|
||||||
@@ -589,7 +645,12 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
|
|
||||||
protected override Float2 CalculateNodeSize(float width, float height)
|
protected override Float2 CalculateNodeSize(float width, float height)
|
||||||
{
|
{
|
||||||
return new Float2(width + FlaxEditor.Surface.Constants.NodeCloseButtonSize + 2 * DecoratorsMarginX, height + FlaxEditor.Surface.Constants.NodeHeaderSize);
|
if (_debugInfoSize.X > 0)
|
||||||
|
{
|
||||||
|
width = Mathf.Max(width, _debugInfoSize.X + 8.0f);
|
||||||
|
height += _debugInfoSize.Y + 8.0f;
|
||||||
|
}
|
||||||
|
return new Float2(width + FlaxEditor.Surface.Constants.NodeCloseButtonSize * 2 + DecoratorsMarginX * 2, height + FlaxEditor.Surface.Constants.NodeHeaderSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateRectangles()
|
protected override void UpdateRectangles()
|
||||||
@@ -603,15 +664,22 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
|
|
||||||
protected override void UpdateTitle()
|
protected override void UpdateTitle()
|
||||||
{
|
{
|
||||||
var title = Title;
|
|
||||||
|
|
||||||
base.UpdateTitle();
|
|
||||||
|
|
||||||
// Update parent node on title change
|
// Update parent node on title change
|
||||||
|
var title = Title;
|
||||||
|
base.UpdateTitle();
|
||||||
if (title != Title)
|
if (title != Title)
|
||||||
Node?.ResizeAuto();
|
Node?.ResizeAuto();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void UpdateDebugInfo(BehaviorTreeNode instance, Behavior behavior)
|
||||||
|
{
|
||||||
|
// Update parent node on debug text change
|
||||||
|
var debugInfoSize = _debugInfoSize;
|
||||||
|
base.UpdateDebugInfo(instance, behavior);
|
||||||
|
if (debugInfoSize != _debugInfoSize)
|
||||||
|
Node?.ResizeAuto();
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnLoaded(SurfaceNodeActions action)
|
public override void OnLoaded(SurfaceNodeActions action)
|
||||||
{
|
{
|
||||||
// Add drag button to reorder decorator
|
// Add drag button to reorder decorator
|
||||||
|
|||||||
@@ -449,6 +449,18 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
UpdateKnowledge();
|
UpdateKnowledge();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateDebugInfos(bool playMode)
|
||||||
|
{
|
||||||
|
var behavior = playMode ? (Behavior)_behaviorPicker.Value : null;
|
||||||
|
if (!behavior)
|
||||||
|
behavior = null;
|
||||||
|
foreach (var e in _surface.Nodes)
|
||||||
|
{
|
||||||
|
if (e is Surface.Archetypes.BehaviorTree.NodeBase node)
|
||||||
|
node.UpdateDebug(behavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnPlayBegin()
|
public override void OnPlayBegin()
|
||||||
{
|
{
|
||||||
@@ -461,6 +473,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
public override void OnPlayEnd()
|
public override void OnPlayEnd()
|
||||||
{
|
{
|
||||||
SetCanEdit(true);
|
SetCanEdit(true);
|
||||||
|
UpdateDebugInfos(false);
|
||||||
|
|
||||||
base.OnPlayEnd();
|
base.OnPlayEnd();
|
||||||
}
|
}
|
||||||
@@ -520,6 +533,12 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
// Update behavior debugging
|
// Update behavior debugging
|
||||||
SetCanDebug(Editor.IsPlayMode && _behaviorPicker.Value);
|
SetCanDebug(Editor.IsPlayMode && _behaviorPicker.Value);
|
||||||
|
|
||||||
|
// Update debug info texts on all nodes
|
||||||
|
if (Editor.IsPlayMode)
|
||||||
|
{
|
||||||
|
UpdateDebugInfos(true);
|
||||||
|
}
|
||||||
|
|
||||||
base.Update(deltaTime);
|
base.Update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -166,3 +166,25 @@ void Behavior::OnDisable()
|
|||||||
{
|
{
|
||||||
BehaviorServiceInstance.UpdateList.Remove(this);
|
BehaviorServiceInstance.UpdateList.Remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
|
String Behavior::GetNodeDebugInfo(BehaviorTreeNode* node, Behavior* behavior)
|
||||||
|
{
|
||||||
|
if (!node)
|
||||||
|
return String::Empty;
|
||||||
|
BehaviorUpdateContext context;
|
||||||
|
Platform::MemoryClear(&context, sizeof(context));
|
||||||
|
if (behavior && behavior->_knowledge.RelevantNodes.Get(node->_executionIndex))
|
||||||
|
{
|
||||||
|
// Pass behavior and knowledge data only for relevant nodes to properly access it
|
||||||
|
context.Behavior = behavior;
|
||||||
|
context.Knowledge = &behavior->_knowledge;
|
||||||
|
context.Memory = behavior->_knowledge.Memory;
|
||||||
|
context.RelevantNodes = &behavior->_knowledge.RelevantNodes;
|
||||||
|
context.Time = behavior->_totalTime;
|
||||||
|
}
|
||||||
|
return node->GetDebugInfo(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ private:
|
|||||||
float _accumulatedTime = 0.0f;
|
float _accumulatedTime = 0.0f;
|
||||||
float _totalTime = 0.0f;
|
float _totalTime = 0.0f;
|
||||||
BehaviorUpdateResult _result = BehaviorUpdateResult::Success;
|
BehaviorUpdateResult _result = BehaviorUpdateResult::Success;
|
||||||
void* _memory = nullptr;
|
|
||||||
|
|
||||||
void UpdateAsync();
|
void UpdateAsync();
|
||||||
|
|
||||||
@@ -91,4 +90,10 @@ public:
|
|||||||
// [Script]
|
// [Script]
|
||||||
void OnEnable() override;
|
void OnEnable() override;
|
||||||
void OnDisable() override;
|
void OnDisable() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if USE_EDITOR
|
||||||
|
// Editor-only utility to debug nodes state.
|
||||||
|
API_FUNCTION(Internal) static String GetNodeDebugInfo(BehaviorTreeNode* node, Behavior* behavior);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -119,12 +119,12 @@ bool AccessBehaviorKnowledge(BehaviorKnowledge* knowledge, const StringAnsiView&
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BehaviorKnowledgeSelectorAny::Set(BehaviorKnowledge* knowledge, const Variant& value)
|
bool BehaviorKnowledgeSelectorAny::Set(BehaviorKnowledge* knowledge, const Variant& value) const
|
||||||
{
|
{
|
||||||
return knowledge && knowledge->Set(Path, value);
|
return knowledge && knowledge->Set(Path, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant BehaviorKnowledgeSelectorAny::Get(BehaviorKnowledge* knowledge)
|
Variant BehaviorKnowledgeSelectorAny::Get(const BehaviorKnowledge* knowledge) const
|
||||||
{
|
{
|
||||||
Variant value;
|
Variant value;
|
||||||
if (knowledge)
|
if (knowledge)
|
||||||
@@ -132,7 +132,7 @@ Variant BehaviorKnowledgeSelectorAny::Get(BehaviorKnowledge* knowledge)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BehaviorKnowledgeSelectorAny::TryGet(BehaviorKnowledge* knowledge, Variant& value)
|
bool BehaviorKnowledgeSelectorAny::TryGet(const BehaviorKnowledge* knowledge, Variant& value) const
|
||||||
{
|
{
|
||||||
return knowledge && knowledge->Get(Path, value);
|
return knowledge && knowledge->Get(Path, value);
|
||||||
}
|
}
|
||||||
@@ -182,9 +182,9 @@ void BehaviorKnowledge::FreeMemory()
|
|||||||
Tree = nullptr;
|
Tree = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BehaviorKnowledge::Get(const StringAnsiView& path, Variant& value)
|
bool BehaviorKnowledge::Get(const StringAnsiView& path, Variant& value) const
|
||||||
{
|
{
|
||||||
return AccessBehaviorKnowledge(this, path, value, false);
|
return AccessBehaviorKnowledge(const_cast<BehaviorKnowledge*>(this), path, value, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BehaviorKnowledge::Set(const StringAnsiView& path, const Variant& value)
|
bool BehaviorKnowledge::Set(const StringAnsiView& path, const Variant& value)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public:
|
|||||||
/// <param name="path">Selector path.</param>
|
/// <param name="path">Selector path.</param>
|
||||||
/// <param name="value">Result value (valid only when returned true).</param>
|
/// <param name="value">Result value (valid only when returned true).</param>
|
||||||
/// <returns>True if got value, otherwise false.</returns>
|
/// <returns>True if got value, otherwise false.</returns>
|
||||||
API_FUNCTION() bool Get(const StringAnsiView& path, API_PARAM(Out) Variant& value);
|
API_FUNCTION() bool Get(const StringAnsiView& path, API_PARAM(Out) Variant& value) const;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the knowledge item value via selector path.
|
/// Sets the knowledge item value via selector path.
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ API_STRUCT(NoDefault, MarshalAs=StringAnsi) struct FLAXENGINE_API BehaviorKnowle
|
|||||||
API_FIELD() StringAnsi Path;
|
API_FIELD() StringAnsi Path;
|
||||||
|
|
||||||
// Sets the selected knowledge value (as Variant).
|
// Sets the selected knowledge value (as Variant).
|
||||||
bool Set(BehaviorKnowledge* knowledge, const Variant& value);
|
bool Set(BehaviorKnowledge* knowledge, const Variant& value) const;
|
||||||
|
|
||||||
// Gets the selected knowledge value (as Variant).
|
// Gets the selected knowledge value (as Variant).
|
||||||
Variant Get(BehaviorKnowledge* knowledge);
|
Variant Get(const BehaviorKnowledge* knowledge) const;
|
||||||
|
|
||||||
// Tries to get the selected knowledge value (as Variant). Returns true if got value, otherwise false.
|
// Tries to get the selected knowledge value (as Variant). Returns true if got value, otherwise false.
|
||||||
bool TryGet(BehaviorKnowledge* knowledge, Variant& value);
|
bool TryGet(const BehaviorKnowledge* knowledge, Variant& value) const;
|
||||||
|
|
||||||
FORCE_INLINE bool operator==(const BehaviorKnowledgeSelectorAny& other) const
|
FORCE_INLINE bool operator==(const BehaviorKnowledgeSelectorAny& other) const
|
||||||
{
|
{
|
||||||
@@ -63,19 +63,19 @@ API_STRUCT(InBuild, Template, MarshalAs=StringAnsi) struct FLAXENGINE_API Behavi
|
|||||||
using BehaviorKnowledgeSelectorAny::TryGet;
|
using BehaviorKnowledgeSelectorAny::TryGet;
|
||||||
|
|
||||||
// Sets the selected knowledge value (typed).
|
// Sets the selected knowledge value (typed).
|
||||||
FORCE_INLINE void Set(BehaviorKnowledge* knowledge, const T& value)
|
FORCE_INLINE void Set(BehaviorKnowledge* knowledge, const T& value) const
|
||||||
{
|
{
|
||||||
BehaviorKnowledgeSelectorAny::Set(knowledge, Variant(value));
|
BehaviorKnowledgeSelectorAny::Set(knowledge, Variant(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the selected knowledge value (typed).
|
// Gets the selected knowledge value (typed).
|
||||||
FORCE_INLINE T Get(BehaviorKnowledge* knowledge)
|
FORCE_INLINE T Get(const BehaviorKnowledge* knowledge) const
|
||||||
{
|
{
|
||||||
return TVariantValueCast<T>::Cast(BehaviorKnowledgeSelectorAny::Get(knowledge));
|
return TVariantValueCast<T>::Cast(BehaviorKnowledgeSelectorAny::Get(knowledge));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tries to get the selected knowledge value (typed). Returns true if got value, otherwise false.
|
// Tries to get the selected knowledge value (typed). Returns true if got value, otherwise false.
|
||||||
FORCE_INLINE bool TryGet(BehaviorKnowledge* knowledge, T& value)
|
FORCE_INLINE bool TryGet(const BehaviorKnowledge* knowledge, T& value)
|
||||||
{
|
{
|
||||||
Variant variant;
|
Variant variant;
|
||||||
if (BehaviorKnowledgeSelectorAny::TryGet(knowledge, variant))
|
if (BehaviorKnowledgeSelectorAny::TryGet(knowledge, variant))
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
REGISTER_BINARY_ASSET(BehaviorTree, "FlaxEngine.BehaviorTree", false);
|
REGISTER_BINARY_ASSET(BehaviorTree, "FlaxEngine.BehaviorTree", false);
|
||||||
|
|
||||||
|
#define IS_BT_NODE(n) (n.GroupID == 19 && (n.TypeID == 1 || n.TypeID == 2 || n.TypeID == 3))
|
||||||
|
|
||||||
bool SortBehaviorTreeChildren(GraphBox* const& a, GraphBox* const& b)
|
bool SortBehaviorTreeChildren(GraphBox* const& a, GraphBox* const& b)
|
||||||
{
|
{
|
||||||
// Sort by node X coordinate on surface
|
// Sort by node X coordinate on surface
|
||||||
@@ -45,7 +47,8 @@ void BehaviorTreeGraph::Clear()
|
|||||||
|
|
||||||
bool BehaviorTreeGraph::onNodeLoaded(Node* n)
|
bool BehaviorTreeGraph::onNodeLoaded(Node* n)
|
||||||
{
|
{
|
||||||
if (n->GroupID == 19 && (n->TypeID == 1 || n->TypeID == 2 || n->TypeID == 3))
|
const Node& node = *n;
|
||||||
|
if (IS_BT_NODE(node))
|
||||||
{
|
{
|
||||||
// Create node instance object
|
// Create node instance object
|
||||||
ScriptingTypeHandle type = Scripting::FindScriptingType((StringAnsiView)n->Values[0]);
|
ScriptingTypeHandle type = Scripting::FindScriptingType((StringAnsiView)n->Values[0]);
|
||||||
@@ -151,6 +154,16 @@ BehaviorTree::BehaviorTree(const SpawnParams& params, const AssetInfo* info)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BehaviorTreeNode* BehaviorTree::GetNodeInstance(uint32 id)
|
||||||
|
{
|
||||||
|
for (const auto& node : Graph.Nodes)
|
||||||
|
{
|
||||||
|
if (node.ID == id && node.Instance && IS_BT_NODE(node))
|
||||||
|
return node.Instance;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
BytesContainer BehaviorTree::LoadSurface()
|
BytesContainer BehaviorTree::LoadSurface()
|
||||||
{
|
{
|
||||||
if (WaitForLoaded())
|
if (WaitForLoaded())
|
||||||
|
|||||||
@@ -66,6 +66,13 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
BehaviorTreeGraph Graph;
|
BehaviorTreeGraph Graph;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a specific node instance object from Behavior Tree.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The unique node identifier (Visject surface).</param>
|
||||||
|
/// <returns>The node instance or null if cannot get it.</returns>
|
||||||
|
API_FUNCTION() BehaviorTreeNode* GetNodeInstance(uint32 id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to load surface graph from the asset.
|
/// Tries to load surface graph from the asset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
API_CLASS(Abstract) class FLAXENGINE_API BehaviorTreeNode : public SerializableScriptingObject
|
API_CLASS(Abstract) class FLAXENGINE_API BehaviorTreeNode : public SerializableScriptingObject
|
||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeNode, SerializableScriptingObject);
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeNode, SerializableScriptingObject);
|
||||||
|
friend class Behavior;
|
||||||
friend class BehaviorTreeGraph;
|
friend class BehaviorTreeGraph;
|
||||||
friend class BehaviorKnowledge;
|
friend class BehaviorKnowledge;
|
||||||
friend class BehaviorTreeSubTreeNode;
|
friend class BehaviorTreeSubTreeNode;
|
||||||
@@ -76,6 +77,18 @@ public:
|
|||||||
return BehaviorUpdateResult::Success;
|
return BehaviorUpdateResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the node debug state text (multiline). Used in Editor-only to display nodes state. Can be called without valid Behavior/Knowledge/Memory to display default debug info (eg. node properties).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Behavior context data.</param>
|
||||||
|
/// <returns>Debug info text (multiline).</returns>
|
||||||
|
API_FUNCTION() virtual String GetDebugInfo(const BehaviorUpdateContext& context) const
|
||||||
|
{
|
||||||
|
return String::Empty;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Helper utility to update node with state creation/cleanup depending on node relevancy.
|
// Helper utility to update node with state creation/cleanup depending on node relevancy.
|
||||||
BehaviorUpdateResult InvokeUpdate(const BehaviorUpdateContext& context);
|
BehaviorUpdateResult InvokeUpdate(const BehaviorUpdateContext& context);
|
||||||
// Helper utility to make node relevant and init it state.
|
// Helper utility to make node relevant and init it state.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "Engine/Core/Random.h"
|
#include "Engine/Core/Random.h"
|
||||||
#include "Engine/Scripting/Scripting.h"
|
#include "Engine/Scripting/Scripting.h"
|
||||||
#if USE_CSHARP
|
#if USE_CSHARP
|
||||||
|
#include "Engine/Core/Utilities.h"
|
||||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||||
#endif
|
#endif
|
||||||
#include "Engine/Engine/Engine.h"
|
#include "Engine/Engine/Engine.h"
|
||||||
@@ -264,6 +265,28 @@ BehaviorUpdateResult BehaviorTreeDelayNode::Update(const BehaviorUpdateContext&
|
|||||||
return state->TimeLeft <= 0.0f ? BehaviorUpdateResult::Success : BehaviorUpdateResult::Running;
|
return state->TimeLeft <= 0.0f ? BehaviorUpdateResult::Success : BehaviorUpdateResult::Running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
|
String BehaviorTreeDelayNode::GetDebugInfo(const BehaviorUpdateContext& context) const
|
||||||
|
{
|
||||||
|
if (context.Memory)
|
||||||
|
{
|
||||||
|
const auto state = GetState<State>(context.Memory);
|
||||||
|
return String::Format(TEXT("Time Left: {}s"), Utilities::RoundTo2DecimalPlaces(state->TimeLeft));
|
||||||
|
}
|
||||||
|
|
||||||
|
String delay;
|
||||||
|
if (WaitTimeSelector.Path.HasChars())
|
||||||
|
delay = String(WaitTimeSelector.Path);
|
||||||
|
else
|
||||||
|
delay = StringUtils::ToString(WaitTime);
|
||||||
|
if (RandomDeviation > 0.0f)
|
||||||
|
delay += String::Format(TEXT("+/-{}"), RandomDeviation);
|
||||||
|
return String::Format(TEXT("Delay: {}s"), delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
int32 BehaviorTreeSubTreeNode::GetStateSize() const
|
int32 BehaviorTreeSubTreeNode::GetStateSize() const
|
||||||
{
|
{
|
||||||
return sizeof(State);
|
return sizeof(State);
|
||||||
@@ -481,6 +504,35 @@ BehaviorUpdateResult BehaviorTreeMoveToNode::Update(const BehaviorUpdateContext&
|
|||||||
return state->Result;
|
return state->Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
|
String BehaviorTreeMoveToNode::GetDebugInfo(const BehaviorUpdateContext& context) const
|
||||||
|
{
|
||||||
|
if (context.Memory)
|
||||||
|
{
|
||||||
|
const auto state = GetState<State>(context.Memory);
|
||||||
|
if (state->Agent)
|
||||||
|
{
|
||||||
|
const String agent = state->Agent->GetNamePath();
|
||||||
|
String goal;
|
||||||
|
const Actor* target = Target.Get(context.Knowledge);
|
||||||
|
if (target)
|
||||||
|
goal = target->GetNamePath();
|
||||||
|
else
|
||||||
|
goal = state->GoalLocation.ToString();
|
||||||
|
const Vector3 agentLocation = state->Agent->GetPosition();
|
||||||
|
const Vector3 agentLocationOnPath = agentLocation + state->AgentOffset;
|
||||||
|
float distanceLeft = state->Path.Count() > state->TargetPathIndex ? Vector3::Distance(state->Path[state->TargetPathIndex], agentLocationOnPath) : 0;
|
||||||
|
for (int32 i = state->TargetPathIndex; i < state->Path.Count(); i++)
|
||||||
|
distanceLeft += Vector3::Distance(state->Path[i - 1], state->Path[i]);
|
||||||
|
return String::Format(TEXT("Agent: '{}'\nGoal: '{}'\nDistance: {}"), agent, goal, (int32)distanceLeft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void BehaviorTreeMoveToNode::State::OnUpdate()
|
void BehaviorTreeMoveToNode::State::OnUpdate()
|
||||||
{
|
{
|
||||||
if (Result != BehaviorUpdateResult::Running)
|
if (Result != BehaviorUpdateResult::Running)
|
||||||
|
|||||||
@@ -122,6 +122,9 @@ public:
|
|||||||
int32 GetStateSize() const override;
|
int32 GetStateSize() const override;
|
||||||
void InitState(const BehaviorUpdateContext& context) override;
|
void InitState(const BehaviorUpdateContext& context) override;
|
||||||
BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override;
|
BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override;
|
||||||
|
#if USE_EDITOR
|
||||||
|
String GetDebugInfo(const BehaviorUpdateContext& context) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct State
|
struct State
|
||||||
@@ -233,6 +236,9 @@ public:
|
|||||||
void InitState(const BehaviorUpdateContext& context) override;
|
void InitState(const BehaviorUpdateContext& context) override;
|
||||||
void ReleaseState(const BehaviorUpdateContext& context) override;
|
void ReleaseState(const BehaviorUpdateContext& context) override;
|
||||||
BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override;
|
BehaviorUpdateResult Update(const BehaviorUpdateContext& context) override;
|
||||||
|
#if USE_EDITOR
|
||||||
|
String GetDebugInfo(const BehaviorUpdateContext& context) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct State
|
struct State
|
||||||
|
|||||||
Reference in New Issue
Block a user