#if GRAPH_DESIGNER using Opsive.BehaviorDesigner.Runtime.Components; using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.BehaviorDesigner.Runtime.Utility; using Opsive.GraphDesigner.Runtime; using Opsive.GraphDesigner.Runtime.Variables.ECS; using Opsive.Shared.Utility; using Unity.Burst; using Unity.Entities; using UnityEngine; namespace Cielonos.MainGame.Characters.AI { [Description("可配置成功/失败策略的高级并行节点。\n" + "OneSuccess:任一子节点 Success 即整体成功并中止其余分支。\n" + "AllFailure:单个失败不中止其余分支,全部失败才整体失败。")] [NodeIcon("f612c025389b22640b1b6df88f4502e7", "8a4a401bcfb527a48a08351efaf92e14")] [Category("Cielonos")] public class AdvancedParallel : ECSCompositeTask, IParentNode, IParallelNode { [Tooltip("OneSuccess:任一子节点成功即整体成功。AllSuccess:全部子节点成功才整体成功。")] [SerializeField] private SuccessPolicy m_SuccessPolicy = SuccessPolicy.AllSuccess; [Tooltip("OneFailure:任一子节点失败即整体失败。AllFailure:所有子节点失败才整体失败,单个失败不中止其它分支。")] [SerializeField] private FailurePolicy m_FailurePolicy = FailurePolicy.AllFailure; /// /// Adds the IBufferElementData and interrupt components to the entity. /// 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; } /// /// Passes task configuration into the ECS buffer element at tree initialization. /// public override AdvancedParallelComponent GetBufferElement() { return new AdvancedParallelComponent { Index = RuntimeIndex, OneSuccessPolicy = m_SuccessPolicy == SuccessPolicy.OneSuccess, AllFailurePolicy = m_FailurePolicy == FailurePolicy.AllFailure }; } public override void Reset() { base.Reset(); m_SuccessPolicy = SuccessPolicy.OneSuccess; m_FailurePolicy = FailurePolicy.AllFailure; } } /// /// ECS buffer element storing the runtime index and policy flags for AdvancedParallel. /// public struct AdvancedParallelComponent : IBufferElementData { [SerializeField] private ushort m_Index; [SerializeField] private bool m_OneSuccessPolicy; [SerializeField] private bool m_AllFailurePolicy; public ushort Index { get => m_Index; set => m_Index = value; } public bool OneSuccessPolicy { get => m_OneSuccessPolicy; set => m_OneSuccessPolicy = value; } public bool AllFailurePolicy { get => m_AllFailurePolicy; set => m_AllFailurePolicy = value; } } /// /// ECS tag indicating an AdvancedParallel node is currently active. /// public struct AdvancedParallelFlag : IComponentData, IEnableableComponent { } /// /// Runs the AdvancedParallel evaluation logic each frame via Burst-compiled ECS job. /// [DisableAutoCreation] public partial struct AdvancedParallelTaskSystem : ISystem { private EntityQuery m_Query; private void OnCreate(ref SystemState state) { m_Query = SystemAPI.QueryBuilder() .WithAllRW() .WithAllRW() .WithAllRW() .WithAll() .Build(); } [BurstCompile] private void OnUpdate(ref SystemState state) { var ecb = SystemAPI.GetSingleton() .CreateCommandBuffer(state.WorldUnmanaged); state.Dependency = new AdvancedParallelJob { EntityCommandBuffer = ecb.AsParallelWriter() }.ScheduleParallel(m_Query, state.Dependency); } [BurstCompile] private partial struct AdvancedParallelJob : IJobEntity { public EntityCommandBuffer.ParallelWriter EntityCommandBuffer; [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; if (taskStatus != TaskStatus.Queued && taskStatus != TaskStatus.Running) continue; var branchComponent = branchComponents[taskComponent.BranchIndex]; if (branchComponent.InterruptType != InterruptType.None || !branchComponent.CanExecute) continue; ushort childIndex; TaskComponent childTaskComponent; // ── Initialize all children on first activation ────────────────────────── 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 initBranch = branchComponents[childTaskComponent.BranchIndex]; if (initBranch.NextIndex != childTaskComponent.Index) { initBranch.NextIndex = childTaskComponent.Index; branchComponents[childTaskComponent.BranchIndex] = initBranch; } childIndex = childTaskComponent.SiblingIndex; } } // ── Scan children and collect status flags ─────────────────────────────── var anyRunning = false; var anySuccess = false; var anyFailure = false; childIndex = (ushort)(parallelComponent.Index + 1); while (childIndex != ushort.MaxValue) { childTaskComponent = taskComponents[childIndex]; if (childTaskComponent.Status == TaskStatus.Queued || childTaskComponent.Status == TaskStatus.Running) { anyRunning = true; } else if (childTaskComponent.Status == TaskStatus.Success) { anySuccess = true; // Mark this branch as done so it doesn't keep executing. var childBranch = branchComponents[childTaskComponent.BranchIndex]; if (childBranch.ActiveIndex != ushort.MaxValue && childBranch.NextIndex != ushort.MaxValue) { childBranch.NextIndex = ushort.MaxValue; branchComponents[childTaskComponent.BranchIndex] = childBranch; } } else if (childTaskComponent.Status == TaskStatus.Failure) { anyFailure = true; // Stop this branch. Other branches continue when AllFailure policy is active. var childBranch = branchComponents[childTaskComponent.BranchIndex]; if (childBranch.NextIndex != ushort.MaxValue) { childBranch.NextIndex = ushort.MaxValue; branchComponents[childTaskComponent.BranchIndex] = childBranch; } } childIndex = childTaskComponent.SiblingIndex; } // ── Apply success/failure policies ─────────────────────────────────────── bool allTerminated = !anyRunning; // Success condition bool shouldSucceed = parallelComponent.OneSuccessPolicy ? anySuccess // OneSuccess: any child succeeded : allTerminated && !anyFailure; // AllSuccess: all done with no failures // Failure condition (only evaluated when success hasn't triggered) bool shouldFail = false; if (!shouldSucceed) { shouldFail = !parallelComponent.AllFailurePolicy ? anyFailure // OneFailure: any single failure → fail immediately : allTerminated; // AllFailure: fail only when nothing is still running } if (!shouldSucceed && !shouldFail) continue; // ── Stop all remaining running/queued children ─────────────────────────── ushort 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; if (!interruptedFlagSet) { EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, true); interruptedFlagSet = true; } branchComponent = branchComponents[childTaskComponent.BranchIndex]; if (branchComponent.ActiveIndex == childTaskComponent.Index && branchComponent.NextIndex != ushort.MaxValue) { branchComponent.NextIndex = ushort.MaxValue; branchComponents[childTaskComponent.BranchIndex] = branchComponent; } } } // ── Propagate result to parent ──────────────────────────────────────────── branchComponent = branchComponents[taskComponent.BranchIndex]; if (branchComponent.NextIndex != taskComponent.ParentIndex) { branchComponent.NextIndex = taskComponent.ParentIndex; branchComponents[taskComponent.BranchIndex] = branchComponent; } taskComponent.Status = shouldSucceed ? TaskStatus.Success : TaskStatus.Failure; taskComponents[taskComponent.Index] = taskComponent; } } } } } #endif