Files
Cielonos/Assets/Opsive/BehaviorDesigner/Add-Ons/CielonosPack/Composites/AdvancedSequence.cs
SoulliesOfficial 2e00676794 重做杂兵
2026-05-11 15:22:30 -04:00

334 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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<AdvancedSequenceTaskSystem, AdvancedSequenceComponent, AdvancedSequenceFlag>,
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);
/// <summary>
/// Returns a new buffer element for use by the ECS system.
/// </summary>
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
};
}
/// <summary>
/// Adds the IBufferElementData and interrupt components to the entity.
/// </summary>
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;
}
/// <summary>
/// Specifies that no reflection-based save is needed.
/// </summary>
public MemberVisibility GetSaveReflectionType(int index) { return MemberVisibility.None; }
/// <summary>
/// Saves the current task state.
/// </summary>
public object Save(World world, Entity entity)
{
var components = world.EntityManager.GetBuffer<AdvancedSequenceComponent>(entity);
var comp = components[m_ComponentIndex];
return new object[] { comp.ActiveChildIndex, comp.AnyChildSucceeded, comp.AnyChildFailed };
}
/// <summary>
/// Loads the previous task state.
/// </summary>
public void Load(object saveData, World world, Entity entity)
{
var arr = (object[])saveData;
var components = world.EntityManager.GetBuffer<AdvancedSequenceComponent>(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;
}
/// <summary>
/// Creates a deep clone of the task.
/// </summary>
public object Clone()
{
var clone = Activator.CreateInstance<AdvancedSequence>();
clone.Index = Index;
clone.ParentIndex = ParentIndex;
clone.SiblingIndex = SiblingIndex;
clone.Enabled = Enabled;
clone.AbortType = AbortType;
return clone;
}
/// <summary>
/// Resets the task fields to their default values.
/// </summary>
public override void Reset()
{
base.Reset();
m_SuccessPolicy = SuccessPolicy.AllSuccess;
m_FailurePolicy = FailurePolicy.OneFailure;
}
}
/// <summary>
/// ECS buffer element storing the runtime state and policy flags for AdvancedSequence.
/// </summary>
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; }
}
/// <summary>
/// ECS tag indicating an AdvancedSequence node is currently active.
/// </summary>
public struct AdvancedSequenceFlag : IComponentData, IEnableableComponent { }
/// <summary>
/// Runs the AdvancedSequence evaluation logic each frame via Burst-compiled ECS job.
/// </summary>
[DisableAutoCreation]
public partial struct AdvancedSequenceTaskSystem : ISystem
{
private EntityQuery m_Query;
private void OnCreate(ref SystemState state)
{
m_Query = SystemAPI.QueryBuilder()
.WithAllRW<BranchComponent>()
.WithAllRW<TaskComponent>()
.WithAllRW<AdvancedSequenceComponent>()
.WithAll<AdvancedSequenceFlag, EvaluateFlag>()
.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<BranchComponent> branchComponents,
ref DynamicBuffer<TaskComponent> taskComponents,
ref DynamicBuffer<AdvancedSequenceComponent> 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;
}
}
}
}
/// <summary>
/// Handles interrupt recovery for AdvancedSequence — ensures ActiveChildIndex
/// points to the correct running child after a conditional abort.
/// </summary>
[DisableAutoCreation]
public partial struct AdvancedSequenceInterruptSystem : ISystem
{
[BurstCompile]
private void OnUpdate(ref SystemState state)
{
foreach (var (taskComponents, sequenceComponents) in
SystemAPI.Query<DynamicBuffer<TaskComponent>, DynamicBuffer<AdvancedSequenceComponent>>()
.WithAll<InterruptFlag>())
{
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