Add Has Goal decorator to BT

This commit is contained in:
Wojtek Figat
2023-08-29 17:31:35 +02:00
parent 57ee884397
commit b6c8a08b58
5 changed files with 83 additions and 20 deletions

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FlaxEditor.GUI; using FlaxEditor.GUI;
using FlaxEditor.GUI.Tree; using FlaxEditor.GUI.Tree;
using FlaxEditor.Scripting; using FlaxEditor.Scripting;
@@ -89,6 +90,15 @@ namespace FlaxEditor.CustomEditors.Editors
typed = new ScriptType(valueType.GenericTypeArguments[0]); 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 // Create menu with tree-like structure and search box
var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 0, true); var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 0, true);
var selected = Path; var selected = Path;
@@ -111,8 +121,11 @@ namespace FlaxEditor.CustomEditors.Editors
if (string.IsNullOrEmpty(selected)) if (string.IsNullOrEmpty(selected))
tree.Select(noneNode); tree.Select(noneNode);
if (!isGoalSelector)
{
// Blackboard // Blackboard
SetupPickerTypeItems(tree, typed, selected, "Blackboard", "Blackboard/", rootNode.BlackboardType); SetupPickerTypeItems(tree, typed, selected, "Blackboard", "Blackboard/", rootNode.BlackboardType);
}
// Goals // Goals
var goalTypes = rootNode.GoalTypes; var goalTypes = rootNode.GoalTypes;
@@ -129,7 +142,7 @@ namespace FlaxEditor.CustomEditors.Editors
var goalType = TypeUtils.GetType(goalTypeName); var goalType = TypeUtils.GetType(goalTypeName);
if (goalType == null) if (goalType == null)
continue; 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; goalTypeNode.Parent = goalsNode;
} }
goalsNode.ExpandAll(true); goalsNode.ExpandAll(true);
@@ -138,12 +151,11 @@ namespace FlaxEditor.CustomEditors.Editors
menu.Show(_label, new Float2(0, _label.Height)); 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); var type = TypeUtils.GetType(typeName);
if (type == null) if (type == null)
return null; return null;
var items = GenericEditor.GetItemsForType(type, type.IsClass, true);
var typeNode = new TreeNode var typeNode = new TreeNode
{ {
Text = text, Text = text,
@@ -155,6 +167,9 @@ namespace FlaxEditor.CustomEditors.Editors
typeNode.Tag = null; typeNode.Tag = null;
if (string.Equals(selected, (string)typeNode.Tag, StringComparison.Ordinal)) if (string.Equals(selected, (string)typeNode.Tag, StringComparison.Ordinal))
tree.Select(typeNode); tree.Select(typeNode);
if (addItems)
{
var items = GenericEditor.GetItemsForType(type, type.IsClass, true);
foreach (var item in items) foreach (var item in items)
{ {
if (typed && !typed.IsAssignableFrom(item.Info.ValueType)) if (typed && !typed.IsAssignableFrom(item.Info.ValueType))
@@ -172,6 +187,7 @@ namespace FlaxEditor.CustomEditors.Editors
// TODO: add support for nested items (eg. field from blackboard structure field) // TODO: add support for nested items (eg. field from blackboard structure field)
} }
typeNode.Expand(true); typeNode.Expand(true);
}
return typeNode; return typeNode;
} }
} }

View File

@@ -19,9 +19,11 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
if (member.IsEmpty()) if (member.IsEmpty())
{ {
// Whole blackboard value // Whole blackboard value
CHECK_RETURN(instance.Type == value.Type, false);
if (set) if (set)
{
CHECK_RETURN(instance.Type == value.Type, false);
instance = value; instance = value;
}
else else
value = instance; value = instance;
return true; return true;

View File

@@ -4,6 +4,28 @@ using System;
namespace FlaxEngine namespace FlaxEngine
{ {
/// <summary>
/// Customizes editor of <see cref="BehaviorKnowledgeSelector{T}"/> or <see cref="BehaviorKnowledgeSelectorAny"/>.
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class BehaviorKnowledgeSelectorAttribute : Attribute
{
/// <summary>
/// Changes selector editor to allow to pick only whole goals.
/// </summary>
public bool IsGoalSelector;
/// <summary>
/// Initializes a new instance of the <see cref="BehaviorKnowledgeSelectorAttribute"/> structure.
/// </summary>
/// <param name="isGoalSelector">Changes selector editor to allow to pick only whole goals.</param>
public BehaviorKnowledgeSelectorAttribute(bool isGoalSelector = false)
{
IsGoalSelector = isGoalSelector;
}
}
#if FLAX_EDITOR #if FLAX_EDITOR
[CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.BehaviorKnowledgeSelectorEditor))] [CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.BehaviorKnowledgeSelectorEditor))]
#endif #endif

View File

@@ -456,3 +456,9 @@ bool BehaviorTreeHasTagDecorator::CanUpdate(const BehaviorUpdateContext& context
result ^= Invert; result ^= Invert;
return result; 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);
}

View File

@@ -374,3 +374,20 @@ public:
// [BehaviorTreeNode] // [BehaviorTreeNode]
bool CanUpdate(const BehaviorUpdateContext& context) override; bool CanUpdate(const BehaviorUpdateContext& context) override;
}; };
/// <summary>
/// Checks if certain goal has been added to Behavior knowledge.
/// </summary>
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;
};