#if GRAPH_DESIGNER /// --------------------------------------------- /// Behavior Designer /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.BehaviorDesigner.Runtime.Tasks.Composites { using Opsive.BehaviorDesigner.Runtime.Components; using Opsive.BehaviorDesigner.Runtime.Utility; using Opsive.GraphDesigner.Runtime.Variables.ECS; using Opsive.GraphDesigner.Runtime; using Unity.Burst; using Unity.Entities; using UnityEngine; /// /// A node representation of the parallel task. /// [NodeIcon("f612c025389b22640b1b6df88f4502e7", "8a4a401bcfb527a48a08351efaf92e14")] [Opsive.Shared.Utility.Description("Similar to the sequence task, the parallel task will run each child task until a child task returns failure. " + "The parallel task will run all of its children tasks simultaneously versus running each task one at a time. " + "Like the sequence class, the parallel task will return success once all of its children tasks have return success. " + "If one tasks returns failure the parallel task will end all of the child tasks and return failure.")] public class Parallel : ECSCompositeTask, IParentNode, IParallelNode { /// /// Adds the IBufferElementData to the entity. /// /// The world that the entity exists in. /// The entity that the IBufferElementData should be assigned to. /// The ECS variable registry for registering SharedVariable fields. /// The GameObject that the entity is attached to. /// The index of the element within the buffer. public override int AddBufferElement(World world, Entity entity, ECSVariableRegistry registry, GameObject gameObject) { var index = base.AddBufferElement(world, entity, registry, gameObject); ComponentUtility.AddInterruptComponents(world.EntityManager, entity); return index; } /// /// Returns a new TBufferElement for use by the system. /// /// A new TBufferElement for use by the system. public override ParallelComponent GetBufferElement() { return new ParallelComponent() { Index = RuntimeIndex }; } } /// /// The DOTS data structure for the Parallel class. /// public struct ParallelComponent : IBufferElementData { [Tooltip("The index of the node.")] [SerializeField] ushort m_Index; public ushort Index { get => m_Index; set => m_Index = value; } } /// /// A DOTS tag indicating when a Parallel node is active. /// public struct ParallelFlag : IComponentData, IEnableableComponent { } /// /// Runs the Parallel logic. /// [DisableAutoCreation] public partial struct ParallelTaskSystem : ISystem { private EntityQuery m_Query; /// /// Builds the query. /// /// THe current SystemState. private void OnCreate(ref SystemState state) { m_Query = SystemAPI.QueryBuilder().WithAllRW().WithAllRW().WithAllRW().WithAll().Build(); } /// /// Creates the job. /// /// The current state of the system. [BurstCompile] private void OnUpdate(ref SystemState state) { var ecb = SystemAPI.GetSingleton().CreateCommandBuffer(state.WorldUnmanaged); state.Dependency = new ParallelJob() { EntityCommandBuffer = ecb.AsParallelWriter() }.ScheduleParallel(m_Query, state.Dependency); } /// /// Job which executes the task logic. /// [BurstCompile] private partial struct ParallelJob : IJobEntity { [Tooltip("CommandBuffer which sets the component data.")] public EntityCommandBuffer.ParallelWriter EntityCommandBuffer; /// /// Executes the parallel logic. /// /// The entity that is being acted upon. /// The index of the entity. /// An array of ParallelComponents. /// An array of TaskComponents. /// An array of BranchComponents. [BurstCompile] public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer parallelComponents, ref DynamicBuffer taskComponents, ref DynamicBuffer branchComponents) { for (int i = 0; i < parallelComponents.Length; ++i) { var parallelComponent = parallelComponents[i]; var taskComponent = taskComponents[parallelComponent.Index]; var taskStatus = taskComponent.Status; // Skip inactive tasks before any branch lookups. if (taskStatus != TaskStatus.Queued && taskStatus != TaskStatus.Running) { continue; } var branchComponent = branchComponents[taskComponent.BranchIndex]; // Do not continue if there will be an interrupt or the branch cannot execute. if (branchComponent.InterruptType != InterruptType.None || !branchComponent.CanExecute) { continue; } ushort childIndex; TaskComponent childTaskComponent; if (taskStatus == TaskStatus.Queued) { taskComponent.Status = TaskStatus.Running; taskComponents[taskComponent.Index] = taskComponent; childIndex = (ushort)(parallelComponent.Index + 1); while (childIndex != ushort.MaxValue) { childTaskComponent = taskComponents[childIndex]; if (childTaskComponent.Status != TaskStatus.Queued) { childTaskComponent.Status = TaskStatus.Queued; taskComponents[childIndex] = childTaskComponent; } var childBranchComponent = branchComponents[childTaskComponent.BranchIndex]; if (childBranchComponent.NextIndex != childTaskComponent.Index) { childBranchComponent.NextIndex = childTaskComponent.Index; branchComponents[childTaskComponent.BranchIndex] = childBranchComponent; } childIndex = childTaskComponent.SiblingIndex; } } var childrenFailure = false; var childrenRunning = false; childIndex = (ushort)(parallelComponent.Index + 1); while (childIndex != ushort.MaxValue) { childTaskComponent = taskComponents[childIndex]; if (childTaskComponent.Status == TaskStatus.Queued || childTaskComponent.Status == TaskStatus.Running) { childrenRunning = true; } else if (childTaskComponent.Status == TaskStatus.Failure) { childrenFailure = true; var childBranchComponent = branchComponents[childTaskComponent.BranchIndex]; if (childBranchComponent.NextIndex != ushort.MaxValue) { childBranchComponent.NextIndex = ushort.MaxValue; branchComponents[childTaskComponent.BranchIndex] = childBranchComponent; } break; } else if (childTaskComponent.Status == TaskStatus.Success) { var childBranchComponent = branchComponents[childTaskComponent.BranchIndex]; if (childBranchComponent.ActiveIndex != ushort.MaxValue && childBranchComponent.NextIndex != ushort.MaxValue) { childBranchComponent.NextIndex = ushort.MaxValue; branchComponents[childTaskComponent.BranchIndex] = childBranchComponent; } } childIndex = childTaskComponent.SiblingIndex; } // If a single child fails then all tasks should be stopped. if (childrenFailure) { var maxChildIndex = taskComponent.ChildUpperIndex > taskComponent.Index ? taskComponent.ChildUpperIndex : (ushort)(taskComponent.Index); var interruptedFlagSet = false; for (ushort j = (ushort)(taskComponent.Index + 1); j <= maxChildIndex; ++j) { childTaskComponent = taskComponents[j]; if (childTaskComponent.Status == TaskStatus.Running || childTaskComponent.Status == TaskStatus.Queued) { childTaskComponent.Status = TaskStatus.Failure; taskComponents[j] = childTaskComponent; branchComponent = branchComponents[childTaskComponent.BranchIndex]; if (!interruptedFlagSet) { EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, true); interruptedFlagSet = true; } if (branchComponent.ActiveIndex == childTaskComponent.Index) { if (branchComponent.NextIndex != ushort.MaxValue) { branchComponent.NextIndex = ushort.MaxValue; branchComponents[childTaskComponent.BranchIndex] = branchComponent; } } } } if (branchComponent.NextIndex != taskComponent.ParentIndex) { branchComponent.NextIndex = taskComponent.ParentIndex; branchComponents[taskComponent.BranchIndex] = branchComponent; } taskComponent.Status = TaskStatus.Failure; taskComponents[taskComponent.Index] = taskComponent; continue; } if (childrenRunning) { continue; } // No more children are running. Resume the parent task. taskComponent.Status = TaskStatus.Success; taskComponents[taskComponent.Index] = taskComponent; if (branchComponent.NextIndex != taskComponent.ParentIndex) { branchComponent.NextIndex = taskComponent.ParentIndex; branchComponents[taskComponent.BranchIndex] = branchComponent; } } } } } } #endif