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; +};