#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime
{
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.GraphDesigner.Runtime;
using Opsive.Shared.Utility;
using System;
///
/// Helper class for ECS node templates.
///
public static class ECSNodeUtility
{
///
/// Converts a name to camelCase format.
///
/// The name to convert.
/// The name in camelCase format.
public static string ToCamelCase(string name)
{
if (string.IsNullOrEmpty(name))
return name;
return char.ToLowerInvariant(name[0]) + name.Substring(1);
}
}
///
/// Template for creating a custom action node.
///
[Category("ECS")]
[DisplayName("Action")]
[Description("Create a new ECS action node.")]
public class ECSActionNode : INodeTemplate
{
public Type BaseType => typeof(IAction);
public bool IsLogicNode => true;
///
/// Returns the script that should be used for the template file.
///
/// The name of the node.
/// The node script.
public string GetScript(string name)
{
var variableName = ECSNodeUtility.ToCamelCase(name);
return $@"using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.GraphDesigner.Runtime;
using Unity.Entities;
using Unity.Burst;
using UnityEngine;
///
/// A custom ECS action node.
///
public class {name} : ECSActionTask<{name}TaskSystem, {name}Component>
{{
///
/// The type of flag that should be enabled when the task is running.
///
public override ComponentType Flag {{ get => typeof({name}Flag); }}
///
/// Returns a new {name}Component for use by the system.
///
/// A new {name}Component for use by the system.
public override {name}Component GetBufferElement()
{{
return new {name}Component() {{
Index = RuntimeIndex,
}};
}}
}}
///
/// The DOTS data structure for the {name} class.
///
public struct {name}Component : IBufferElementData
{{
[Tooltip(""The index of the node."")]
public ushort Index;
}}
///
/// A DOTS flag indicating when a {name} node is active.
///
public struct {name}Flag : IComponentData, IEnableableComponent {{ }}
///
/// Runs the {name} logic.
///
[DisableAutoCreation]
public partial struct {name}TaskSystem : ISystem
{{
///
/// Creates the job.
///
/// The current state of the system.
[BurstCompile]
private void OnUpdate(ref SystemState state)
{{
var query = SystemAPI.QueryBuilder().WithAllRW().WithAllRW().WithAllRW<{name}Component>().WithAll<{name}Flag, EvaluateFlag>().Build();
state.Dependency = new {name}Job().ScheduleParallel(query, state.Dependency);
}}
///
/// Job which executes the task logic.
///
[BurstCompile]
private partial struct {name}Job : IJobEntity
{{
///
/// Executes the {name} logic.
///
/// An array of BranchComponents.
/// An array of TaskComponents.
/// An array of {name}Components.
[BurstCompile]
public void Execute(ref DynamicBuffer branchComponents, ref DynamicBuffer taskComponents, ref DynamicBuffer<{name}Component> {variableName}Components)
{{
for (int i = 0; i < {variableName}Components.Length; ++i) {{
var {variableName}Component = {variableName}Components[i];
var taskComponent = taskComponents[{variableName}Component.Index];
var branchComponent = branchComponents[taskComponent.BranchIndex];
if (!branchComponent.CanExecute) {{
continue;
}}
if (taskComponent.Status == TaskStatus.Queued) {{
taskComponent.Status = TaskStatus.Success;
taskComponents[{variableName}Component.Index] = taskComponent;
}}
}}
}}
}}
}}";
}
}
///
/// Template for creating a custom composite node.
///
[Category("ECS")]
[DisplayName("Composite")]
[Description("Create a new ECS composite node.")]
public class ECSCompositeNode : IParentNodeTemplate
{
public Type BaseType => typeof(IComposite);
public bool IsLogicNode => true;
///
/// Returns the script that should be used for the template file.
///
/// The name of the node.
/// The node script.
public string GetScript(string name)
{
var variableName = ECSNodeUtility.ToCamelCase(name);
return $@"using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.GraphDesigner.Runtime;
using Unity.Entities;
using Unity.Burst;
using UnityEngine;
///
/// A custom ECS composite node.
///
public class {name} : ECSCompositeTask<{name}TaskSystem, {name}Component>
{{
///
/// The type of tag that should be enabled when the task is running.
///
public override ComponentType Flag {{ get => typeof({name}Flag); }}
///
/// Returns a new {name}Component for use by the system.
///
/// A new {name}Component for use by the system.
public override {name}Component GetBufferElement()
{{
return new {name}Component() {{
Index = RuntimeIndex,
}};
}}
}}
///
/// The DOTS data structure for the {name} class.
///
public struct {name}Component : IBufferElementData
{{
[Tooltip(""The index of the node."")]
public ushort Index;
[Tooltip(""The index of the child that is currently active."")]
public ushort ActiveChildIndex;
}}
///
/// A DOTS flag indicating when a {name} node is active.
///
public struct {name}Flag : IComponentData, IEnableableComponent {{ }}
///
/// Runs the {name} logic.
///
[DisableAutoCreation]
public partial struct {name}TaskSystem : ISystem
{{
///
/// Creates the job.
///
/// The current state of the system.
[BurstCompile]
private void OnUpdate(ref SystemState state)
{{
var query = SystemAPI.QueryBuilder().WithAllRW().WithAllRW().WithAllRW<{name}Component>().WithAll<{name}Flag, EvaluateFlag>().Build();
state.Dependency = new {name}Job().ScheduleParallel(query, state.Dependency);
}}
///
/// Job which executes the task logic.
///
[BurstCompile]
private partial struct {name}Job : IJobEntity
{{
///
/// Executes the {name} logic.
///
/// An array of BranchComponents.
/// An array of TaskComponents.
/// An array of {name}Components.
[BurstCompile]
public void Execute(ref DynamicBuffer branchComponents, ref DynamicBuffer taskComponents, ref DynamicBuffer<{name}Component> {variableName}Components)
{{
for (int i = 0; i < {variableName}Components.Length; ++i) {{
var {variableName}Component = {variableName}Components[i];
var taskComponent = taskComponents[{variableName}Component.Index];
var branchComponent = branchComponents[taskComponent.BranchIndex];
if (!branchComponent.CanExecute) {{
continue;
}}
if (taskComponent.Status == TaskStatus.Queued) {{
taskComponent.Status = TaskStatus.Success;
taskComponents[{variableName}Component.Index] = taskComponent;
branchComponent.NextIndex = taskComponent.ParentIndex;
branchComponents[taskComponent.BranchIndex] = branchComponent;
}}
}}
}}
}}
}}";
}
}
///
/// Template for creating a custom conditional node.
///
[Category("ECS")]
[DisplayName("Conditional")]
[Description("Create a new ECS conditional node.")]
public class ECSConditionalNode : INodeTemplate
{
public Type BaseType => typeof(IConditional);
public bool IsLogicNode => true;
///
/// Returns the script that should be used for the template file.
///
/// The name of the node.
/// The node script.
public string GetScript(string name)
{
var variableName = ECSNodeUtility.ToCamelCase(name);
return $@"using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.GraphDesigner.Runtime;
using Unity.Burst;
using Unity.Entities;
using UnityEngine;
///
/// A custom ECS conditional node.
///
public class {name} : ECSConditionalTask<{name}TaskSystem, {name}Component>, IReevaluateResponder
{{
///
/// The type of flag that should be enabled when the task is running.
///
public override ComponentType Flag {{ get => typeof({name}Flag); }}
///
/// The type of flag that should be enabled when the task is being reevaluated.
///
public ComponentType ReevaluateFlag {{ get => typeof({name}ReevaluateFlag); }}
///
/// The system type that the reevaluation component uses.
///
public System.Type ReevaluateSystemType {{ get => typeof({name}ReevaluateTaskSystem); }}
///
/// Returns a new {name}Component for use by the system.
///
/// A new {name}Component for use by the system.
public override {name}Component GetBufferElement()
{{
return new {name}Component() {{
Index = RuntimeIndex,
}};
}}
}}
///
/// The DOTS data structure for the {name} class.
///
public struct {name}Component : IBufferElementData
{{
[Tooltip(""The index of the node."")]
public ushort Index;
}}
///
/// A DOTS flag indicating when a {name} node is active.
///
public struct {name}Flag : IComponentData, IEnableableComponent {{ }}
///
/// Runs the {name} logic.
///
[DisableAutoCreation]
public partial struct {name}TaskSystem : ISystem
{{
///
/// Creates the job.
///
/// The current state of the system.
[BurstCompile]
private void OnUpdate(ref SystemState state)
{{
var query = SystemAPI.QueryBuilder().WithAllRW().WithAllRW().WithAllRW<{name}Component>().WithAll<{name}Flag, EvaluateFlag>().Build();
state.Dependency = new {name}Job().ScheduleParallel(query, state.Dependency);
}}
///
/// Job which executes the task logic.
///
[BurstCompile]
private partial struct {name}Job : IJobEntity
{{
///
/// Executes the {name} logic.
///
/// An array of BranchComponents.
/// An array of TaskComponents.
/// An array of {name}Components.
[BurstCompile]
public void Execute(ref DynamicBuffer branchComponents, ref DynamicBuffer taskComponents, ref DynamicBuffer<{name}Component> {variableName}Components)
{{
for (int i = 0; i < {variableName}Components.Length; ++i) {{
var {variableName}Component = {variableName}Components[i];
var taskComponent = taskComponents[{variableName}Component.Index];
var branchComponent = branchComponents[taskComponent.BranchIndex];
if (!branchComponent.CanExecute) {{
continue;
}}
if (taskComponent.Status == TaskStatus.Queued || taskComponent.Status == TaskStatus.Running) {{ // Conditional aborts can set the status to Running.
taskComponent.Status = TaskStatus.Success;
taskComponents[{variableName}Component.Index] = taskComponent;
}}
}}
}}
}}
}}
///
/// A DOTS flag indicating when an {name} node needs to be reevaluated.
///
public struct {name}ReevaluateFlag : IComponentData, IEnableableComponent {{ }}
///
/// Runs the {name} reevaluation logic.
///
[DisableAutoCreation]
public partial struct {name}ReevaluateTaskSystem : ISystem
{{
///
/// Updates the reevaluation logic.
///
/// The current state of the system.
[BurstCompile]
private void OnUpdate(ref SystemState state)
{{
foreach (var (branchComponents, taskComponents, {variableName}Components) in
SystemAPI.Query, DynamicBuffer, DynamicBuffer<{name}Component>>().WithAll<{name}ReevaluateFlag, EvaluateFlag>()) {{
for (int i = 0; i < {variableName}Components.Length; ++i) {{
var {variableName}Component = {variableName}Components[i];
var taskComponent = taskComponents[{variableName}Component.Index];
var branchComponent = branchComponents[taskComponent.BranchIndex];
if (!branchComponent.CanExecute) {{
continue;
}}
if (!taskComponent.Reevaluate) {{
continue;
}}
var status = TaskStatus.Success;
if (status != taskComponent.Status) {{
taskComponent.Status = status;
var buffer = taskComponents;
buffer[taskComponent.Index] = taskComponent;
}}
}}
}}
}}
}}";
}
}
///
/// Template for creating a custom decorator node.
///
[Category("ECS")]
[DisplayName("Decorator")]
[Description("Create a new ECS decorator node.")]
public class ECSDecoratorNode : IParentNodeTemplate
{
public Type BaseType => typeof(IDecorator);
public bool IsLogicNode => true;
///
/// Returns the script that should be used for the template file.
///
/// The name of the node.
/// The node script.
public string GetScript(string name)
{
var variableName = ECSNodeUtility.ToCamelCase(name);
return $@"using Opsive.BehaviorDesigner.Runtime.Components;
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.GraphDesigner.Runtime;
using Unity.Entities;
using Unity.Burst;
using UnityEngine;
///
/// A custom ECS decorator node.
///
public class {name} : ECSDecoratorTask<{name}TaskSystem, {name}Component>
{{
///
/// The type of flag that should be enabled when the task is running.
///
public override ComponentType Flag {{ get => typeof({name}Flag); }}
///
/// Returns a new {name}Component for use by the system.
///
/// A new {name}Component for use by the system.
public override {name}Component GetBufferElement()
{{
return new {name}Component() {{
Index = RuntimeIndex,
}};
}}
}}
///
/// The DOTS data structure for the {name} class.
///
public struct {name}Component : IBufferElementData
{{
[Tooltip(""The index of the node."")]
public ushort Index;
}}
///
/// A DOTS flag indicating when a {name} node is active.
///
public struct {name}Flag : IComponentData, IEnableableComponent {{ }}
///
/// Runs the {name} logic.
///
[DisableAutoCreation]
public partial struct {name}TaskSystem : ISystem
{{
///
/// Creates the job.
///
/// The current state of the system.
[BurstCompile]
private void OnUpdate(ref SystemState state)
{{
var query = SystemAPI.QueryBuilder().WithAllRW().WithAllRW().WithAllRW<{name}Component>().WithAll<{name}Flag, EvaluateFlag>().Build();
state.Dependency = new {name}Job().ScheduleParallel(query, state.Dependency);
}}
///
/// Job which executes the task logic.
///
[BurstCompile]
private partial struct {name}Job : IJobEntity
{{
///
/// Executes the {name} logic.
///
/// An array of BranchComponents.
/// An array of TaskComponents.
/// An array of {name}Components.
[BurstCompile]
public void Execute(ref DynamicBuffer branchComponents, ref DynamicBuffer taskComponents, ref DynamicBuffer<{name}Component> {variableName}Components)
{{
for (int i = 0; i < {variableName}Components.Length; ++i) {{
var {variableName}Component = {variableName}Components[i];
var taskComponent = taskComponents[{variableName}Component.Index];
var branchComponent = branchComponents[taskComponent.BranchIndex];
if (!branchComponent.CanExecute) {{
continue;
}}
if (taskComponent.Status == TaskStatus.Queued) {{
taskComponent.Status = TaskStatus.Success;
taskComponents[{variableName}Component.Index] = taskComponent;
}}
}}
}}
}}
}}";
}
}
}
#endif