diff --git a/Source/Editor/CustomEditors/Editors/BehaviorKnowledgeSelectorEditor.cs b/Source/Editor/CustomEditors/Editors/BehaviorKnowledgeSelectorEditor.cs
index c8695e09a..a326adcb7 100644
--- a/Source/Editor/CustomEditors/Editors/BehaviorKnowledgeSelectorEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/BehaviorKnowledgeSelectorEditor.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using FlaxEditor.GUI;
using FlaxEditor.GUI.Tree;
using FlaxEditor.Scripting;
@@ -89,6 +90,15 @@ namespace FlaxEditor.CustomEditors.Editors
typed = new ScriptType(valueType.GenericTypeArguments[0]);
}
+ // Get customization options
+ var attributes = Values.GetAttributes();
+ var attribute = (BehaviorKnowledgeSelectorAttribute)attributes?.FirstOrDefault(x => x is BehaviorKnowledgeSelectorAttribute);
+ bool isGoalSelector = false;
+ if (attribute != null)
+ {
+ isGoalSelector = attribute.IsGoalSelector;
+ }
+
// Create menu with tree-like structure and search box
var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 0, true);
var selected = Path;
@@ -111,8 +121,11 @@ namespace FlaxEditor.CustomEditors.Editors
if (string.IsNullOrEmpty(selected))
tree.Select(noneNode);
- // Blackboard
- SetupPickerTypeItems(tree, typed, selected, "Blackboard", "Blackboard/", rootNode.BlackboardType);
+ if (!isGoalSelector)
+ {
+ // Blackboard
+ SetupPickerTypeItems(tree, typed, selected, "Blackboard", "Blackboard/", rootNode.BlackboardType);
+ }
// Goals
var goalTypes = rootNode.GoalTypes;
@@ -129,7 +142,7 @@ namespace FlaxEditor.CustomEditors.Editors
var goalType = TypeUtils.GetType(goalTypeName);
if (goalType == null)
continue;
- var goalTypeNode = SetupPickerTypeItems(tree, typed, selected, goalType.Name, "Goal/" + goalTypeName + "/", goalTypeName);
+ var goalTypeNode = SetupPickerTypeItems(tree, typed, selected, goalType.Name, "Goal/" + goalTypeName + "/", goalTypeName, !isGoalSelector);
goalTypeNode.Parent = goalsNode;
}
goalsNode.ExpandAll(true);
@@ -138,12 +151,11 @@ namespace FlaxEditor.CustomEditors.Editors
menu.Show(_label, new Float2(0, _label.Height));
}
- private TreeNode SetupPickerTypeItems(Tree tree, ScriptType typed, string selected, string text, string typePath, string typeName)
+ private TreeNode SetupPickerTypeItems(Tree tree, ScriptType typed, string selected, string text, string typePath, string typeName, bool addItems = true)
{
var type = TypeUtils.GetType(typeName);
if (type == null)
return null;
- var items = GenericEditor.GetItemsForType(type, type.IsClass, true);
var typeNode = new TreeNode
{
Text = text,
@@ -155,23 +167,27 @@ namespace FlaxEditor.CustomEditors.Editors
typeNode.Tag = null;
if (string.Equals(selected, (string)typeNode.Tag, StringComparison.Ordinal))
tree.Select(typeNode);
- foreach (var item in items)
+ if (addItems)
{
- if (typed && !typed.IsAssignableFrom(item.Info.ValueType))
- continue;
- var itemPath = typePath + item.Info.Name;
- var node = new TreeNode
+ var items = GenericEditor.GetItemsForType(type, type.IsClass, true);
+ foreach (var item in items)
{
- Text = item.DisplayName,
- TooltipText = item.TooltipText,
- Tag = itemPath,
- Parent = typeNode,
- };
- if (string.Equals(selected, itemPath, StringComparison.Ordinal))
- tree.Select(node);
- // TODO: add support for nested items (eg. field from blackboard structure field)
+ if (typed && !typed.IsAssignableFrom(item.Info.ValueType))
+ continue;
+ var itemPath = typePath + item.Info.Name;
+ var node = new TreeNode
+ {
+ Text = item.DisplayName,
+ TooltipText = item.TooltipText,
+ Tag = itemPath,
+ Parent = typeNode,
+ };
+ if (string.Equals(selected, itemPath, StringComparison.Ordinal))
+ tree.Select(node);
+ // TODO: add support for nested items (eg. field from blackboard structure field)
+ }
+ typeNode.Expand(true);
}
- typeNode.Expand(true);
return typeNode;
}
}
diff --git a/Source/Engine/AI/BehaviorKnowledge.cpp b/Source/Engine/AI/BehaviorKnowledge.cpp
index 3b611cf11..ea698704c 100644
--- a/Source/Engine/AI/BehaviorKnowledge.cpp
+++ b/Source/Engine/AI/BehaviorKnowledge.cpp
@@ -19,9 +19,11 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
if (member.IsEmpty())
{
// Whole blackboard value
- CHECK_RETURN(instance.Type == value.Type, false);
if (set)
+ {
+ CHECK_RETURN(instance.Type == value.Type, false);
instance = value;
+ }
else
value = instance;
return true;
diff --git a/Source/Engine/AI/BehaviorKnowledgeSelector.cs b/Source/Engine/AI/BehaviorKnowledgeSelector.cs
index b77bf371f..84e923ccf 100644
--- a/Source/Engine/AI/BehaviorKnowledgeSelector.cs
+++ b/Source/Engine/AI/BehaviorKnowledgeSelector.cs
@@ -4,6 +4,28 @@ using System;
namespace FlaxEngine
{
+ ///
+ /// Customizes editor of or .
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+ public sealed class BehaviorKnowledgeSelectorAttribute : Attribute
+ {
+ ///
+ /// Changes selector editor to allow to pick only whole goals.
+ ///
+ public bool IsGoalSelector;
+
+ ///
+ /// Initializes a new instance of the structure.
+ ///
+ /// Changes selector editor to allow to pick only whole goals.
+ public BehaviorKnowledgeSelectorAttribute(bool isGoalSelector = false)
+ {
+ IsGoalSelector = isGoalSelector;
+ }
+ }
+
#if FLAX_EDITOR
[CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.BehaviorKnowledgeSelectorEditor))]
#endif
diff --git a/Source/Engine/AI/BehaviorTreeNodes.cpp b/Source/Engine/AI/BehaviorTreeNodes.cpp
index 20082c9d7..0ebf5e3c6 100644
--- a/Source/Engine/AI/BehaviorTreeNodes.cpp
+++ b/Source/Engine/AI/BehaviorTreeNodes.cpp
@@ -456,3 +456,9 @@ bool BehaviorTreeHasTagDecorator::CanUpdate(const BehaviorUpdateContext& context
result ^= Invert;
return result;
}
+
+bool BehaviorTreeHasGoalDecorator::CanUpdate(const BehaviorUpdateContext& context)
+{
+ Variant value; // TODO: use HasGoal in Knowledge to optimize this (goal struct is copied by selector accessor)
+ return Goal.TryGet(context.Knowledge, value);
+}
diff --git a/Source/Engine/AI/BehaviorTreeNodes.h b/Source/Engine/AI/BehaviorTreeNodes.h
index 8ccc6a3db..a642b6911 100644
--- a/Source/Engine/AI/BehaviorTreeNodes.h
+++ b/Source/Engine/AI/BehaviorTreeNodes.h
@@ -374,3 +374,20 @@ 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;
+};