#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; using System; namespace Cielonos.MainGame.Characters.AI { [Description("可配置成功/失败策略的高级顺序节点。\n" + "按顺序逐个执行子节点,根据策略决定整体结果。\n" + "OneSuccess:任一子节点 Success 即整体成功。\n" + "AllFailure:单个失败不中止后续子节点,全部失败才整体失败。")] [NodeIcon("f612c025389b22640b1b6df88f4502e7", "8a4a401bcfb527a48a08351efaf92e14")] [Category("Cielonos")] public class AdvancedSequence : ECSCompositeTask, IParentNode, IConditionalAbortParent, IInterruptResponder, ISavableTask, ICloneable { [Tooltip("AllSuccess:全部子节点成功才整体成功。OneSuccess:任一子节点成功即整体成功。")] [SerializeField] private SuccessPolicy m_SuccessPolicy = SuccessPolicy.AllSuccess; [Tooltip("OneFailure:任一子节点失败即整体失败。AllFailure:所有子节点失败才整体失败,单个失败不中止后续子节点。")] [SerializeField] private FailurePolicy m_FailurePolicy = FailurePolicy.OneFailure; [Tooltip("指定子节点条件中断的重新评估方式。")] [SerializeField] private ConditionalAbortType m_AbortType; private ushort m_ComponentIndex; public ConditionalAbortType AbortType { get => m_AbortType; set => m_AbortType = value; } public Type InterruptSystemType => typeof(AdvancedSequenceInterruptSystem); /// /// Returns a new buffer element for use by the ECS system. /// public override AdvancedSequenceComponent GetBufferElement() { return new AdvancedSequenceComponent { Index = RuntimeIndex, ActiveChildIndex = 0, OneSuccessPolicy = m_SuccessPolicy == SuccessPolicy.OneSuccess, AllFailurePolicy = m_FailurePolicy == FailurePolicy.AllFailure, AnyChildSucceeded = false, AnyChildFailed = false }; } /// /// Adds the IBufferElementData and interrupt components to the entity. /// public override int AddBufferElement(World world, Entity entity, ECSVariableRegistry registry, GameObject gameObject) { m_ComponentIndex = (ushort)base.AddBufferElement(world, entity, registry, gameObject); ComponentUtility.AddInterruptComponents(world.EntityManager, entity); return m_ComponentIndex; } /// /// Specifies that no reflection-based save is needed. /// public MemberVisibility GetSaveReflectionType(int index) { return MemberVisibility.None; } /// /// Saves the current task state. /// public object Save(World world, Entity entity) { var components = world.EntityManager.GetBuffer(entity); var comp = components[m_ComponentIndex]; return new object[] { comp.ActiveChildIndex, comp.AnyChildSucceeded, comp.AnyChildFailed }; } /// /// Loads the previous task state. /// public void Load(object saveData, World world, Entity entity) { var arr = (object[])saveData; var components = world.EntityManager.GetBuffer(entity); var comp = components[m_ComponentIndex]; comp.ActiveChildIndex = (ushort)arr[0]; comp.AnyChildSucceeded = (bool)arr[1]; comp.AnyChildFailed = (bool)arr[2]; components[m_ComponentIndex] = comp; } /// /// Creates a deep clone of the task. /// public object Clone() { var clone = Activator.CreateInstance(); clone.Index = Index; clone.ParentIndex = ParentIndex; clone.SiblingIndex = SiblingIndex; clone.Enabled = Enabled; clone.AbortType = AbortType; return clone; } /// /// Resets the task fields to their default values. /// public override void Reset() { base.Reset(); m_SuccessPolicy = SuccessPolicy.AllSuccess; m_FailurePolicy = FailurePolicy.OneFailure; } } /// /// ECS buffer element storing the runtime state and policy flags for AdvancedSequence. /// public struct AdvancedSequenceComponent : IBufferElementData { [SerializeField] private ushort m_Index; [SerializeField] private ushort m_ActiveChildIndex; [SerializeField] private bool m_OneSuccessPolicy; [SerializeField] private bool m_AllFailurePolicy; [SerializeField] private bool m_AnyChildSucceeded; [SerializeField] private bool m_AnyChildFailed; public ushort Index { get => m_Index; set => m_Index = value; } public ushort ActiveChildIndex { get => m_ActiveChildIndex; set => m_ActiveChildIndex = value; } public bool OneSuccessPolicy { get => m_OneSuccessPolicy; set => m_OneSuccessPolicy = value; } public bool AllFailurePolicy { get => m_AllFailurePolicy; set => m_AllFailurePolicy = value; } public bool AnyChildSucceeded { get => m_AnyChildSucceeded; set => m_AnyChildSucceeded = value; } public bool AnyChildFailed { get => m_AnyChildFailed; set => m_AnyChildFailed = value; } } /// /// ECS tag indicating an AdvancedSequence node is currently active. /// public struct AdvancedSequenceFlag : IComponentData, IEnableableComponent { } /// /// Runs the AdvancedSequence evaluation logic each frame via Burst-compiled ECS job. /// [DisableAutoCreation] public partial struct AdvancedSequenceTaskSystem : 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) { state.Dependency = new AdvancedSequenceJob() .ScheduleParallel(m_Query, state.Dependency); } [BurstCompile] private partial struct AdvancedSequenceJob : IJobEntity { [BurstCompile] public void Execute( ref DynamicBuffer branchComponents, ref DynamicBuffer taskComponents, ref DynamicBuffer sequenceComponents) { for (int i = 0; i < sequenceComponents.Length; ++i) { var seqComp = sequenceComponents[i]; var taskComp = taskComponents[seqComp.Index]; var taskStatus = taskComp.Status; if (taskStatus != TaskStatus.Queued && taskStatus != TaskStatus.Running) continue; var branchComp = branchComponents[taskComp.BranchIndex]; if (branchComp.InterruptType != InterruptType.None || !branchComp.CanExecute) continue; // ── First activation: queue the first child ───────────────────────── if (taskStatus == TaskStatus.Queued) { taskComp.Status = TaskStatus.Running; taskComponents[taskComp.Index] = taskComp; seqComp.ActiveChildIndex = (ushort)(taskComp.Index + 1); seqComp.AnyChildSucceeded = false; seqComp.AnyChildFailed = false; sequenceComponents[i] = seqComp; branchComp.NextIndex = seqComp.ActiveChildIndex; branchComponents[taskComp.BranchIndex] = branchComp; var firstChild = taskComponents[branchComp.NextIndex]; if (firstChild.Status != TaskStatus.Queued) { firstChild.Status = TaskStatus.Queued; taskComponents[branchComp.NextIndex] = firstChild; } } // ── Check the active child's status ───────────────────────────────── var childComp = taskComponents[seqComp.ActiveChildIndex]; if (childComp.Status == TaskStatus.Queued || childComp.Status == TaskStatus.Running) continue; // Still running, wait. // Treat Inactive (disabled) children as Success. var childSucceeded = childComp.Status == TaskStatus.Success || childComp.Status == TaskStatus.Inactive; var childFailed = childComp.Status == TaskStatus.Failure; if (childSucceeded) seqComp.AnyChildSucceeded = true; if (childFailed) seqComp.AnyChildFailed = true; // ── Early termination checks ──────────────────────────────────────── // OneSuccess: any child succeeding ends the sequence with Success. if (childSucceeded && seqComp.OneSuccessPolicy) { taskComp.Status = TaskStatus.Success; taskComponents[seqComp.Index] = taskComp; branchComp.NextIndex = taskComp.ParentIndex; branchComponents[taskComp.BranchIndex] = branchComp; sequenceComponents[i] = seqComp; continue; } // OneFailure (default): any child failing ends the sequence with Failure. if (childFailed && !seqComp.AllFailurePolicy) { taskComp.Status = TaskStatus.Failure; taskComponents[seqComp.Index] = taskComp; branchComp.NextIndex = taskComp.ParentIndex; branchComponents[taskComp.BranchIndex] = branchComp; sequenceComponents[i] = seqComp; continue; } // ── Advance to next sibling or finalize ───────────────────────────── if (childComp.SiblingIndex == ushort.MaxValue) { // No more children. Determine final result based on policies. bool shouldSucceed = seqComp.OneSuccessPolicy ? seqComp.AnyChildSucceeded : !seqComp.AnyChildFailed; taskComp.Status = shouldSucceed ? TaskStatus.Success : TaskStatus.Failure; seqComp.ActiveChildIndex = (ushort)(seqComp.Index + 1); taskComponents[seqComp.Index] = taskComp; branchComp.NextIndex = taskComp.ParentIndex; branchComponents[taskComp.BranchIndex] = branchComp; } else { // Move to the next sibling. var siblingComp = taskComponents[childComp.SiblingIndex]; if (siblingComp.Status != TaskStatus.Queued) { siblingComp.Status = TaskStatus.Queued; taskComponents[childComp.SiblingIndex] = siblingComp; } seqComp.ActiveChildIndex = childComp.SiblingIndex; branchComp.NextIndex = seqComp.ActiveChildIndex; branchComponents[taskComp.BranchIndex] = branchComp; } sequenceComponents[i] = seqComp; } } } } /// /// Handles interrupt recovery for AdvancedSequence — ensures ActiveChildIndex /// points to the correct running child after a conditional abort. /// [DisableAutoCreation] public partial struct AdvancedSequenceInterruptSystem : ISystem { [BurstCompile] private void OnUpdate(ref SystemState state) { foreach (var (taskComponents, sequenceComponents) in SystemAPI.Query, DynamicBuffer>() .WithAll()) { for (int i = 0; i < sequenceComponents.Length; ++i) { var seqComp = sequenceComponents[i]; if (seqComp.ActiveChildIndex == ushort.MaxValue || seqComp.ActiveChildIndex >= taskComponents.Length) continue; if (taskComponents[seqComp.ActiveChildIndex].Status != TaskStatus.Running) { var childIndex = (ushort)(seqComp.Index + 1); while (childIndex != ushort.MaxValue && childIndex < taskComponents.Length && taskComponents[childIndex].Status != TaskStatus.Running) { childIndex = taskComponents[childIndex].SiblingIndex; } if (childIndex != ushort.MaxValue && childIndex < taskComponents.Length) { seqComp.ActiveChildIndex = childIndex; } var buffer = sequenceComponents; buffer[i] = seqComp; } } } } } } #endif