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

View File

@@ -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;

View File

@@ -4,6 +4,28 @@ using System;
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
[CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.BehaviorKnowledgeSelectorEditor))]
#endif

View File

@@ -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);
}

View File

@@ -374,3 +374,20 @@ public:
// [BehaviorTreeNode]
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;
};