Add concept of Goals to Behavior Knowledge

This commit is contained in:
Wojtek Figat
2023-08-29 11:41:07 +02:00
parent 81a2898d4e
commit 0defecaeb9
7 changed files with 213 additions and 48 deletions

View File

@@ -100,7 +100,20 @@ bool AccessBehaviorKnowledge(BehaviorKnowledge* knowledge, const StringAnsiView&
const StringAnsiView member(path.Get() + typeEnd + 1, path.Length() - typeEnd - 1);
return AccessVariant(knowledge->Blackboard, member, value, set);
}
// TODO: goals and sensors data access from BehaviorKnowledge via Selector
if (type == "Goal")
{
const StringAnsiView subPath(path.Get() + typeEnd + 1, path.Length() - typeEnd - 1);
const int32 goalTypeEnd = subPath.Find('/');
const StringAnsiView goalType(subPath.Get(), goalTypeEnd);
const StringAnsiView member(subPath.Get() + goalTypeEnd + 1, subPath.Length() - goalTypeEnd - 1);
for (Variant& goal : knowledge->Goals)
{
if (goalType == goal.Type.GetTypeName())
{
return AccessVariant(goal, member, value, set);
}
}
}
return false;
}
@@ -161,6 +174,9 @@ void BehaviorKnowledge::FreeMemory()
}
RelevantNodes.Clear();
Blackboard.DeleteValue();
for (Variant& goal : Goals)
goal.DeleteValue();
Goals.Resize(0);
Tree = nullptr;
}
@@ -174,6 +190,43 @@ bool BehaviorKnowledge::Set(const StringAnsiView& path, const Variant& value)
return AccessBehaviorKnowledge(this, path, const_cast<Variant&>(value), true);
}
bool BehaviorKnowledge::HasGoal(ScriptingTypeHandle type) const
{
for (int32 i = 0; i < Goals.Count(); i++)
{
const ScriptingTypeHandle goalType = Scripting::FindScriptingType(Goals[i].Type.GetTypeName());
if (goalType == type)
return true;
}
return false;
}
void BehaviorKnowledge::AddGoal(Variant&& goal)
{
int32 i = 0;
for (; i < Goals.Count(); i++)
{
if (Goals[i].Type == goal.Type)
break;
}
if (i == Goals.Count())
Goals.AddDefault();
Goals.Get()[i] = MoveTemp(goal);
}
void BehaviorKnowledge::RemoveGoal(ScriptingTypeHandle type)
{
for (int32 i = 0; i < Goals.Count(); i++)
{
const ScriptingTypeHandle goalType = Scripting::FindScriptingType(Goals[i].Type.GetTypeName());
if (goalType == type)
{
Goals.RemoveAt(i);
break;
}
}
}
bool BehaviorKnowledge::CompareValues(float a, float b, BehaviorValueComparison comparison)
{
switch (comparison)

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Core/Types/Variant.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/BitArray.h"
#include "Engine/Scripting/ScriptingObject.h"
@@ -43,6 +44,12 @@ API_CLASS() class FLAXENGINE_API BehaviorKnowledge : public ScriptingObject
/// </summary>
API_FIELD() Variant Blackboard;
/// <summary>
/// List of all active goals of the behaviour (structure or class).
/// </summary>
Array<Variant> Goals;
public:
/// <summary>
/// Initializes the knowledge for a certain tree.
/// </summary>
@@ -71,6 +78,46 @@ API_CLASS() class FLAXENGINE_API BehaviorKnowledge : public ScriptingObject
/// <returns>True if set value, otherwise false.</returns>
API_FUNCTION() bool Set(const StringAnsiView& path, const Variant& value);
public:
/// <summary>
/// Checks if knowledge has a given goal (exact type match without base class check).
/// </summary>
/// <param name="type">The goal type.</param>
/// <returns>True if has a given goal, otherwise false.</returns>
API_FUNCTION() bool HasGoal(ScriptingTypeHandle type) const;
/// <summary>
/// Checks if knowledge has a given goal (exact type match without base class check).
/// </summary>
/// <returns>True if has a given goal, otherwise false.</returns>
template<typename T>
FORCE_INLINE bool HasGoal()
{
return HasGoal(T::TypeInitializer);
}
/// <summary>
/// Adds the goal to the knowledge. If goal of that type already exists then it's value is updated.
/// </summary>
/// <param name="goal">The goal value to add/set.</param>
API_FUNCTION() void AddGoal(Variant&& goal);
/// <summary>
/// Removes the goal from the knowledge. Does nothing if goal of the given type doesn't exist in the knowledge.
/// </summary>
/// <param name="type">The goal type.</param>
API_FUNCTION() void RemoveGoal(ScriptingTypeHandle type);
/// <summary>
/// Removes the goal from the knowledge. Does nothing if goal of the given type doesn't exist in the knowledge.
/// </summary>
template<typename T>
FORCE_INLINE void RemoveGoal()
{
RemoveGoal(T::TypeInitializer);
}
public:
/// <summary>
/// Compares two values and returns the comparision result.
/// </summary>

View File

@@ -179,10 +179,9 @@ namespace FlaxEngine
/// <returns>The output value or null (if cannot read it - eg. missing goal or no blackboard entry of that name).</returns>
public T Get(BehaviorKnowledge knowledge)
{
object value = null;
if (knowledge != null)
knowledge.Get(Path, out value);
return (T)value;
if (knowledge != null && knowledge.Get(Path, out var value))
return (T)value;
return default;
}
/// <summary>

View File

@@ -11,6 +11,30 @@ using FlaxEngine.GUI;
namespace FlaxEngine
{
partial class BehaviorKnowledge
{
/// <summary>
/// Checks if knowledge has a given goal (exact type match without base class check).
/// </summary>
/// <typeparam name="T"> goal type.</typeparam>
/// <returns>True if ahs a given goal, otherwise false.</returns>
[Unmanaged]
public bool HasGoal<T>()
{
return HasGoal(typeof(T));
}
/// <summary>
/// Removes the goal from the knowledge. Does nothing if goal of the given type doesn't exist in the knowledge.
/// </summary>
/// <typeparam name="T"> goal type.</typeparam>
[Unmanaged]
public void RemoveGoal<T>()
{
RemoveGoal(typeof(T));
}
}
partial class BehaviorTreeRootNode
{
#if FLAX_EDITOR

View File

@@ -86,8 +86,12 @@ API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeRootNode : public BehaviorTre
API_FIELD(Attributes="EditorOrder(0), TypeReference(\"\", \"IsValidBlackboardType\"), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.TypeNameEditor\")")
StringAnsi BlackboardType;
// List of full typenames of the behavior goals (structure or class).
API_FIELD(Attributes="EditorOrder(10), Collection(OverrideEditorTypeName=\"FlaxEditor.CustomEditors.Editors.TypeNameEditor\")")
Array<StringAnsi> GoalTypes;
// The target amount of the behavior logic updates per second.
API_FIELD(Attributes="EditorOrder(10)")
API_FIELD(Attributes="EditorOrder(100)")
float UpdateFPS = 10.0f;
};