Files
Cielonos/Assets/Opsive/BehaviorDesigner/Add-Ons/CielonosPack/Composites/AdvancedParallel.cs

268 lines
13 KiB
C#
Raw Normal View History

2026-05-11 15:22:30 -04:00
#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<AdvancedParallelTaskSystem, AdvancedParallelComponent, AdvancedParallelFlag>, IParentNode, IParallelNode
{
[Tooltip("OneSuccess任一子节点成功即整体成功。AllSuccess全部子节点成功才整体成功。")]
[SerializeField] private SuccessPolicy m_SuccessPolicy = SuccessPolicy.AllSuccess;
[Tooltip("OneFailure任一子节点失败即整体失败。AllFailure所有子节点失败才整体失败单个失败不中止其它分支。")]
[SerializeField] private FailurePolicy m_FailurePolicy = FailurePolicy.AllFailure;
/// <summary>
/// Adds the IBufferElementData and interrupt components to the entity.
/// </summary>
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;
}
/// <summary>
/// Passes task configuration into the ECS buffer element at tree initialization.
/// </summary>
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;
}
}
/// <summary>
/// ECS buffer element storing the runtime index and policy flags for AdvancedParallel.
/// </summary>
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; }
}
/// <summary>
/// ECS tag indicating an AdvancedParallel node is currently active.
/// </summary>
public struct AdvancedParallelFlag : IComponentData, IEnableableComponent { }
/// <summary>
/// Runs the AdvancedParallel evaluation logic each frame via Burst-compiled ECS job.
/// </summary>
[DisableAutoCreation]
public partial struct AdvancedParallelTaskSystem : ISystem
{
private EntityQuery m_Query;
private void OnCreate(ref SystemState state)
{
m_Query = SystemAPI.QueryBuilder()
.WithAllRW<BranchComponent>()
.WithAllRW<TaskComponent>()
.WithAllRW<AdvancedParallelComponent>()
.WithAll<AdvancedParallelFlag, EvaluateFlag>()
.Build();
}
[BurstCompile]
private void OnUpdate(ref SystemState state)
{
var ecb = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>()
.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<AdvancedParallelComponent> parallelComponents,
ref DynamicBuffer<TaskComponent> taskComponents,
ref DynamicBuffer<BranchComponent> 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<InterruptedFlag>(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