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

268 lines
13 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;
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