#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