Add **Behavior Tree** asset type and editing
This commit is contained in:
66
Source/Editor/Content/Proxy/BehaviorTreeProxy.cs
Normal file
66
Source/Editor/Content/Proxy/BehaviorTreeProxy.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using FlaxEditor.Content.Thumbnails;
|
||||||
|
using FlaxEditor.Windows;
|
||||||
|
using FlaxEditor.Windows.Assets;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
|
namespace FlaxEditor.Content
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="BehaviorTree"/> asset proxy object.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="FlaxEditor.Content.BinaryAssetProxy" />
|
||||||
|
[ContentContextMenu("New/AI/Behavior Tree")]
|
||||||
|
public class BehaviorTreeProxy : BinaryAssetProxy
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Name => "Behavior Tree";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanReimport(ContentItem item)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override EditorWindow Open(Editor editor, ContentItem item)
|
||||||
|
{
|
||||||
|
return new BehaviorTreeWindow(editor, item as BinaryAssetItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Color AccentColor => Color.FromRGB(0x3256A8);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Type AssetType => typeof(BehaviorTree);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanCreate(ContentFolder targetLocation)
|
||||||
|
{
|
||||||
|
return targetLocation.CanHaveAssets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Create(string outputPath, object arg)
|
||||||
|
{
|
||||||
|
if (Editor.CreateAsset(Editor.NewAssetType.BehaviorTree, outputPath))
|
||||||
|
throw new Exception("Failed to create new asset.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context)
|
||||||
|
{
|
||||||
|
guiRoot.AddChild(new Label
|
||||||
|
{
|
||||||
|
Text = Path.GetFileNameWithoutExtension(request.Asset.Path),
|
||||||
|
Offsets = Margin.Zero,
|
||||||
|
AnchorPreset = AnchorPresets.StretchAll,
|
||||||
|
Wrapping = TextWrapping.WrapWords
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -930,6 +930,11 @@ namespace FlaxEditor
|
|||||||
/// The <see cref="FlaxEngine.Animation"/>.
|
/// The <see cref="FlaxEngine.Animation"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Animation = 11,
|
Animation = 11,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="FlaxEngine.BehaviorTree"/>.
|
||||||
|
/// </summary>
|
||||||
|
BehaviorTree = 12,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -221,6 +221,7 @@ enum class NewAssetType
|
|||||||
ParticleEmitterFunction = 9,
|
ParticleEmitterFunction = 9,
|
||||||
AnimationGraphFunction = 10,
|
AnimationGraphFunction = 10,
|
||||||
Animation = 11,
|
Animation = 11,
|
||||||
|
BehaviorTree = 12,
|
||||||
};
|
};
|
||||||
|
|
||||||
DEFINE_INTERNAL_CALL(bool) EditorInternal_CreateAsset(NewAssetType type, MString* outputPathObj)
|
DEFINE_INTERNAL_CALL(bool) EditorInternal_CreateAsset(NewAssetType type, MString* outputPathObj)
|
||||||
@@ -264,6 +265,9 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_CreateAsset(NewAssetType type, MString
|
|||||||
case NewAssetType::Animation:
|
case NewAssetType::Animation:
|
||||||
tag = AssetsImportingManager::CreateAnimationTag;
|
tag = AssetsImportingManager::CreateAnimationTag;
|
||||||
break;
|
break;
|
||||||
|
case NewAssetType::BehaviorTree:
|
||||||
|
tag = AssetsImportingManager::CreateBehaviorTreeTag;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1032,6 +1032,7 @@ namespace FlaxEditor.Modules
|
|||||||
Proxy.Add(new SkeletonMaskProxy());
|
Proxy.Add(new SkeletonMaskProxy());
|
||||||
Proxy.Add(new GameplayGlobalsProxy());
|
Proxy.Add(new GameplayGlobalsProxy());
|
||||||
Proxy.Add(new VisualScriptProxy());
|
Proxy.Add(new VisualScriptProxy());
|
||||||
|
Proxy.Add(new BehaviorTreeProxy());
|
||||||
Proxy.Add(new LocalizedStringTableProxy());
|
Proxy.Add(new LocalizedStringTableProxy());
|
||||||
Proxy.Add(new FileProxy());
|
Proxy.Add(new FileProxy());
|
||||||
Proxy.Add(new SpawnableJsonAssetProxy<PhysicalMaterial>());
|
Proxy.Add(new SpawnableJsonAssetProxy<PhysicalMaterial>());
|
||||||
|
|||||||
@@ -178,6 +178,11 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly CachedCustomAnimGraphNodesCollection AnimGraphNodes = new CachedCustomAnimGraphNodesCollection(32, new ScriptType(typeof(AnimationGraph.CustomNodeArchetypeFactoryAttribute)), IsTypeValidScriptingType, HasAssemblyValidScriptingTypes);
|
public readonly CachedCustomAnimGraphNodesCollection AnimGraphNodes = new CachedCustomAnimGraphNodesCollection(32, new ScriptType(typeof(AnimationGraph.CustomNodeArchetypeFactoryAttribute)), IsTypeValidScriptingType, HasAssemblyValidScriptingTypes);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Behavior Tree custom nodes collection.
|
||||||
|
/// </summary>
|
||||||
|
public readonly CachedTypesCollection BehaviorTreeNodes = new CachedTypesCollection(64, new ScriptType(typeof(BehaviorTreeNode)), IsTypeValidScriptingType, HasAssemblyValidScriptingTypes);
|
||||||
|
|
||||||
internal CodeEditingModule(Editor editor)
|
internal CodeEditingModule(Editor editor)
|
||||||
: base(editor)
|
: base(editor)
|
||||||
{
|
{
|
||||||
@@ -361,6 +366,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
|||||||
Scripts.ClearTypes();
|
Scripts.ClearTypes();
|
||||||
Controls.ClearTypes();
|
Controls.ClearTypes();
|
||||||
AnimGraphNodes.ClearTypes();
|
AnimGraphNodes.ClearTypes();
|
||||||
|
BehaviorTreeNodes.ClearTypes();
|
||||||
TypesCleared?.Invoke();
|
TypesCleared?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -385,7 +385,12 @@ namespace FlaxEngine.Utilities
|
|||||||
return type.IsValueType && !type.IsEnum && !type.IsPrimitive;
|
return type.IsValueType && !type.IsEnum && !type.IsPrimitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsDelegate(Type type)
|
/// <summary>
|
||||||
|
/// Checks if the input type represents a delegate.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The input type of the object to check.</param>
|
||||||
|
/// <returns>Returns true if the input type represents a delegate.</returns>
|
||||||
|
public static bool IsDelegate(this Type type)
|
||||||
{
|
{
|
||||||
return typeof(MulticastDelegate).IsAssignableFrom(type.BaseType);
|
return typeof(MulticastDelegate).IsAssignableFrom(type.BaseType);
|
||||||
}
|
}
|
||||||
|
|||||||
243
Source/Editor/Surface/Archetypes/BehaviorTree.cs
Normal file
243
Source/Editor/Surface/Archetypes/BehaviorTree.cs
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEditor.Surface.Elements;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
using FlaxEngine.Utilities;
|
||||||
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
|
namespace FlaxEditor.Surface.Archetypes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains archetypes for nodes from the Behavior Tree group.
|
||||||
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
|
public static class BehaviorTree
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Customized <see cref="SurfaceNode" /> for the Behavior Tree node.
|
||||||
|
/// </summary>
|
||||||
|
internal class Node : SurfaceNode
|
||||||
|
{
|
||||||
|
private const float ConnectionAreaMargin = 12.0f;
|
||||||
|
private const float ConnectionAreaHeight = 12.0f;
|
||||||
|
|
||||||
|
private ScriptType _type;
|
||||||
|
private InputBox _input;
|
||||||
|
private OutputBox _output;
|
||||||
|
|
||||||
|
public BehaviorTreeNode Instance;
|
||||||
|
|
||||||
|
internal static SurfaceNode Create(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||||
|
{
|
||||||
|
return new Node(id, context, nodeArch, groupArch);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Node(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||||
|
: base(id, context, nodeArch, groupArch)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetTitle(ScriptType scriptType)
|
||||||
|
{
|
||||||
|
var title = scriptType.Name;
|
||||||
|
if (title.StartsWith("BehaviorTree"))
|
||||||
|
title = title.Substring(12);
|
||||||
|
if (title.EndsWith("Node"))
|
||||||
|
title = title.Substring(0, title.Length - 4);
|
||||||
|
title = Utilities.Utils.GetPropertyNameUI(title);
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTitle()
|
||||||
|
{
|
||||||
|
string title = null;
|
||||||
|
if (Instance != null)
|
||||||
|
{
|
||||||
|
title = Instance.Name;
|
||||||
|
if (string.IsNullOrEmpty(title))
|
||||||
|
title = GetTitle(_type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var typeName = (string)Values[0];
|
||||||
|
title = "Missing Type " + typeName;
|
||||||
|
}
|
||||||
|
if (title != Title)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
ResizeAuto();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnLoaded()
|
||||||
|
{
|
||||||
|
base.OnLoaded();
|
||||||
|
|
||||||
|
// Setup boxes
|
||||||
|
_input = (InputBox)GetBox(0);
|
||||||
|
_output = (OutputBox)GetBox(1);
|
||||||
|
|
||||||
|
// Setup node type and data
|
||||||
|
var flagsRoot = NodeFlags.NoRemove | NodeFlags.NoCloseButton | NodeFlags.NoSpawnViaPaste;
|
||||||
|
var flags = Archetype.Flags & ~flagsRoot;
|
||||||
|
var typeName = (string)Values[0];
|
||||||
|
_type = TypeUtils.GetType(typeName);
|
||||||
|
if (_type != null)
|
||||||
|
{
|
||||||
|
bool isRoot = _type.Type == typeof(BehaviorTreeRootNode);
|
||||||
|
_input.Enabled = _input.Visible = !isRoot;
|
||||||
|
_output.Enabled = _output.Visible = new ScriptType(typeof(BehaviorTreeCompoundNode)).IsAssignableFrom(_type);
|
||||||
|
if (isRoot)
|
||||||
|
flags |= flagsRoot;
|
||||||
|
TooltipText = Editor.Instance.CodeDocs.GetTooltip(_type);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Load node instance from data
|
||||||
|
Instance = (BehaviorTreeNode)_type.CreateInstance();
|
||||||
|
var instanceData = (byte[])Values[1];
|
||||||
|
FlaxEngine.Json.JsonSerializer.LoadFromBytes(Instance, instanceData, Globals.EngineBuildNumber);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to load Behavior Tree node of type " + typeName);
|
||||||
|
Editor.LogWarning(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Instance = null;
|
||||||
|
}
|
||||||
|
if (Archetype.Flags != flags)
|
||||||
|
{
|
||||||
|
// Apply custom flags
|
||||||
|
Archetype = (NodeArchetype)Archetype.Clone();
|
||||||
|
Archetype.Flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void ResizeAuto()
|
||||||
|
{
|
||||||
|
if (Surface == null)
|
||||||
|
return;
|
||||||
|
var width = 0.0f;
|
||||||
|
var height = 0.0f;
|
||||||
|
var titleLabelFont = Style.Current.FontLarge;
|
||||||
|
width = Mathf.Max(width, 100.0f);
|
||||||
|
width = Mathf.Max(width, titleLabelFont.MeasureText(Title).X + 30);
|
||||||
|
if (_input != null && _input.Visible)
|
||||||
|
height += ConnectionAreaHeight;
|
||||||
|
if (_output != null && _output.Visible)
|
||||||
|
height += ConnectionAreaHeight;
|
||||||
|
Size = new Float2(width + FlaxEditor.Surface.Constants.NodeMarginX * 2, height + FlaxEditor.Surface.Constants.NodeHeaderSize + FlaxEditor.Surface.Constants.NodeFooterSize);
|
||||||
|
if (_input != null && _input.Visible)
|
||||||
|
_input.Bounds = new Rectangle(ConnectionAreaMargin, 0, Width - ConnectionAreaMargin * 2, ConnectionAreaHeight);
|
||||||
|
if (_output != null && _output.Visible)
|
||||||
|
_output.Bounds = new Rectangle(ConnectionAreaMargin, Height - ConnectionAreaHeight, Width - ConnectionAreaMargin * 2, ConnectionAreaHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void UpdateRectangles()
|
||||||
|
{
|
||||||
|
base.UpdateRectangles();
|
||||||
|
|
||||||
|
// Update boxes placement
|
||||||
|
const float footerSize = FlaxEditor.Surface.Constants.NodeFooterSize;
|
||||||
|
const float headerSize = FlaxEditor.Surface.Constants.NodeHeaderSize;
|
||||||
|
const float closeButtonMargin = FlaxEditor.Surface.Constants.NodeCloseButtonMargin;
|
||||||
|
const float closeButtonSize = FlaxEditor.Surface.Constants.NodeCloseButtonSize;
|
||||||
|
_headerRect = new Rectangle(0, 0, Width, headerSize);
|
||||||
|
if (_input != null && _input.Visible)
|
||||||
|
_headerRect.Y += ConnectionAreaHeight;
|
||||||
|
_footerRect = new Rectangle(0, _headerRect.Bottom, Width, footerSize);
|
||||||
|
_closeButtonRect = new Rectangle(Width - closeButtonSize - closeButtonMargin, _headerRect.Y + closeButtonMargin, closeButtonSize, closeButtonSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnValuesChanged()
|
||||||
|
{
|
||||||
|
base.OnValuesChanged();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Instance != null)
|
||||||
|
{
|
||||||
|
// Reload node instance from data
|
||||||
|
var instanceData = (byte[])Values[1];
|
||||||
|
if (instanceData == null || instanceData.Length == 0)
|
||||||
|
{
|
||||||
|
// Recreate instance data to default state if previous state was empty
|
||||||
|
var defaultInstance = (BehaviorTreeNode)_type.CreateInstance(); // TODO: use default instance from native ScriptingType
|
||||||
|
instanceData = FlaxEngine.Json.JsonSerializer.SaveToBytes(defaultInstance);
|
||||||
|
}
|
||||||
|
FlaxEngine.Json.JsonSerializer.LoadFromBytes(Instance, instanceData, Globals.EngineBuildNumber);
|
||||||
|
UpdateTitle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to load Behavior Tree node of type " + _type);
|
||||||
|
Editor.LogWarning(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDestroy()
|
||||||
|
{
|
||||||
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
|
_type = ScriptType.Null;
|
||||||
|
Object.Destroy(ref Instance);
|
||||||
|
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The nodes for that group.
|
||||||
|
/// </summary>
|
||||||
|
public static NodeArchetype[] Nodes =
|
||||||
|
{
|
||||||
|
new NodeArchetype
|
||||||
|
{
|
||||||
|
TypeID = 1,
|
||||||
|
Create = Node.Create,
|
||||||
|
Flags = NodeFlags.BehaviorTreeGraph | NodeFlags.NoSpawnViaGUI,
|
||||||
|
DefaultValues = new object[]
|
||||||
|
{
|
||||||
|
string.Empty, // Type Name
|
||||||
|
Utils.GetEmptyArray<byte>(), // Instance Data
|
||||||
|
},
|
||||||
|
Size = new Float2(100, 0),
|
||||||
|
Elements = new[]
|
||||||
|
{
|
||||||
|
NodeElementArchetype.Factory.Input(0, string.Empty, true, ScriptType.Void, 0),
|
||||||
|
NodeElementArchetype.Factory.Output(0, string.Empty, ScriptType.Void, 1),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new NodeArchetype
|
||||||
|
{
|
||||||
|
TypeID = 2,
|
||||||
|
Create = Node.Create,
|
||||||
|
Flags = NodeFlags.BehaviorTreeGraph | NodeFlags.NoSpawnViaGUI,
|
||||||
|
DefaultValues = new object[]
|
||||||
|
{
|
||||||
|
typeof(BehaviorTreeRootNode).FullName, // Root node
|
||||||
|
Utils.GetEmptyArray<byte>(), // Instance Data
|
||||||
|
},
|
||||||
|
Size = new Float2(100, 0),
|
||||||
|
Elements = new[]
|
||||||
|
{
|
||||||
|
NodeElementArchetype.Factory.Input(0, string.Empty, true, ScriptType.Void, 0),
|
||||||
|
NodeElementArchetype.Factory.Output(0, string.Empty, ScriptType.Void, 1),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
158
Source/Editor/Surface/BehaviorTreeSurface.cs
Normal file
158
Source/Editor/Surface/BehaviorTreeSurface.cs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEditor.Surface.ContextMenu;
|
||||||
|
using FlaxEditor.Surface.Elements;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
|
namespace FlaxEditor.Surface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Visject Surface implementation for the Behavior Tree graphs.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="VisjectSurface" />
|
||||||
|
[HideInEditor]
|
||||||
|
public class BehaviorTreeSurface : VisjectSurface
|
||||||
|
{
|
||||||
|
private static NodesCache _nodesCache = new NodesCache(IterateNodesCache);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public BehaviorTreeSurface(IVisjectSurfaceOwner owner, Action onSave, FlaxEditor.Undo undo)
|
||||||
|
: base(owner, onSave, undo, CreateStyle())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SurfaceStyle CreateStyle()
|
||||||
|
{
|
||||||
|
var editor = Editor.Instance;
|
||||||
|
var style = SurfaceStyle.CreateStyleHandler(editor);
|
||||||
|
style.DrawBox = DrawBox;
|
||||||
|
style.DrawConnection = DrawConnection;
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawBox(Box box)
|
||||||
|
{
|
||||||
|
var rect = new Rectangle(Float2.Zero, box.Size);
|
||||||
|
const float minBoxSize = 5.0f;
|
||||||
|
if (rect.Size.LengthSquared < minBoxSize * minBoxSize)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var style = FlaxEngine.GUI.Style.Current;
|
||||||
|
var color = style.LightBackground;
|
||||||
|
if (box.IsMouseOver)
|
||||||
|
color *= 1.2f;
|
||||||
|
Render2D.FillRectangle(rect, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawConnection(Float2 start, Float2 end, Color color, float thickness)
|
||||||
|
{
|
||||||
|
Archetypes.Animation.StateMachineStateBase.DrawConnection(ref start, ref end, ref color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnActiveContextMenuVisibleChanged(Control activeCM)
|
||||||
|
{
|
||||||
|
_nodesCache.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void IterateNodesCache(ScriptType scriptType, Dictionary<KeyValuePair<string, ushort>, GroupArchetype> cache, int version)
|
||||||
|
{
|
||||||
|
// Filter by BT node types only
|
||||||
|
if (!new ScriptType(typeof(BehaviorTreeNode)).IsAssignableFrom(scriptType))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Skip in-built types
|
||||||
|
if (scriptType == typeof(BehaviorTreeNode) ||
|
||||||
|
scriptType == typeof(BehaviorTreeCompoundNode) ||
|
||||||
|
scriptType == typeof(BehaviorTreeRootNode))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Create group archetype
|
||||||
|
var groupKey = new KeyValuePair<string, ushort>("Behavior Tree", 19);
|
||||||
|
if (!cache.TryGetValue(groupKey, out var group))
|
||||||
|
{
|
||||||
|
group = new GroupArchetype
|
||||||
|
{
|
||||||
|
GroupID = groupKey.Value,
|
||||||
|
Name = groupKey.Key,
|
||||||
|
Color = new Color(70, 220, 181),
|
||||||
|
Tag = version,
|
||||||
|
Archetypes = new List<NodeArchetype>(),
|
||||||
|
};
|
||||||
|
cache.Add(groupKey, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create node archetype
|
||||||
|
var node = (NodeArchetype)Archetypes.BehaviorTree.Nodes[0].Clone();
|
||||||
|
node.DefaultValues[0] = scriptType.TypeName;
|
||||||
|
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||||
|
node.Title = Archetypes.BehaviorTree.Node.GetTitle(scriptType);
|
||||||
|
node.Description = Editor.Instance.CodeDocs.GetTooltip(scriptType);
|
||||||
|
((IList<NodeArchetype>)group.Archetypes).Add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, Box startBox)
|
||||||
|
{
|
||||||
|
activeCM.ShowExpanded = true;
|
||||||
|
_nodesCache.Get(activeCM);
|
||||||
|
|
||||||
|
base.OnShowPrimaryMenu(activeCM, location, startBox);
|
||||||
|
|
||||||
|
activeCM.VisibleChanged += OnActiveContextMenuVisibleChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string GetTypeName(ScriptType type)
|
||||||
|
{
|
||||||
|
if (type == ScriptType.Void)
|
||||||
|
return string.Empty; // Remove `Void` tooltip from connection areas
|
||||||
|
return base.GetTypeName(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Load()
|
||||||
|
{
|
||||||
|
if (base.Load())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Ensure to have Root node created (UI blocks spawning of it)
|
||||||
|
if (RootContext.FindNode(19, 2) == null)
|
||||||
|
{
|
||||||
|
RootContext.SpawnNode(19, 2, Float2.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanUseNodeType(GroupArchetype groupArchetype, NodeArchetype nodeArchetype)
|
||||||
|
{
|
||||||
|
// Comments
|
||||||
|
if (groupArchetype.GroupID == 7 && nodeArchetype.TypeID == 11)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Single root node
|
||||||
|
if (groupArchetype.GroupID == 19 && nodeArchetype.TypeID == 2 && RootContext.FindNode(19, 2) != null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Behavior Tree nodes only
|
||||||
|
return (nodeArchetype.Flags & NodeFlags.BehaviorTreeGraph) != 0 &&
|
||||||
|
groupArchetype.GroupID == 19 &&
|
||||||
|
base.CanUseNodeType(groupArchetype, nodeArchetype);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDestroy()
|
||||||
|
{
|
||||||
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
|
_nodesCache.Wait();
|
||||||
|
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -175,6 +175,13 @@ namespace FlaxEditor.Surface
|
|||||||
Color = new Color(110, 180, 81),
|
Color = new Color(110, 180, 81),
|
||||||
Archetypes = Archetypes.Collections.Nodes
|
Archetypes = Archetypes.Collections.Nodes
|
||||||
},
|
},
|
||||||
|
new GroupArchetype
|
||||||
|
{
|
||||||
|
GroupID = 19,
|
||||||
|
Name = "Behavior Tree",
|
||||||
|
Color = new Color(70, 220, 181),
|
||||||
|
Archetypes = Archetypes.BehaviorTree.Nodes
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -68,9 +68,14 @@ namespace FlaxEditor.Surface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
NoSpawnViaPaste = 512,
|
NoSpawnViaPaste = 512,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Node can be used in the Behavior Tree graphs.
|
||||||
|
/// </summary>
|
||||||
|
BehaviorTreeGraph = 1024,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Node can be used in the all visual graphs.
|
/// Node can be used in the all visual graphs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AllGraphs = MaterialGraph | ParticleEmitterGraph | AnimGraph | VisualScriptGraph,
|
AllGraphs = MaterialGraph | ParticleEmitterGraph | AnimGraph | VisualScriptGraph | BehaviorTreeGraph,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
433
Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
Normal file
433
Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Xml;
|
||||||
|
using FlaxEditor.Content;
|
||||||
|
using FlaxEditor.CustomEditors;
|
||||||
|
using FlaxEditor.GUI;
|
||||||
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEditor.Surface;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
using FlaxEngine.Utilities;
|
||||||
|
|
||||||
|
namespace FlaxEditor.Windows.Assets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Behavior Tree window allows to view and edit <see cref="BehaviorTree"/> asset.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="BehaviorTree" />
|
||||||
|
/// <seealso cref="BehaviorTreeSurface" />
|
||||||
|
public sealed class BehaviorTreeWindow : AssetEditorWindowBase<BehaviorTree>, IVisjectSurfaceWindow
|
||||||
|
{
|
||||||
|
private readonly SplitPanel _split1;
|
||||||
|
private readonly SplitPanel _split2;
|
||||||
|
private CustomEditorPresenter _nodePropertiesEditor;
|
||||||
|
private CustomEditorPresenter _knowledgePropertiesEditor;
|
||||||
|
private BehaviorTreeSurface _surface;
|
||||||
|
private Undo _undo;
|
||||||
|
private readonly ToolStripButton _saveButton;
|
||||||
|
private readonly ToolStripButton _undoButton;
|
||||||
|
private readonly ToolStripButton _redoButton;
|
||||||
|
private bool _showWholeGraphOnLoad = true;
|
||||||
|
private bool _isWaitingForSurfaceLoad;
|
||||||
|
private bool _canEdit = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Visject Surface.
|
||||||
|
/// </summary>
|
||||||
|
public BehaviorTreeSurface Surface => _surface;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the undo history context for this window.
|
||||||
|
/// </summary>
|
||||||
|
public Undo Undo => _undo;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public BehaviorTreeWindow(Editor editor, BinaryAssetItem item)
|
||||||
|
: base(editor, item)
|
||||||
|
{
|
||||||
|
var isPlayMode = Editor.IsPlayMode;
|
||||||
|
|
||||||
|
// Undo
|
||||||
|
_undo = new Undo();
|
||||||
|
_undo.UndoDone += OnUndoRedo;
|
||||||
|
_undo.RedoDone += OnUndoRedo;
|
||||||
|
_undo.ActionDone += OnUndoRedo;
|
||||||
|
|
||||||
|
// Split Panels
|
||||||
|
_split1 = new SplitPanel(Orientation.Horizontal, ScrollBars.None, ScrollBars.None)
|
||||||
|
{
|
||||||
|
AnchorPreset = AnchorPresets.StretchAll,
|
||||||
|
Offsets = new Margin(0, 0, _toolstrip.Bottom, 0),
|
||||||
|
SplitterValue = 0.7f,
|
||||||
|
Parent = this
|
||||||
|
};
|
||||||
|
_split2 = new SplitPanel(Orientation.Vertical, ScrollBars.Vertical, ScrollBars.Vertical)
|
||||||
|
{
|
||||||
|
AnchorPreset = AnchorPresets.StretchAll,
|
||||||
|
Offsets = Margin.Zero,
|
||||||
|
SplitterValue = 0.5f,
|
||||||
|
Parent = _split1.Panel2
|
||||||
|
};
|
||||||
|
|
||||||
|
// Surface
|
||||||
|
_surface = new BehaviorTreeSurface(this, Save, _undo)
|
||||||
|
{
|
||||||
|
Parent = _split1.Panel1,
|
||||||
|
Enabled = false
|
||||||
|
};
|
||||||
|
_surface.SelectionChanged += OnNodeSelectionChanged;
|
||||||
|
|
||||||
|
// Properties editors
|
||||||
|
_nodePropertiesEditor = new CustomEditorPresenter(null); // Surface handles undo for nodes editing
|
||||||
|
_nodePropertiesEditor.Features = FeatureFlags.UseDefault;
|
||||||
|
_nodePropertiesEditor.Panel.Parent = _split2.Panel1;
|
||||||
|
_nodePropertiesEditor.Modified += OnNodePropertyEdited;
|
||||||
|
_knowledgePropertiesEditor = new CustomEditorPresenter(null, "No blackboard type assigned"); // No undo for knowledge editing
|
||||||
|
_knowledgePropertiesEditor.Features = FeatureFlags.None;
|
||||||
|
_knowledgePropertiesEditor.Panel.Parent = _split2.Panel2;
|
||||||
|
|
||||||
|
// Toolstrip
|
||||||
|
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
||||||
|
_toolstrip.AddSeparator();
|
||||||
|
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
||||||
|
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
||||||
|
_toolstrip.AddSeparator();
|
||||||
|
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip("Open content search tool (Ctrl+F)");
|
||||||
|
_toolstrip.AddButton(editor.Icons.CenterView64, _surface.ShowWholeGraph).LinkTooltip("Show whole graph");
|
||||||
|
|
||||||
|
// Setup input actions
|
||||||
|
InputActions.Add(options => options.Undo, _undo.PerformUndo);
|
||||||
|
InputActions.Add(options => options.Redo, _undo.PerformRedo);
|
||||||
|
InputActions.Add(options => options.Search, Editor.ContentFinding.ShowSearch);
|
||||||
|
|
||||||
|
SetCanEdit(!isPlayMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUndoRedo(IUndoAction action)
|
||||||
|
{
|
||||||
|
MarkAsEdited();
|
||||||
|
UpdateToolstrip();
|
||||||
|
_nodePropertiesEditor.BuildLayoutOnUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNodeSelectionChanged()
|
||||||
|
{
|
||||||
|
// Select node instances to view/edit
|
||||||
|
var selection = new List<object>();
|
||||||
|
var nodes = _surface.Nodes;
|
||||||
|
if (nodes != null)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < nodes.Count; i++)
|
||||||
|
{
|
||||||
|
if (nodes[i] is Surface.Archetypes.BehaviorTree.Node node && node.IsSelected && node.Instance)
|
||||||
|
selection.Add(node.Instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_nodePropertiesEditor.Select(selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNodePropertyEdited()
|
||||||
|
{
|
||||||
|
_surface.MarkAsEdited();
|
||||||
|
var nodes = _surface.Nodes;
|
||||||
|
for (var i = 0; i < _nodePropertiesEditor.Selection.Count; i++)
|
||||||
|
{
|
||||||
|
if (_nodePropertiesEditor.Selection[i] is BehaviorTreeNode instance)
|
||||||
|
{
|
||||||
|
// Sync instance data with surface node value storage
|
||||||
|
for (var j = 0; j < nodes.Count; j++)
|
||||||
|
{
|
||||||
|
if (nodes[j] is Surface.Archetypes.BehaviorTree.Node node && node.Instance == instance)
|
||||||
|
{
|
||||||
|
node.SetValue(1, FlaxEngine.Json.JsonSerializer.SaveToBytes(instance));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateKnowledge()
|
||||||
|
{
|
||||||
|
var rootNode = _surface.FindNode(19, 2) as Surface.Archetypes.BehaviorTree.Node;
|
||||||
|
if (rootNode != null)
|
||||||
|
rootNode.ValuesChanged += UpdateKnowledge;
|
||||||
|
var rootInstance = rootNode?.Instance as BehaviorTreeRootNode;
|
||||||
|
var blackboardType = TypeUtils.GetType(rootInstance?.BlackboardType);
|
||||||
|
if (blackboardType)
|
||||||
|
{
|
||||||
|
var blackboardInstance = blackboardType.CreateInstance();
|
||||||
|
_knowledgePropertiesEditor.Select(blackboardInstance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_knowledgePropertiesEditor.Deselect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Save()
|
||||||
|
{
|
||||||
|
// Early check
|
||||||
|
if (!IsEdited || _asset == null || _isWaitingForSurfaceLoad)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check if surface has been edited
|
||||||
|
if (_surface.IsEdited)
|
||||||
|
{
|
||||||
|
if (SaveSurface())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearEditedFlag();
|
||||||
|
OnSurfaceEditedChanged();
|
||||||
|
_item.RefreshThumbnail();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void UpdateToolstrip()
|
||||||
|
{
|
||||||
|
_saveButton.Enabled = _canEdit && IsEdited;
|
||||||
|
_undoButton.Enabled = _canEdit && _undo.CanUndo;
|
||||||
|
_redoButton.Enabled = _canEdit && _undo.CanRedo;
|
||||||
|
|
||||||
|
base.UpdateToolstrip();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnAssetLinked()
|
||||||
|
{
|
||||||
|
_isWaitingForSurfaceLoad = true;
|
||||||
|
|
||||||
|
base.OnAssetLinked();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Focuses the node.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="node">The node.</param>
|
||||||
|
public void ShowNode(SurfaceNode node)
|
||||||
|
{
|
||||||
|
SelectTab();
|
||||||
|
RootWindow.Focus();
|
||||||
|
Surface.Focus();
|
||||||
|
Surface.FocusNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Asset SurfaceAsset => Asset;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string SurfaceName => "Behavior Tree";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public byte[] SurfaceData
|
||||||
|
{
|
||||||
|
get => _asset.LoadSurface();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// Save data to the asset
|
||||||
|
if (_asset.SaveSurface(value))
|
||||||
|
{
|
||||||
|
_surface.MarkAsEdited();
|
||||||
|
Editor.LogError("Failed to save surface data");
|
||||||
|
}
|
||||||
|
_asset.Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurfaceContext ParentContext => null;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnContextCreated(VisjectSurfaceContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnSurfaceEditedChanged()
|
||||||
|
{
|
||||||
|
if (_surface.IsEdited)
|
||||||
|
MarkAsEdited();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnSurfaceGraphEdited()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnSurfaceClose()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void UnlinkItem()
|
||||||
|
{
|
||||||
|
_isWaitingForSurfaceLoad = false;
|
||||||
|
|
||||||
|
base.UnlinkItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool LoadSurface()
|
||||||
|
{
|
||||||
|
if (_surface.Load())
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to load Behavior Tree surface.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SaveSurface()
|
||||||
|
{
|
||||||
|
_surface.Save();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetCanEdit(bool canEdit)
|
||||||
|
{
|
||||||
|
if (_canEdit == canEdit)
|
||||||
|
return;
|
||||||
|
_canEdit = canEdit;
|
||||||
|
_undo.Enabled = canEdit;
|
||||||
|
_surface.CanEdit = canEdit;
|
||||||
|
_nodePropertiesEditor.ReadOnly = !_canEdit;
|
||||||
|
_knowledgePropertiesEditor.ReadOnly = true;
|
||||||
|
UpdateToolstrip();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnPlayBegin()
|
||||||
|
{
|
||||||
|
base.OnPlayBegin();
|
||||||
|
|
||||||
|
SetCanEdit(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnPlayEnd()
|
||||||
|
{
|
||||||
|
SetCanEdit(true);
|
||||||
|
|
||||||
|
base.OnPlayEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Update(float deltaTime)
|
||||||
|
{
|
||||||
|
base.Update(deltaTime);
|
||||||
|
|
||||||
|
if (_isWaitingForSurfaceLoad && _asset.IsLoaded)
|
||||||
|
{
|
||||||
|
_isWaitingForSurfaceLoad = false;
|
||||||
|
|
||||||
|
if (LoadSurface())
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
_undo.Clear();
|
||||||
|
_surface.Enabled = true;
|
||||||
|
_nodePropertiesEditor.BuildLayout();
|
||||||
|
_knowledgePropertiesEditor.BuildLayout();
|
||||||
|
ClearEditedFlag();
|
||||||
|
if (_showWholeGraphOnLoad)
|
||||||
|
{
|
||||||
|
_showWholeGraphOnLoad = false;
|
||||||
|
_surface.ShowWholeGraph();
|
||||||
|
}
|
||||||
|
SurfaceLoaded?.Invoke();
|
||||||
|
_knowledgePropertiesEditor.ReadOnly = true;
|
||||||
|
UpdateKnowledge();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool UseLayoutData => true;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnLayoutSerialize(XmlWriter writer)
|
||||||
|
{
|
||||||
|
LayoutSerializeSplitter(writer, "Split1", _split1);
|
||||||
|
LayoutSerializeSplitter(writer, "Split2", _split1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnLayoutDeserialize(XmlElement node)
|
||||||
|
{
|
||||||
|
LayoutDeserializeSplitter(node, "Split1", _split1);
|
||||||
|
LayoutDeserializeSplitter(node, "Split2", _split2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnLayoutDeserialize()
|
||||||
|
{
|
||||||
|
_split1.SplitterValue = 0.7f;
|
||||||
|
_split2.SplitterValue = 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDestroy()
|
||||||
|
{
|
||||||
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
|
_undo.Enabled = false;
|
||||||
|
_nodePropertiesEditor.Deselect();
|
||||||
|
_knowledgePropertiesEditor.Deselect();
|
||||||
|
_undo.Clear();
|
||||||
|
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<ScriptType> NewParameterTypes => Editor.CodeEditing.VisualScriptPropertyTypes.Get();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event Action SurfaceLoaded;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnParamRenameUndo()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnParamEditAttributesUndo()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnParamAddUndo()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnParamRemoveUndo()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object GetParameter(int index)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void SetParameter(int index, object value)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Asset VisjectAsset => Asset;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public VisjectSurface VisjectSurface => _surface;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Source/Engine/AI/BehaviorKnowledge.h
Normal file
18
Source/Engine/AI/BehaviorKnowledge.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Engine/Scripting/ScriptingObject.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Behavior logic component knowledge data container. Contains blackboard values, sensors data and goals storage for Behavior Tree execution.
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS() class FLAXENGINE_API BehaviorKnowledge : public ScriptingObject
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorKnowledge, ScriptingObject);
|
||||||
|
|
||||||
|
// TODO: blackboard
|
||||||
|
// TODO: sensors data
|
||||||
|
// TODO: goals
|
||||||
|
// TODO: GetGoal/HasGoal
|
||||||
|
};
|
||||||
161
Source/Engine/AI/BehaviorTree.cpp
Normal file
161
Source/Engine/AI/BehaviorTree.cpp
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#include "BehaviorTree.h"
|
||||||
|
#include "BehaviorTreeNode.h"
|
||||||
|
#include "BehaviorTreeNodes.h"
|
||||||
|
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||||
|
#include "Engine/Scripting/Scripting.h"
|
||||||
|
#include "Engine/Serialization/JsonSerializer.h"
|
||||||
|
#include "Engine/Serialization/MemoryReadStream.h"
|
||||||
|
#include "Engine/Threading/Threading.h"
|
||||||
|
#include "FlaxEngine.Gen.h"
|
||||||
|
|
||||||
|
REGISTER_BINARY_ASSET(BehaviorTree, "FlaxEngine.BehaviorTree", false);
|
||||||
|
|
||||||
|
BehaviorTreeGraphNode::~BehaviorTreeGraphNode()
|
||||||
|
{
|
||||||
|
SAFE_DELETE(Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BehaviorTreeGraph::Load(ReadStream* stream, bool loadMeta)
|
||||||
|
{
|
||||||
|
if (VisjectGraph<BehaviorTreeGraphNode>::Load(stream, loadMeta))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Build node instances hierarchy
|
||||||
|
for (Node& node : Nodes)
|
||||||
|
{
|
||||||
|
if (auto* nodeCompound = ScriptingObject::Cast<BehaviorTreeCompoundNode>(node.Instance))
|
||||||
|
{
|
||||||
|
for (const GraphBox* childBox : node.Boxes[1].Connections)
|
||||||
|
{
|
||||||
|
const Node* child = childBox ? (Node*)childBox->Parent : nullptr;
|
||||||
|
if (child && child->Instance)
|
||||||
|
{
|
||||||
|
nodeCompound->Children.Add(child->Instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BehaviorTreeGraph::Clear()
|
||||||
|
{
|
||||||
|
VisjectGraph<BehaviorTreeGraphNode>::Clear();
|
||||||
|
|
||||||
|
Root = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BehaviorTreeGraph::onNodeLoaded(Node* n)
|
||||||
|
{
|
||||||
|
if (n->GroupID == 19 && (n->TypeID == 1 || n->TypeID == 2))
|
||||||
|
{
|
||||||
|
// Create node instance object
|
||||||
|
ScriptingTypeHandle type = Scripting::FindScriptingType((StringAnsiView)n->Values[0]);
|
||||||
|
if (!type)
|
||||||
|
type = Scripting::FindScriptingType(StringAnsi((StringView)n->Values[0]));
|
||||||
|
if (type)
|
||||||
|
{
|
||||||
|
n->Instance = (BehaviorTreeNode*)Scripting::NewObject(type);
|
||||||
|
const Variant& data = n->Values[1];
|
||||||
|
if (data.Type == VariantType::Blob)
|
||||||
|
JsonSerializer::LoadFromBytes(n->Instance, Span<byte>((byte*)data.AsBlob.Data, data.AsBlob.Length), FLAXENGINE_VERSION_BUILD);
|
||||||
|
|
||||||
|
// Find root node
|
||||||
|
if (!Root && n->Instance && BehaviorTreeRootNode::TypeInitializer == type)
|
||||||
|
Root = (BehaviorTreeRootNode*)n->Instance;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const String name = n->Values[0].ToString();
|
||||||
|
if (name.HasChars())
|
||||||
|
LOG(Error, "Missing type '{0}'", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return VisjectGraph<BehaviorTreeGraphNode>::onNodeLoaded(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
BehaviorTree::BehaviorTree(const SpawnParams& params, const AssetInfo* info)
|
||||||
|
: BinaryAsset(params, info)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BytesContainer BehaviorTree::LoadSurface()
|
||||||
|
{
|
||||||
|
if (WaitForLoaded())
|
||||||
|
return BytesContainer();
|
||||||
|
ScopeLock lock(Locker);
|
||||||
|
if (!LoadChunks(GET_CHUNK_FLAG(0)))
|
||||||
|
{
|
||||||
|
const auto data = GetChunk(0);
|
||||||
|
BytesContainer result;
|
||||||
|
result.Copy(data->Data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
LOG(Warning, "\'{0}\' surface data is missing.", ToString());
|
||||||
|
return BytesContainer();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
|
bool BehaviorTree::SaveSurface(const BytesContainer& data)
|
||||||
|
{
|
||||||
|
// Wait for asset to be loaded or don't if last load failed
|
||||||
|
if (LastLoadFailed())
|
||||||
|
{
|
||||||
|
LOG(Warning, "Saving asset that failed to load.");
|
||||||
|
}
|
||||||
|
else if (WaitForLoaded())
|
||||||
|
{
|
||||||
|
LOG(Error, "Asset loading failed. Cannot save it.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeLock lock(Locker);
|
||||||
|
|
||||||
|
// Set Visject Surface data
|
||||||
|
GetOrCreateChunk(0)->Data.Copy(data);
|
||||||
|
|
||||||
|
// Save
|
||||||
|
AssetInitData assetData;
|
||||||
|
assetData.SerializedVersion = 1;
|
||||||
|
if (SaveAsset(assetData))
|
||||||
|
{
|
||||||
|
LOG(Error, "Cannot save \'{0}\'", ToString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Asset::LoadResult BehaviorTree::load()
|
||||||
|
{
|
||||||
|
// Load graph
|
||||||
|
const auto surfaceChunk = GetChunk(0);
|
||||||
|
if (surfaceChunk == nullptr)
|
||||||
|
return LoadResult::MissingDataChunk;
|
||||||
|
MemoryReadStream surfaceStream(surfaceChunk->Get(), surfaceChunk->Size());
|
||||||
|
if (Graph.Load(&surfaceStream, true))
|
||||||
|
{
|
||||||
|
LOG(Warning, "Failed to load graph \'{0}\'", ToString());
|
||||||
|
return LoadResult::Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadResult::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BehaviorTree::unload(bool isReloading)
|
||||||
|
{
|
||||||
|
// Clear resources
|
||||||
|
Graph.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetChunksFlag BehaviorTree::getChunksToPreload() const
|
||||||
|
{
|
||||||
|
return GET_CHUNK_FLAG(0);
|
||||||
|
}
|
||||||
42
Source/Engine/AI/BehaviorTree.cs
Normal file
42
Source/Engine/AI/BehaviorTree.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#if FLAX_EDITOR
|
||||||
|
using System;
|
||||||
|
using FlaxEngine.Utilities;
|
||||||
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace FlaxEngine
|
||||||
|
{
|
||||||
|
partial class BehaviorTreeRootNode
|
||||||
|
{
|
||||||
|
#if FLAX_EDITOR
|
||||||
|
private static bool IsValidBlackboardType(ScriptType type)
|
||||||
|
{
|
||||||
|
if (ScriptType.FlaxObject.IsAssignableFrom(type))
|
||||||
|
return false;
|
||||||
|
if (type.Type != null)
|
||||||
|
{
|
||||||
|
if (type.Type.IsDelegate())
|
||||||
|
return false;
|
||||||
|
if (typeof(Control).IsAssignableFrom(type.Type))
|
||||||
|
return false;
|
||||||
|
if (typeof(Attribute).IsAssignableFrom(type.Type))
|
||||||
|
return false;
|
||||||
|
if (type.Type.FullName.StartsWith("FlaxEditor.", StringComparison.Ordinal))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !type.IsGenericType &&
|
||||||
|
!type.IsInterface &&
|
||||||
|
!type.IsStatic &&
|
||||||
|
!type.IsAbstract &&
|
||||||
|
!type.IsArray &&
|
||||||
|
!type.IsVoid &&
|
||||||
|
(type.IsClass || type.IsStructure) &&
|
||||||
|
type.IsPublic &&
|
||||||
|
type.CanCreateInstance;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
91
Source/Engine/AI/BehaviorTree.h
Normal file
91
Source/Engine/AI/BehaviorTree.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Engine/Content/BinaryAsset.h"
|
||||||
|
#include "Engine/Visject/VisjectGraph.h"
|
||||||
|
|
||||||
|
class BehaviorTreeNode;
|
||||||
|
class BehaviorTreeRootNode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Behavior Tree graph node.
|
||||||
|
/// </summary>
|
||||||
|
class BehaviorTreeGraphNode : public VisjectGraphNode<>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Instance of the graph node.
|
||||||
|
BehaviorTreeNode* Instance = nullptr;
|
||||||
|
|
||||||
|
~BehaviorTreeGraphNode();
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Behavior Tree graph.
|
||||||
|
/// </summary>
|
||||||
|
class BehaviorTreeGraph : public VisjectGraph<BehaviorTreeGraphNode>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Instance of the graph root node.
|
||||||
|
BehaviorTreeRootNode* Root = nullptr;
|
||||||
|
|
||||||
|
// [VisjectGraph]
|
||||||
|
bool Load(ReadStream* stream, bool loadMeta) override;
|
||||||
|
void Clear() override;
|
||||||
|
bool onNodeLoaded(Node* n) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Behavior Tree graph executor runtime.
|
||||||
|
/// </summary>
|
||||||
|
class BehaviorTreeExecutor : public VisjectExecutor
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Behavior Tree asset with AI logic graph.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="BinaryAsset" />
|
||||||
|
API_CLASS(NoSpawn, Sealed) class FLAXENGINE_API BehaviorTree : public BinaryAsset
|
||||||
|
{
|
||||||
|
DECLARE_BINARY_ASSET_HEADER(BehaviorTree, 1);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// The Behavior Tree graph.
|
||||||
|
/// </summary>
|
||||||
|
BehaviorTreeGraph Graph;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to load surface graph from the asset.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The surface data or empty if failed to load it.</returns>
|
||||||
|
API_FUNCTION() BytesContainer LoadSurface();
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the graph surface (save new one, discard cached data, reload asset).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Stream with graph data.</param>
|
||||||
|
/// <returns>True if cannot save it, otherwise false.</returns>
|
||||||
|
API_FUNCTION() bool SaveSurface(const BytesContainer& data);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
// [BinaryAsset]
|
||||||
|
#if USE_EDITOR
|
||||||
|
void GetReferences(Array<Guid>& output) const override
|
||||||
|
{
|
||||||
|
// Base
|
||||||
|
BinaryAsset::GetReferences(output);
|
||||||
|
|
||||||
|
Graph.GetReferences(output);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// [BinaryAsset]
|
||||||
|
LoadResult load() override;
|
||||||
|
void unload(bool isReloading) override;
|
||||||
|
AssetChunksFlag getChunksToPreload() const override;
|
||||||
|
};
|
||||||
29
Source/Engine/AI/BehaviorTreeNode.h
Normal file
29
Source/Engine/AI/BehaviorTreeNode.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Engine/Scripting/SerializableScriptingObject.h"
|
||||||
|
#include "BehaviorTypes.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for Behavior Tree nodes.
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS(Abstract) class FLAXENGINE_API BehaviorTreeNode : public SerializableScriptingObject
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeNode, SerializableScriptingObject);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Node user name (eg. Follow Enemy, or Pick up Weapon).
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD() String Name;
|
||||||
|
|
||||||
|
// TODO: decorators/conditionals
|
||||||
|
// TODO: init methods
|
||||||
|
// TODO: instance data ctor/dtor
|
||||||
|
// TODO: start/stop methods
|
||||||
|
// TODO: update method
|
||||||
|
|
||||||
|
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||||
|
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||||
|
};
|
||||||
20
Source/Engine/AI/BehaviorTreeNodes.cpp
Normal file
20
Source/Engine/AI/BehaviorTreeNodes.cpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#include "BehaviorTreeNodes.h"
|
||||||
|
#include "Engine/Serialization/Serialization.h"
|
||||||
|
|
||||||
|
void BehaviorTreeNode::Serialize(SerializeStream& stream, const void* otherObj)
|
||||||
|
{
|
||||||
|
SerializableScriptingObject::Serialize(stream, otherObj);
|
||||||
|
|
||||||
|
SERIALIZE_GET_OTHER_OBJ(BehaviorTreeNode);
|
||||||
|
SERIALIZE(Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BehaviorTreeNode::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||||
|
{
|
||||||
|
SerializableScriptingObject::Deserialize(stream, modifier);
|
||||||
|
|
||||||
|
Name.Clear(); // Missing Name is assumes as unnamed node
|
||||||
|
DESERIALIZE(Name);
|
||||||
|
}
|
||||||
41
Source/Engine/AI/BehaviorTreeNodes.h
Normal file
41
Source/Engine/AI/BehaviorTreeNodes.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BehaviorTreeNode.h"
|
||||||
|
#include "Engine/Core/Collections/Array.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for compound Behavior Tree nodes that composite child nodes.
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS(Abstract) class FLAXENGINE_API BehaviorTreeCompoundNode : public BehaviorTreeNode
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeCompoundNode, BehaviorTreeNode);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// List with all child nodes.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Readonly) Array<BehaviorTreeNode*, InlinedAllocation<8>> Children;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sequence node updates all its children as long as they return success. If any child fails, the sequence is failed.
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS() class FLAXENGINE_API BehaviorTreeSequenceNode : public BehaviorTreeCompoundNode
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeSequenceNode, BehaviorTreeCompoundNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Root node of the behavior tree. Contains logic properties and definitions for the runtime.
|
||||||
|
/// </summary>
|
||||||
|
API_CLASS(Sealed) class FLAXENGINE_API BehaviorTreeRootNode : public BehaviorTreeCompoundNode
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(BehaviorTreeRootNode, BehaviorTreeCompoundNode);
|
||||||
|
API_AUTO_SERIALIZATION();
|
||||||
|
|
||||||
|
// Full typename of the blackboard data type (structure or class). Spawned for each instance of the behavior.
|
||||||
|
API_FIELD(Attributes="EditorOrder(0), TypeReference(\"\", \"IsValidBlackboardType\"), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.TypeNameEditor\")")
|
||||||
|
StringAnsi BlackboardType;
|
||||||
|
};
|
||||||
35
Source/Engine/AI/BehaviorTypes.h
Normal file
35
Source/Engine/AI/BehaviorTypes.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Engine/Scripting/ScriptingType.h"
|
||||||
|
|
||||||
|
class BehaviorTree;
|
||||||
|
class BehaviorTreeNode;
|
||||||
|
class BehaviorKnowledge;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Behavior update context state.
|
||||||
|
/// </summary>
|
||||||
|
API_STRUCT() struct FLAXENGINE_API BehaviorUpdateContext
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_MINIMAL(BehaviorUpdateContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simulation time delta (in seconds) since the last update.
|
||||||
|
/// </summary>
|
||||||
|
float DeltaTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Behavior update result.
|
||||||
|
/// </summary>
|
||||||
|
API_ENUM() enum class BehaviorUpdateResult
|
||||||
|
{
|
||||||
|
// Action completed successfully.
|
||||||
|
Success,
|
||||||
|
// Action is still running and active.
|
||||||
|
Running,
|
||||||
|
// Action failed.
|
||||||
|
Failed,
|
||||||
|
};
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "CreateAnimationGraphFunction.h"
|
#include "CreateAnimationGraphFunction.h"
|
||||||
#include "CreateVisualScript.h"
|
#include "CreateVisualScript.h"
|
||||||
#include "CreateAnimation.h"
|
#include "CreateAnimation.h"
|
||||||
|
#include "CreateBehaviorTree.h"
|
||||||
#include "CreateJson.h"
|
#include "CreateJson.h"
|
||||||
|
|
||||||
// Tags used to detect asset creation mode
|
// Tags used to detect asset creation mode
|
||||||
@@ -54,6 +55,7 @@ const String AssetsImportingManager::CreateMaterialFunctionTag(TEXT("MaterialFun
|
|||||||
const String AssetsImportingManager::CreateParticleEmitterFunctionTag(TEXT("ParticleEmitterFunction"));
|
const String AssetsImportingManager::CreateParticleEmitterFunctionTag(TEXT("ParticleEmitterFunction"));
|
||||||
const String AssetsImportingManager::CreateAnimationGraphFunctionTag(TEXT("AnimationGraphFunction"));
|
const String AssetsImportingManager::CreateAnimationGraphFunctionTag(TEXT("AnimationGraphFunction"));
|
||||||
const String AssetsImportingManager::CreateAnimationTag(TEXT("Animation"));
|
const String AssetsImportingManager::CreateAnimationTag(TEXT("Animation"));
|
||||||
|
const String AssetsImportingManager::CreateBehaviorTreeTag(TEXT("BehaviorTree"));
|
||||||
const String AssetsImportingManager::CreateVisualScriptTag(TEXT("VisualScript"));
|
const String AssetsImportingManager::CreateVisualScriptTag(TEXT("VisualScript"));
|
||||||
|
|
||||||
class AssetsImportingManagerService : public EngineService
|
class AssetsImportingManagerService : public EngineService
|
||||||
@@ -485,6 +487,7 @@ bool AssetsImportingManagerService::Init()
|
|||||||
{ AssetsImportingManager::CreateParticleEmitterFunctionTag, CreateParticleEmitterFunction::Create },
|
{ AssetsImportingManager::CreateParticleEmitterFunctionTag, CreateParticleEmitterFunction::Create },
|
||||||
{ AssetsImportingManager::CreateAnimationGraphFunctionTag, CreateAnimationGraphFunction::Create },
|
{ AssetsImportingManager::CreateAnimationGraphFunctionTag, CreateAnimationGraphFunction::Create },
|
||||||
{ AssetsImportingManager::CreateAnimationTag, CreateAnimation::Create },
|
{ AssetsImportingManager::CreateAnimationTag, CreateAnimation::Create },
|
||||||
|
{ AssetsImportingManager::CreateBehaviorTreeTag, CreateBehaviorTree::Create },
|
||||||
{ AssetsImportingManager::CreateVisualScriptTag, CreateVisualScript::Create },
|
{ AssetsImportingManager::CreateVisualScriptTag, CreateVisualScript::Create },
|
||||||
};
|
};
|
||||||
AssetsImportingManager::Creators.Add(InBuildCreators, ARRAY_COUNT(InBuildCreators));
|
AssetsImportingManager::Creators.Add(InBuildCreators, ARRAY_COUNT(InBuildCreators));
|
||||||
|
|||||||
@@ -118,6 +118,11 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
static const String CreateAnimationTag;
|
static const String CreateAnimationTag;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The create Behavior Tree asset tag.
|
||||||
|
/// </summary>
|
||||||
|
static const String CreateBehaviorTreeTag;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The create visual script asset tag.
|
/// The create visual script asset tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
36
Source/Engine/ContentImporters/CreateBehaviorTree.h
Normal file
36
Source/Engine/ContentImporters/CreateBehaviorTree.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||||
|
|
||||||
|
#include "Engine/AI/BehaviorTree.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creating animation utility
|
||||||
|
/// </summary>
|
||||||
|
class CreateBehaviorTree
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static CreateAssetResult Create(CreateAssetContext& context)
|
||||||
|
{
|
||||||
|
// Base
|
||||||
|
IMPORT_SETUP(BehaviorTree, 1);
|
||||||
|
|
||||||
|
// Chunk 0 - Visject Surface
|
||||||
|
if (context.AllocateChunk(0))
|
||||||
|
return CreateAssetResult::CannotAllocateChunk;
|
||||||
|
{
|
||||||
|
const BehaviorTreeGraph graph;
|
||||||
|
MemoryWriteStream stream(64);
|
||||||
|
graph.Save(&stream, true);
|
||||||
|
context.Data.Header.Chunks[0]->Data.Copy(stream.GetHandle(), stream.GetPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateAssetResult::Ok;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -216,7 +216,7 @@ public:
|
|||||||
typedef void (VisjectExecutor::*ProcessBoxHandler)(Box*, Node*, Value&);
|
typedef void (VisjectExecutor::*ProcessBoxHandler)(Box*, Node*, Value&);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ProcessBoxHandler _perGroupProcessCall[19];
|
ProcessBoxHandler _perGroupProcessCall[20];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user