添加bd
This commit is contained in:
@@ -0,0 +1,412 @@
|
||||
#if GRAPH_DESIGNER
|
||||
/// ---------------------------------------------
|
||||
/// Behavior Designer
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
namespace Opsive.BehaviorDesigner.Runtime.Systems
|
||||
{
|
||||
using Opsive.BehaviorDesigner.Runtime.Components;
|
||||
using Opsive.BehaviorDesigner.Runtime.Groups;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||
using Opsive.BehaviorDesigner.Runtime.Utility;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// System which checks for any tasks that should be reevaluated with conditional aborts. This system only marks the tasks, it does not do
|
||||
/// the actual reevaluation or interruption.
|
||||
/// </summary>
|
||||
[UpdateInGroup(typeof(BeforeTraversalSystemGroup), OrderFirst = true)]
|
||||
[DisableAutoCreation]
|
||||
public partial struct ReevaluateSystem : ISystem
|
||||
{
|
||||
private EntityQuery m_Query;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the query.
|
||||
/// </summary>
|
||||
/// <param name="state">THe current SystemState.</param>
|
||||
private void OnCreate(ref SystemState state)
|
||||
{
|
||||
m_Query = SystemAPI.QueryBuilder().WithAllRW<TaskComponent>().WithAllRW<ReevaluateTaskComponent>().WithAll<BranchComponent>().WithAbsent<BakedBehaviorTree>().Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the ReevaluateJob.
|
||||
/// </summary>
|
||||
/// <param name="state">The current SystemState.</param>
|
||||
private void OnUpdate(ref SystemState state)
|
||||
{
|
||||
var ecb = new EntityCommandBuffer(Allocator.TempJob);
|
||||
state.Dependency = new ReevaluateJob()
|
||||
{
|
||||
EntityCommandBuffer = ecb.AsParallelWriter(),
|
||||
}.ScheduleParallel(m_Query, state.Dependency);
|
||||
|
||||
// The job must run immediately for the next systems.
|
||||
state.Dependency.Complete();
|
||||
ecb.Playback(state.EntityManager);
|
||||
ecb.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Job which checks for any tasks that should be reevaluated with conditional aborts. This job only flags the tasks, it does not do
|
||||
/// the actual reevaluation or interruption.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
private partial struct ReevaluateJob : IJobEntity
|
||||
{
|
||||
[Tooltip("CommandBuffer which sets the component data.")]
|
||||
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the job.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param>
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
/// <param name="reevaluateTaskComponents">An array of reevaluate task components.</param>
|
||||
[BurstCompile]
|
||||
public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, in DynamicBuffer<BranchComponent> branchComponents, ref DynamicBuffer<TaskComponent> taskComponents, ref DynamicBuffer<ReevaluateTaskComponent> reevaluateTaskComponents)
|
||||
{
|
||||
for (int i = 0; i < reevaluateTaskComponents.Length; ++i) {
|
||||
var reevaluateTaskComponent = reevaluateTaskComponents[i];
|
||||
// The task may not be able to reevaluate.
|
||||
var taskComponent = taskComponents[reevaluateTaskComponent.Index];
|
||||
if (!taskComponent.CanReevaluate || taskComponent.Disabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The branch may not be active.
|
||||
var branchComponent = branchComponents[taskComponent.BranchIndex];
|
||||
if (branchComponent.ActiveIndex == ushort.MaxValue) {
|
||||
if (taskComponent.Reevaluate) {
|
||||
taskComponent.Reevaluate = false;
|
||||
taskComponents[reevaluateTaskComponent.Index] = taskComponent;
|
||||
|
||||
reevaluateTaskComponent.ReevaluateStatus = ReevaluateStatus.Inactive;
|
||||
reevaluateTaskComponents[i] = reevaluateTaskComponent;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var reevaluate = false;
|
||||
if (reevaluateTaskComponent.AbortType == ConditionalAbortType.Self || reevaluateTaskComponent.AbortType == ConditionalAbortType.Both) {
|
||||
if (branchComponent.ActiveIndex > taskComponent.Index && branchComponent.ActiveIndex <= reevaluateTaskComponent.SelfPriorityUpperIndex) {
|
||||
// Reevaluate.
|
||||
reevaluate = true;
|
||||
if (reevaluateTaskComponent.ReevaluateStatus == ReevaluateStatus.Inactive) {
|
||||
reevaluateTaskComponent.ReevaluateStatus = ReevaluateStatus.Active;
|
||||
EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, reevaluateTaskComponent.ReevaluateFlagComponentType, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!reevaluate && (reevaluateTaskComponent.AbortType == ConditionalAbortType.LowerPriority || reevaluateTaskComponent.AbortType == ConditionalAbortType.Both)) {
|
||||
if (branchComponent.ActiveIndex > reevaluateTaskComponent.LowerPriorityLowerIndex && branchComponent.ActiveIndex <= reevaluateTaskComponent.LowerPriorityUpperIndex) {
|
||||
// Reevaluate.
|
||||
reevaluate = true;
|
||||
if (reevaluateTaskComponent.ReevaluateStatus == ReevaluateStatus.Inactive) {
|
||||
reevaluateTaskComponent.ReevaluateStatus = ReevaluateStatus.Active;
|
||||
EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, reevaluateTaskComponent.ReevaluateFlagComponentType, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The task should no longer reevaluate.
|
||||
if (!reevaluate && (taskComponent.Reevaluate || reevaluateTaskComponent.ReevaluateStatus == ReevaluateStatus.Dirty)) {
|
||||
// The system needs to be kept active if there are other tasks with the same reevaluate tag.
|
||||
var keepSystemActive = false;
|
||||
for (int j = 0; j < reevaluateTaskComponents.Length; ++j) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((reevaluateTaskComponents[j].ReevaluateStatus == ReevaluateStatus.Active || reevaluateTaskComponents[j].ReevaluateStatus == ReevaluateStatus.Dirty) &&
|
||||
reevaluateTaskComponent.ReevaluateFlagComponentType == reevaluateTaskComponents[j].ReevaluateFlagComponentType) {
|
||||
keepSystemActive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!keepSystemActive) {
|
||||
EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, reevaluateTaskComponent.ReevaluateFlagComponentType, false);
|
||||
}
|
||||
// The task should always disable itself.
|
||||
taskComponent.Reevaluate = false;
|
||||
reevaluateTaskComponent.ReevaluateStatus = ReevaluateStatus.Inactive;
|
||||
} else {
|
||||
// Store the current status of the task. This status will be compared after the task is reevaluated within DetermineInterruptSystem.
|
||||
reevaluateTaskComponent.OriginalStatus = taskComponent.Status;
|
||||
}
|
||||
|
||||
reevaluateTaskComponents[i] = reevaluateTaskComponent;
|
||||
|
||||
taskComponent.Reevaluate = reevaluate;
|
||||
taskComponents[reevaluateTaskComponent.Index] = taskComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The tasks have been reevaluated. Compare the status to determine if an interrupt should occur.
|
||||
/// </summary>
|
||||
[UpdateInGroup(typeof(InterruptSystemGroup))]
|
||||
[UpdateBefore(typeof(InterruptSystem))]
|
||||
public partial struct ConditionalAbortsInvokerSystem : ISystem
|
||||
{
|
||||
private EntityQuery m_Query;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the query.
|
||||
/// </summary>
|
||||
/// <param name="state">THe current SystemState.</param>
|
||||
private void OnCreate(ref SystemState state)
|
||||
{
|
||||
m_Query = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAllRW<TaskComponent>().WithAllRW<ReevaluateTaskComponent>().WithAbsent<BakedBehaviorTree>().Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the jobs necessary for conditional aborts.
|
||||
/// </summary>
|
||||
/// <param name="state">The current SystemState.</param>
|
||||
[BurstCompile]
|
||||
private void OnUpdate(ref SystemState state)
|
||||
{
|
||||
var ecb = new EntityCommandBuffer(Allocator.TempJob);
|
||||
state.Dependency = new ConditionalAbortsJob()
|
||||
{
|
||||
EntityCommandBuffer = ecb.AsParallelWriter()
|
||||
}.ScheduleParallel(m_Query, state.Dependency);
|
||||
|
||||
// The jobs must be run immediately for the next systems.
|
||||
state.Dependency.Complete();
|
||||
ecb.Playback(state.EntityManager);
|
||||
ecb.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Job which checks for any tasks that should be reevaluated with conditional aborts. This job only flags the tasks, it does not do
|
||||
/// the actual reevaluation or interruption.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
private partial struct ConditionalAbortsJob : IJobEntity
|
||||
{
|
||||
[Tooltip("CommandBuffer which sets the component data.")]
|
||||
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the job.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param>
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
/// <param name="reevaluateTaskComponents">An array of reevaluate task components.</param>
|
||||
[BurstCompile]
|
||||
public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, ref DynamicBuffer<TaskComponent> taskComponents, ref DynamicBuffer<ReevaluateTaskComponent> reevaluateTaskComponents)
|
||||
{
|
||||
for (int i = 0; i < reevaluateTaskComponents.Length; ++i) {
|
||||
var reevaluateTaskComponent = reevaluateTaskComponents[i];
|
||||
var taskComponent = taskComponents[reevaluateTaskComponent.Index];
|
||||
|
||||
if (taskComponent.Reevaluate) {
|
||||
if (reevaluateTaskComponent.OriginalStatus != taskComponent.Status) {
|
||||
// The status is different. This will cause an interrupt.
|
||||
var branchComponent = branchComponents[taskComponent.BranchIndex];
|
||||
// The task with the highest priority should cause the abort.
|
||||
if (branchComponent.InterruptType == InterruptType.None || taskComponent.Index < branchComponent.InterruptIndex) {
|
||||
branchComponent.InterruptIndex = taskComponent.Index;
|
||||
branchComponent.InterruptType = InterruptType.Branch;
|
||||
branchComponents[taskComponent.BranchIndex] = branchComponent;
|
||||
} else {
|
||||
taskComponent.Status = TaskStatus.Inactive;
|
||||
}
|
||||
|
||||
taskComponent.Reevaluate = false;
|
||||
taskComponents[reevaluateTaskComponent.Index] = taskComponent;
|
||||
EntityCommandBuffer.SetComponentEnabled<InterruptFlag>(entityIndex, entity, true);
|
||||
|
||||
reevaluateTaskComponent.ReevaluateStatus = ReevaluateStatus.Dirty;
|
||||
var reevaluateTaskComponentsBuffer = reevaluateTaskComponents;
|
||||
reevaluateTaskComponentsBuffer[i] = reevaluateTaskComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes any interrupts.
|
||||
/// </summary>
|
||||
[UpdateInGroup(typeof(InterruptSystemGroup))]
|
||||
[UpdateAfter(typeof(ConditionalAbortsInvokerSystem))]
|
||||
public partial struct InterruptSystem : ISystem
|
||||
{
|
||||
private EntityQuery m_Query;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the query.
|
||||
/// </summary>
|
||||
/// <param name="state">THe current SystemState.</param>
|
||||
private void OnCreate(ref SystemState state)
|
||||
{
|
||||
m_Query = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAllRW<TaskComponent>().WithAll<InterruptFlag>().Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the InterruptJob.
|
||||
/// </summary>
|
||||
/// <param name="state">The current SystemState.</param>
|
||||
[BurstCompile]
|
||||
private void OnUpdate(ref SystemState state)
|
||||
{
|
||||
var ecb = new EntityCommandBuffer(Allocator.TempJob);
|
||||
state.Dependency = new InterruptJob()
|
||||
{
|
||||
EntityCommandBuffer = ecb.AsParallelWriter(),
|
||||
}.ScheduleParallel(m_Query, state.Dependency);
|
||||
|
||||
// The job must run immediately for the next systems.
|
||||
state.Dependency.Complete();
|
||||
ecb.Playback(state.EntityManager);
|
||||
ecb.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the interrupts.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
private partial struct InterruptJob : IJobEntity
|
||||
{
|
||||
[Tooltip("CommandBuffer which sets the component data.")]
|
||||
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the job.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param>
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
[BurstCompile]
|
||||
public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, DynamicBuffer<BranchComponent> branchComponents, DynamicBuffer<TaskComponent> taskComponents)
|
||||
{
|
||||
for (ushort i = 0; i < branchComponents.Length; ++i) {
|
||||
var branchComponent = branchComponents[i];
|
||||
if (branchComponent.InterruptType != InterruptType.None) {
|
||||
var targetTaskComponent = taskComponents[branchComponent.InterruptIndex];
|
||||
var parentIndex = targetTaskComponent.ParentIndex == ushort.MaxValue ? targetTaskComponent.Index : targetTaskComponent.ParentIndex;
|
||||
TaskStatus prevActiveNewStatus;
|
||||
if (branchComponent.InterruptType == InterruptType.Branch) {
|
||||
branchComponent.NextIndex = branchComponent.InterruptIndex;
|
||||
branchComponents[i] = branchComponent;
|
||||
|
||||
// Start the target task.
|
||||
targetTaskComponent.Status = TaskStatus.Running;
|
||||
// Set the target branch tasks to running. Any parent that uses conditional aborts should implement IInterruptResponder.
|
||||
while (parentIndex != ushort.MaxValue && taskComponents[parentIndex].Status != TaskStatus.Running) {
|
||||
var parentTaskComponent = taskComponents[parentIndex];
|
||||
parentTaskComponent.Status = TaskStatus.Running;
|
||||
taskComponents[parentIndex] = parentTaskComponent;
|
||||
parentIndex = parentTaskComponent.ParentIndex;
|
||||
}
|
||||
|
||||
prevActiveNewStatus = TaskStatus.Failure;
|
||||
} else { // InterruptType.ImmediateSuccess/Failure.
|
||||
targetTaskComponent.Status = branchComponent.InterruptType == InterruptType.ImmediateSuccess ? TaskStatus.Success : TaskStatus.Failure;
|
||||
var targetBranchComponent = branchComponents[targetTaskComponent.BranchIndex];
|
||||
targetBranchComponent.NextIndex = targetTaskComponent.ParentIndex;
|
||||
branchComponents[targetTaskComponent.BranchIndex] = targetBranchComponent;
|
||||
|
||||
prevActiveNewStatus = targetTaskComponent.Status;
|
||||
}
|
||||
|
||||
// Determine if any other branches need to be interrupted.
|
||||
for (ushort j = i; j < branchComponents.Length; ++j) {
|
||||
if (i == j || TraversalUtility.IsParent((ushort)branchComponents[j].ActiveIndex, parentIndex, ref taskComponents)) {
|
||||
AbortChildren((ushort)branchComponents[j].ActiveIndex, parentIndex, ref taskComponents, prevActiveNewStatus);
|
||||
|
||||
// Reset any queued children.
|
||||
var taskComponentBuffer = taskComponents;
|
||||
var childCount = TraversalUtility.GetChildCount(branchComponent.ActiveIndex, ref taskComponentBuffer);
|
||||
for (int k = 0; k < childCount; ++k) {
|
||||
var childTaskComponent = taskComponents[branchComponent.ActiveIndex + k + 1];
|
||||
if (childTaskComponent.Status == TaskStatus.Queued) {
|
||||
childTaskComponent.Status = TaskStatus.Inactive;
|
||||
taskComponentBuffer[branchComponent.ActiveIndex + k + 1] = childTaskComponent;
|
||||
}
|
||||
}
|
||||
|
||||
// If the branch is a parallel branch then reset the NextIndex. The current branch (i) will be interrupted normally above.
|
||||
var localBranchComponent = branchComponents[j];
|
||||
if (localBranchComponent.InterruptType == InterruptType.None) {
|
||||
localBranchComponent.NextIndex = ushort.MaxValue;
|
||||
branchComponents[j] = localBranchComponent;
|
||||
}
|
||||
EntityCommandBuffer.SetComponentEnabled<InterruptedFlag>(entityIndex, entity, true);
|
||||
}
|
||||
}
|
||||
|
||||
taskComponents[targetTaskComponent.Index] = targetTaskComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aborts all of the children within the specified branch.
|
||||
/// </summary>
|
||||
/// <param name="activeIndex">The index of the active task within the branch.</param>
|
||||
/// <param name="parentIndex">Aborts the tasks up to the specified parent index.</param>
|
||||
/// <param name="taskComponents">All of the tasks.</param>
|
||||
/// <param name="status">The abort status.</param>
|
||||
[BurstCompile]
|
||||
private void AbortChildren(ushort activeIndex, ushort parentIndex, ref DynamicBuffer<TaskComponent> taskComponents, TaskStatus status)
|
||||
{
|
||||
while (activeIndex != ushort.MaxValue && activeIndex != parentIndex) {
|
||||
var activeTask = taskComponents[activeIndex];
|
||||
activeTask.Status = status;
|
||||
taskComponents[activeIndex] = activeTask;
|
||||
activeIndex = activeTask.ParentIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup the interrupts after they have run.
|
||||
/// </summary>
|
||||
[UpdateInGroup(typeof(InterruptSystemGroup), OrderLast = true)]
|
||||
public partial struct InterruptCleanupSystem : ISystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the system.
|
||||
/// </summary>
|
||||
/// <param name="state">The current SystemState.</param>
|
||||
[BurstCompile]
|
||||
private void OnUpdate(ref SystemState state)
|
||||
{
|
||||
foreach (var (branchComponents, entity) in
|
||||
SystemAPI.Query<DynamicBuffer<BranchComponent>>().WithAll<InterruptFlag>().WithEntityAccess()) {
|
||||
for (int i = 0; i < branchComponents.Length; ++i) {
|
||||
var branchComponent = branchComponents[i];
|
||||
if (branchComponent.InterruptType != InterruptType.None) {
|
||||
// Reset the interruption.
|
||||
branchComponent.InterruptType = InterruptType.None;
|
||||
branchComponent.InterruptIndex = 0;
|
||||
var branchComponentBuffer = branchComponents;
|
||||
branchComponentBuffer[i] = branchComponent;
|
||||
|
||||
state.EntityManager.SetComponentEnabled<InterruptFlag>(entity, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffa99079a1d11e547943e43bd0446c86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,176 @@
|
||||
#if GRAPH_DESIGNER
|
||||
/// ---------------------------------------------
|
||||
/// Behavior Designer
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
namespace Opsive.BehaviorDesigner.Runtime.Systems
|
||||
{
|
||||
using Opsive.BehaviorDesigner.Runtime.Components;
|
||||
using Opsive.BehaviorDesigner.Runtime.Groups;
|
||||
using Unity.Burst;
|
||||
using Unity.Burst.Intrinsics;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Resets the evaluation status.
|
||||
/// </summary>
|
||||
[DisableAutoCreation]
|
||||
[UpdateInGroup(typeof(BehaviorTreeSystemGroup), OrderLast = true)]
|
||||
[BurstCompile]
|
||||
public partial struct EvaluationCleanupSystem : ISystem
|
||||
{
|
||||
private EntityQuery m_EvaluateCleanupQuery;
|
||||
private ComponentTypeHandle<EnabledFlag> m_EnabledComponentHandle;
|
||||
private ComponentTypeHandle<EvaluateFlag> m_EvaluateComponentHandle;
|
||||
private BufferTypeHandle<BranchComponent> m_BranchComponentHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the required objects for use within the job system.
|
||||
/// </summary>
|
||||
/// <param name="state">The current SystemState.</param>
|
||||
[BurstCompile]
|
||||
private void OnCreate(ref SystemState state)
|
||||
{
|
||||
m_EvaluateCleanupQuery = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<EvaluateFlag>()
|
||||
.WithAllRW<BranchComponent>()
|
||||
.WithOptions(EntityQueryOptions.IgnoreComponentEnabledState)
|
||||
.Build(ref state);
|
||||
m_EnabledComponentHandle = state.GetComponentTypeHandle<EnabledFlag>();
|
||||
m_EvaluateComponentHandle = state.GetComponentTypeHandle<EvaluateFlag>();
|
||||
m_BranchComponentHandle = state.GetBufferTypeHandle<BranchComponent>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the data object values for use within the job system.
|
||||
/// </summary>
|
||||
/// <param name="state">The current SystemState.</param>
|
||||
[BurstCompile]
|
||||
private void OnUpdate(ref SystemState state)
|
||||
{
|
||||
state.Dependency.Complete();
|
||||
|
||||
// Reset the evaluation status.
|
||||
m_EnabledComponentHandle.Update(ref state);
|
||||
m_EvaluateComponentHandle.Update(ref state);
|
||||
m_BranchComponentHandle.Update(ref state);
|
||||
var evaluationCleanupJob = new EvaluationCleanupJob()
|
||||
{
|
||||
EnabledComponentHandle = m_EnabledComponentHandle,
|
||||
EvaluateComponentHandle = m_EvaluateComponentHandle,
|
||||
BranchComponentHandle = m_BranchComponentHandle,
|
||||
};
|
||||
state.Dependency = evaluationCleanupJob.ScheduleParallel(m_EvaluateCleanupQuery, state.Dependency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Job that resets the EvaluationComponent component value.
|
||||
/// </summary>
|
||||
[BurstCompile(CompileSynchronously = true)]
|
||||
public struct EvaluationCleanupJob : IJobChunk
|
||||
{
|
||||
[UnityEngine.Tooltip("A reference to the Enabled Component Handle.")]
|
||||
public ComponentTypeHandle<EnabledFlag> EnabledComponentHandle;
|
||||
[UnityEngine.Tooltip("A reference to the Evaluate Component Handle.")]
|
||||
public ComponentTypeHandle<EvaluateFlag> EvaluateComponentHandle;
|
||||
[UnityEngine.Tooltip("A reference to the Branch Component Handle.")]
|
||||
public BufferTypeHandle<BranchComponent> BranchComponentHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Resets the EvaluationComponent component value.
|
||||
/// </summary>
|
||||
/// <param name="chunk">Block of memory that contains the entity and components.</param>
|
||||
/// <param name="unfilteredChunkIndex">The index of the chunk.</param>
|
||||
/// <param name="useEnabledMask">Should the enabled mask be used?</param>
|
||||
/// <param name="chunkEnabledMask">The bitwise enabled mask.</param>
|
||||
[BurstCompile]
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var branchAccessor = chunk.GetBufferAccessor(ref BranchComponentHandle);
|
||||
for (int i = 0; i < chunk.Count; i++) {
|
||||
// If the chunk is enabled then it should be evaluated.
|
||||
if (chunk.IsComponentEnabled<EnabledFlag>(ref EnabledComponentHandle, i)) {
|
||||
chunk.SetComponentEnabled<EvaluateFlag>(ref EvaluateComponentHandle, i, true);
|
||||
}
|
||||
|
||||
// Reset CanExecute for all branches so they can execute in the next tick.
|
||||
var branchComponents = branchAccessor[i];
|
||||
for (int j = 0; j < branchComponents.Length; j++) {
|
||||
var branchComponent = branchComponents[j];
|
||||
branchComponent.CanExecute = true;
|
||||
branchComponent.LastActiveIndex = ushort.MaxValue;
|
||||
branchComponents[j] = branchComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the InterruptedFlag enabled value.
|
||||
/// </summary>
|
||||
[DisableAutoCreation]
|
||||
[UpdateInGroup(typeof(BehaviorTreeSystemGroup), OrderLast = true)]
|
||||
[BurstCompile]
|
||||
public partial struct InterruptedCleanupSystem : ISystem
|
||||
{
|
||||
private EntityQuery m_InterruptedCleanupQuery;
|
||||
private ComponentTypeHandle<InterruptedFlag> m_InterruptedComponentHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the required objects for use within the job system.
|
||||
/// </summary>
|
||||
/// <param name="state">The current SystemState.</param>
|
||||
[BurstCompile]
|
||||
private void OnCreate(ref SystemState state)
|
||||
{
|
||||
m_InterruptedCleanupQuery = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<InterruptedFlag>()
|
||||
.Build(ref state);
|
||||
m_InterruptedComponentHandle = state.GetComponentTypeHandle<InterruptedFlag>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the data object values for use within the job system.
|
||||
/// </summary>
|
||||
/// <param name="state">The current SystemState.</param>
|
||||
[BurstCompile]
|
||||
private void OnUpdate(ref SystemState state)
|
||||
{
|
||||
// Clean up the interrupted tag.
|
||||
m_InterruptedComponentHandle.Update(ref state);
|
||||
var interruptedJob = new InterruptedCleanupJob()
|
||||
{
|
||||
InterruptedComponentHandle = m_InterruptedComponentHandle,
|
||||
};
|
||||
state.Dependency = interruptedJob.ScheduleParallel(m_InterruptedCleanupQuery, state.Dependency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Job that resets the InterruptedFlag value.
|
||||
/// </summary>
|
||||
[BurstCompile(CompileSynchronously = true)]
|
||||
public partial struct InterruptedCleanupJob : IJobChunk
|
||||
{
|
||||
[UnityEngine.Tooltip("A reference to the Interrupted Component Handle.")]
|
||||
public ComponentTypeHandle<InterruptedFlag> InterruptedComponentHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Resets the InterruptedFlag value.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param>
|
||||
[BurstCompile]
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
for (int i = 0; i < chunk.Count; i++) {
|
||||
// Only chunks with the tag enabled will be returned so there's no need to check if the tag is enabled.
|
||||
chunk.SetComponentEnabled<InterruptedFlag>(ref InterruptedComponentHandle, i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36ac1accbf78b7344be0384fd1edf942
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,217 @@
|
||||
#if GRAPH_DESIGNER
|
||||
/// ---------------------------------------------
|
||||
/// Behavior Designer
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
namespace Opsive.BehaviorDesigner.Runtime.Systems
|
||||
{
|
||||
using Opsive.BehaviorDesigner.Runtime.Components;
|
||||
using Opsive.BehaviorDesigner.Runtime.Groups;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||
using Opsive.BehaviorDesigner.Runtime.Utility;
|
||||
using Opsive.GraphDesigner.Runtime;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that the node is an object task which can specify the next child that should run.
|
||||
/// </summary>
|
||||
public interface ITaskObjectParentNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the index of the next child that should run. Set to ushort.MaxValue to ignore.
|
||||
/// </summary>
|
||||
ushort NextChildIndex { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The DOTS data structure for the TaskObject class.
|
||||
/// </summary>
|
||||
public struct TaskObjectComponent : IBufferElementData
|
||||
{
|
||||
[Tooltip("The index of the task.")]
|
||||
public ushort Index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A DOTS flag indicating when an TaskObject node is active.
|
||||
/// </summary>
|
||||
public struct TaskObjectFlag : IComponentData, IEnableableComponent { }
|
||||
|
||||
/// <summary>
|
||||
/// Runs the TaskObject logic.
|
||||
/// </summary>
|
||||
[DisableAutoCreation]
|
||||
[UpdateInGroup(typeof(TraversalTaskSystemGroup), OrderLast = true)]
|
||||
public partial struct TaskObjectSystem : ISystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the logic.
|
||||
/// </summary>
|
||||
/// <param name="state">The current state of the system.</param>
|
||||
private void OnUpdate(ref SystemState state)
|
||||
{
|
||||
// When the task is interrupted there is no callback which prevents Task.OnEnd from being called. Track the status within the referenced task object and if the status is different then
|
||||
// the task was aborted and OnEnd needs to be called.
|
||||
foreach (var (taskObjectComponents, taskComponents, entity) in
|
||||
SystemAPI.Query<DynamicBuffer<TaskObjectComponent>, DynamicBuffer<TaskComponent>>().WithAll<InterruptedFlag>().WithEntityAccess()) {
|
||||
var behaviorTree = BehaviorTree.GetBehaviorTree(entity);
|
||||
if (behaviorTree == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < taskObjectComponents.Length; ++i) {
|
||||
var taskObjectComponent = taskObjectComponents[i];
|
||||
var taskComponent = taskComponents[taskObjectComponent.Index];
|
||||
if (taskComponent.Status == TaskStatus.Success || taskComponent.Status == TaskStatus.Failure) {
|
||||
var task = behaviorTree.GetTask(taskObjectComponent.Index) as Task;
|
||||
if (task.Status != taskComponent.Status) {
|
||||
task.OnEnd();
|
||||
task.Status = taskComponent.Status;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the task objects.
|
||||
foreach (var (taskObjectComponents, taskComponents, branchComponents, entity) in
|
||||
SystemAPI.Query<DynamicBuffer<TaskObjectComponent>, DynamicBuffer<TaskComponent>, DynamicBuffer<BranchComponent>>().WithAll<TaskObjectFlag, EvaluateFlag>().WithEntityAccess()) {
|
||||
|
||||
var behaviorTree = BehaviorTree.GetBehaviorTree(entity);
|
||||
if (behaviorTree == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < taskObjectComponents.Length; ++i) {
|
||||
var taskObjectComponent = taskObjectComponents[i];
|
||||
var taskComponent = taskComponents[taskObjectComponent.Index];
|
||||
var branchComponent = branchComponents[taskComponent.BranchIndex];
|
||||
if (!branchComponent.CanExecute || branchComponent.ActiveIndex != taskComponent.Index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var task = behaviorTree.GetTask(taskObjectComponent.Index) as Task;
|
||||
if (taskComponent.Status == TaskStatus.Queued) {
|
||||
task.Status = taskComponent.Status = TaskStatus.Running;
|
||||
var taskComponentBuffer = taskComponents;
|
||||
taskComponentBuffer[taskComponent.Index] = taskComponent;
|
||||
|
||||
task.OnStart();
|
||||
}
|
||||
if (taskComponent.Status != TaskStatus.Running) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var status = task.OnUpdate();
|
||||
// Update the status if has changed.
|
||||
if (status != taskComponent.Status) {
|
||||
task.Status = taskComponent.Status = status;
|
||||
var taskComponentBuffer = taskComponents;
|
||||
taskComponentBuffer[taskComponent.Index] = taskComponent;
|
||||
|
||||
// End the task if it is done running.
|
||||
if (status != TaskStatus.Running) {
|
||||
task.OnEnd();
|
||||
|
||||
branchComponent = branchComponents[taskComponent.BranchIndex];
|
||||
branchComponent.NextIndex = taskComponent.ParentIndex;
|
||||
var branchComponentBuffer = branchComponents;
|
||||
branchComponentBuffer[taskComponent.BranchIndex] = branchComponent;
|
||||
}
|
||||
}
|
||||
|
||||
if (task is IParentNode && (task is ITaskObjectParentNode taskObjectParentNode)) {
|
||||
if (status == TaskStatus.Running) {
|
||||
// Parent object tasks do not have a direct way to set the next child. Use the ITaskObjectParentNode to switch the child task.
|
||||
if (taskObjectParentNode.NextChildIndex != ushort.MaxValue && taskComponents[taskObjectParentNode.NextChildIndex].Status != TaskStatus.Running) {
|
||||
branchComponent = branchComponents[taskComponent.BranchIndex];
|
||||
branchComponent.NextIndex = taskObjectParentNode.NextChildIndex;
|
||||
var branchComponentBuffer = branchComponents;
|
||||
branchComponentBuffer[taskComponent.BranchIndex] = branchComponent;
|
||||
|
||||
var nextTaskComponent = taskComponents[taskObjectParentNode.NextChildIndex];
|
||||
nextTaskComponent.Status = TaskStatus.Queued;
|
||||
var taskComponentBuffer = taskComponents;
|
||||
taskComponentBuffer[taskObjectParentNode.NextChildIndex] = nextTaskComponent;
|
||||
}
|
||||
} else if (status == TaskStatus.Success || status == TaskStatus.Failure) {
|
||||
// An interrupt should occur if the parent returns a success or failure status before the children.
|
||||
var taskComponentBuffer = taskComponents;
|
||||
var childCount = TraversalUtility.GetChildCount(taskComponent.Index, ref taskComponentBuffer);
|
||||
var branchComponentBuffer = branchComponents;
|
||||
var hasInterruptComponents = SystemAPI.HasComponent<InterruptFlag>(entity);
|
||||
var interruptedFlagEnabled = SystemAPI.IsComponentEnabled<InterruptedFlag>(entity);
|
||||
for (ushort j = (ushort)(taskComponent.Index + 1); j < taskComponent.Index + 1 + childCount; ++j) {
|
||||
var childTaskComponent = taskComponentBuffer[j];
|
||||
if (childTaskComponent.Status == TaskStatus.Running || childTaskComponent.Status == TaskStatus.Queued) {
|
||||
childTaskComponent.Status = status;
|
||||
taskComponentBuffer[j] = childTaskComponent;
|
||||
|
||||
branchComponent = branchComponentBuffer[childTaskComponent.BranchIndex];
|
||||
if (!hasInterruptComponents) {
|
||||
ComponentUtility.AddInterruptComponents(behaviorTree.World.EntityManager, entity);
|
||||
hasInterruptComponents = true;
|
||||
}
|
||||
if (!interruptedFlagEnabled) {
|
||||
SystemAPI.SetComponentEnabled<InterruptedFlag>(entity, true);
|
||||
interruptedFlagEnabled = true;
|
||||
}
|
||||
if (branchComponent.ActiveIndex == childTaskComponent.Index) {
|
||||
branchComponent.NextIndex = ushort.MaxValue;
|
||||
branchComponentBuffer[childTaskComponent.BranchIndex] = branchComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A DOTS tag indicating when an TaskObject node needs to be reevaluated.
|
||||
/// </summary>
|
||||
public struct TaskObjectReevaluateFlag : IComponentData, IEnableableComponent
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the TaskObject reevaluation logic.
|
||||
/// </summary>
|
||||
[DisableAutoCreation]
|
||||
public partial struct TaskObjectReevaluateSystem : ISystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the reevaluation logic.
|
||||
/// </summary>
|
||||
/// <param name="state">The current state of the system.</param>
|
||||
private void OnUpdate(ref SystemState state)
|
||||
{
|
||||
foreach (var (taskComponents, taskObjectComponents, entity) in
|
||||
SystemAPI.Query<DynamicBuffer<TaskComponent>, DynamicBuffer<TaskObjectComponent>>().WithAll<TaskObjectReevaluateFlag, EvaluateFlag>().WithEntityAccess()) {
|
||||
for (int i = 0; i < taskObjectComponents.Length; ++i) {
|
||||
var taskObjectComponent = taskObjectComponents[i];
|
||||
var taskComponent = taskComponents[taskObjectComponent.Index];
|
||||
if (!taskComponent.Reevaluate) {
|
||||
continue;
|
||||
}
|
||||
var behaviorTree = BehaviorTree.GetBehaviorTree(entity);
|
||||
if (behaviorTree == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var task = behaviorTree.GetTask(taskObjectComponent.Index) as IConditionalReevaluation;
|
||||
var status = task.OnReevaluateUpdate();
|
||||
if (status != taskComponent.Status) {
|
||||
taskComponent.Status = status;
|
||||
var buffer = taskComponents;
|
||||
buffer[taskComponent.Index] = taskComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 203f2349f12650c4f887e6a0a3925c04
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,617 @@
|
||||
#if GRAPH_DESIGNER
|
||||
/// ---------------------------------------------
|
||||
/// Behavior Designer
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
namespace Opsive.BehaviorDesigner.Runtime.Systems
|
||||
{
|
||||
using Opsive.BehaviorDesigner.Runtime.Components;
|
||||
using Opsive.BehaviorDesigner.Runtime.Groups;
|
||||
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||||
using Opsive.BehaviorDesigner.Runtime.Utility;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Traverses and ensures the correct tasks are active.
|
||||
/// </summary>
|
||||
[UpdateInGroup(typeof(TraversalSystemGroup))]
|
||||
[UpdateAfter(typeof(TraversalTaskSystemGroup))]
|
||||
public partial struct EvaluationSystem : ISystem
|
||||
{
|
||||
private bool m_JobScheduled;
|
||||
private EntityQuery m_Query;
|
||||
private JobHandle m_Dependency;
|
||||
private EntityCommandBuffer m_EntityCommandBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// The system has been created.
|
||||
/// </summary>
|
||||
/// <param name="state">The state of the system.</param>
|
||||
private void OnCreate(ref SystemState state)
|
||||
{
|
||||
m_JobScheduled = false;
|
||||
m_Query = SystemAPI.QueryBuilder().WithAllRW<BranchComponent, TaskComponent>().WithAbsent<BakedBehaviorTree>().Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the job which traverses the tree.
|
||||
/// </summary>
|
||||
/// <param name="state">The current state of the system.</param>
|
||||
private void OnUpdate(ref SystemState state)
|
||||
{
|
||||
m_JobScheduled = true;
|
||||
|
||||
m_EntityCommandBuffer = new EntityCommandBuffer(state.WorldUpdateAllocator);
|
||||
m_Dependency = state.Dependency = new EvaluationJob()
|
||||
{
|
||||
EntityCommandBuffer = m_EntityCommandBuffer.AsParallelWriter(),
|
||||
}.ScheduleParallel(m_Query, state.Dependency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completes the job and releases any memory.
|
||||
/// </summary>
|
||||
/// <param name="entityManager">The running EntityManager.</param>
|
||||
/// <param name="stopRunning">Has the system been stopped?</param>
|
||||
[BurstCompile]
|
||||
public void Complete(EntityManager entityManager, bool stopRunning = false)
|
||||
{
|
||||
if (!m_JobScheduled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stopRunning) {
|
||||
m_Dependency.Complete();
|
||||
m_EntityCommandBuffer.Playback(entityManager);
|
||||
m_EntityCommandBuffer.Dispose();
|
||||
}
|
||||
|
||||
m_JobScheduled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Job which traverses the tree.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
public partial struct EvaluationJob : IJobEntity
|
||||
{
|
||||
[Tooltip("CommandBuffer which sets the component data.")]
|
||||
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the job.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
[BurstCompile]
|
||||
public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, ref DynamicBuffer<TaskComponent> taskComponents)
|
||||
{
|
||||
for (int i = 0; i < branchComponents.Length; ++i) {
|
||||
var branchComponent = branchComponents[i];
|
||||
if (branchComponent.ActiveIndex != ushort.MaxValue && branchComponent.ActiveIndex == branchComponent.NextIndex) {
|
||||
var activeTask = taskComponents[branchComponent.ActiveIndex];
|
||||
if (activeTask.Status == TaskStatus.Success || activeTask.Status == TaskStatus.Failure) {
|
||||
branchComponent.NextIndex = activeTask.ParentIndex;
|
||||
}
|
||||
}
|
||||
if (branchComponent.ActiveIndex != branchComponent.NextIndex) {
|
||||
// Do not switch into a disabled task.
|
||||
if (branchComponent.NextIndex != ushort.MaxValue && taskComponents[branchComponent.NextIndex].Disabled) {
|
||||
var taskComponent = taskComponents[branchComponent.NextIndex];
|
||||
taskComponent.Status = TaskStatus.Inactive;
|
||||
var taskComponentBuffer = taskComponents;
|
||||
taskComponentBuffer[branchComponent.NextIndex] = taskComponent;
|
||||
|
||||
branchComponent.NextIndex = branchComponent.ActiveIndex;
|
||||
} else {
|
||||
// The status for all children should be reset back to their inactive state if the next task is within a new branch. This will prevent
|
||||
// the return status from being reset when the task ends normally.
|
||||
var taskComponentBuffer = taskComponents;
|
||||
if (branchComponent.NextIndex != ushort.MaxValue &&
|
||||
!TraversalUtility.IsParent((ushort)branchComponent.ActiveIndex, (ushort)branchComponent.NextIndex, ref taskComponentBuffer)) {
|
||||
var nextTaskComponent = taskComponents[branchComponent.NextIndex];
|
||||
if (branchComponent.ActiveIndex != ushort.MaxValue && nextTaskComponent.Status != TaskStatus.Running) { // If the next task is already running then an interrupt has already reset the children.
|
||||
var childCount = TraversalUtility.GetChildCount(branchComponent.NextIndex, ref taskComponentBuffer);
|
||||
for (int j = 0; j < childCount; ++j) {
|
||||
var childTaskComponent = taskComponents[branchComponent.NextIndex + j + 1];
|
||||
childTaskComponent.Status = TaskStatus.Inactive;
|
||||
taskComponentBuffer[branchComponent.NextIndex + j + 1] = childTaskComponent;
|
||||
}
|
||||
}
|
||||
nextTaskComponent.Status = nextTaskComponent.Status == TaskStatus.Running ? TaskStatus.Running : TaskStatus.Queued;
|
||||
taskComponentBuffer[branchComponent.NextIndex] = nextTaskComponent;
|
||||
}
|
||||
branchComponent.ActiveIndex = branchComponent.NextIndex;
|
||||
|
||||
// Change the component tag if the task type is different.
|
||||
var componentType = branchComponent.ActiveIndex != ushort.MaxValue ? taskComponents[branchComponent.ActiveIndex].FlagComponentType : new ComponentType();
|
||||
if (componentType != branchComponent.ActiveFlagComponentType) {
|
||||
if (branchComponent.ActiveFlagComponentType.TypeIndex != TypeIndex.Null) {
|
||||
var deactivateTag = true;
|
||||
for (int j = 0; j < branchComponents.Length; ++j) {
|
||||
// The tag should be deactivated if no other tasks have the same tag type.
|
||||
if (i != j && branchComponents[j].ActiveIndex != ushort.MaxValue &&
|
||||
branchComponent.ActiveFlagComponentType == branchComponents[j].ActiveFlagComponentType) {
|
||||
deactivateTag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The task of that type is no longer active - disable the system to prevent it from running.
|
||||
if (deactivateTag) {
|
||||
EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, branchComponent.ActiveFlagComponentType, false);
|
||||
}
|
||||
}
|
||||
// A new system type should start.
|
||||
if (branchComponent.ActiveIndex != ushort.MaxValue) {
|
||||
var taskComponent = taskComponents[branchComponent.ActiveIndex];
|
||||
EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, taskComponent.FlagComponentType, true);
|
||||
}
|
||||
branchComponent.ActiveFlagComponentType = componentType;
|
||||
}
|
||||
}
|
||||
var branchComponentBuffer = branchComponents;
|
||||
branchComponentBuffer[i] = branchComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loops through the active tasks to determine if the system should stay active for the current tick.
|
||||
/// </summary>
|
||||
[UpdateInGroup(typeof(TraversalSystemGroup))]
|
||||
[UpdateAfter(typeof(EvaluationSystem))]
|
||||
public partial struct DetermineEvaluationSystem : ISystem
|
||||
{
|
||||
[Tooltip("Should the group stay active? An inactive tree does not run.")]
|
||||
public bool Active { get; private set; }
|
||||
[Tooltip("Should the group be evaluated? This bool indicates if the entire tree should be evaluated instead of the reevaluation" +
|
||||
"concept for conditional aborts. The tree will be reevaluated if any of the leaf tasks have a status of running.")]
|
||||
public bool Evaluate { get; private set; }
|
||||
|
||||
private bool m_JobScheduled;
|
||||
private JobHandle m_Dependency;
|
||||
|
||||
private EntityQuery m_Query32;
|
||||
private EntityQuery m_Query64;
|
||||
private EntityQuery m_Query128;
|
||||
private EntityQuery m_Query512;
|
||||
private EntityQuery m_Query4096;
|
||||
private EntityCommandBuffer m_EntityCommandBuffer32;
|
||||
private EntityCommandBuffer m_EntityCommandBuffer64;
|
||||
private EntityCommandBuffer m_EntityCommandBuffer128;
|
||||
private EntityCommandBuffer m_EntityCommandBuffer512;
|
||||
private EntityCommandBuffer m_EntityCommandBuffer4096;
|
||||
|
||||
private NativeArray<bool> m_Results;
|
||||
|
||||
/// <summary>
|
||||
/// The system has been created.
|
||||
/// </summary>
|
||||
/// <param name="state">The state of the system.</param>
|
||||
private void OnCreate(ref SystemState state)
|
||||
{
|
||||
Active = Evaluate = true;
|
||||
m_JobScheduled = false;
|
||||
m_Query32 = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAll<TaskComponent, EvaluationComponent32, EvaluateFlag>().WithAbsent<BakedBehaviorTree>().Build();
|
||||
m_Query64 = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAll<TaskComponent, EvaluationComponent64, EvaluateFlag>().WithAbsent<BakedBehaviorTree>().Build();
|
||||
m_Query128 = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAll<TaskComponent, EvaluationComponent128, EvaluateFlag>().WithAbsent<BakedBehaviorTree>().Build();
|
||||
m_Query512 = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAll<TaskComponent, EvaluationComponent512, EvaluateFlag>().WithAbsent<BakedBehaviorTree>().Build();
|
||||
m_Query4096 = SystemAPI.QueryBuilder().WithAllRW<BranchComponent>().WithAll<TaskComponent, EvaluationComponent4096, EvaluateFlag>().WithAbsent<BakedBehaviorTree>().Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the job to determine if the system should stay active and evaluating.
|
||||
/// </summary>
|
||||
/// <param name="state">The current state of the system.</param>
|
||||
[BurstCompile]
|
||||
private void OnUpdate(ref SystemState state)
|
||||
{
|
||||
Active = Evaluate = true;
|
||||
m_JobScheduled = true;
|
||||
|
||||
m_EntityCommandBuffer32 = new EntityCommandBuffer(Allocator.TempJob);
|
||||
m_EntityCommandBuffer64 = new EntityCommandBuffer(Allocator.TempJob);
|
||||
m_EntityCommandBuffer128 = new EntityCommandBuffer(Allocator.TempJob);
|
||||
m_EntityCommandBuffer512 = new EntityCommandBuffer(Allocator.TempJob);
|
||||
m_EntityCommandBuffer4096 = new EntityCommandBuffer(Allocator.TempJob);
|
||||
|
||||
m_Results = new NativeArray<bool>(3, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||||
for (int i = 0; i < m_Results.Length; ++i) {
|
||||
m_Results[i] = false;
|
||||
}
|
||||
|
||||
// Chain jobs sequentially since they all write to the shared Results array.
|
||||
m_Dependency = new DetermineEvaluationJob32()
|
||||
{
|
||||
EntityCommandBuffer = m_EntityCommandBuffer32.AsParallelWriter(),
|
||||
Results = m_Results
|
||||
}.ScheduleParallel(m_Query32, state.Dependency);
|
||||
m_Dependency = new DetermineEvaluationJob64()
|
||||
{
|
||||
EntityCommandBuffer = m_EntityCommandBuffer64.AsParallelWriter(),
|
||||
Results = m_Results
|
||||
}.ScheduleParallel(m_Query64, m_Dependency);
|
||||
m_Dependency = new DetermineEvaluationJob128()
|
||||
{
|
||||
EntityCommandBuffer = m_EntityCommandBuffer128.AsParallelWriter(),
|
||||
Results = m_Results
|
||||
}.ScheduleParallel(m_Query128, m_Dependency);
|
||||
m_Dependency = new DetermineEvaluationJob512()
|
||||
{
|
||||
EntityCommandBuffer = m_EntityCommandBuffer512.AsParallelWriter(),
|
||||
Results = m_Results
|
||||
}.ScheduleParallel(m_Query512, m_Dependency);
|
||||
m_Dependency = new DetermineEvaluationJob4096()
|
||||
{
|
||||
EntityCommandBuffer = m_EntityCommandBuffer4096.AsParallelWriter(),
|
||||
Results = m_Results
|
||||
}.ScheduleParallel(m_Query4096, m_Dependency);
|
||||
|
||||
state.Dependency = m_Dependency;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completes the job and releases any memory.
|
||||
/// </summary>
|
||||
/// <param name="entityManager">The running EntityManager.</param>
|
||||
[BurstCompile]
|
||||
public void Complete(EntityManager entityManager)
|
||||
{
|
||||
if (!m_JobScheduled) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Dependency.Complete();
|
||||
m_EntityCommandBuffer32.Playback(entityManager);
|
||||
m_EntityCommandBuffer32.Dispose();
|
||||
m_EntityCommandBuffer64.Playback(entityManager);
|
||||
m_EntityCommandBuffer64.Dispose();
|
||||
m_EntityCommandBuffer128.Playback(entityManager);
|
||||
m_EntityCommandBuffer128.Dispose();
|
||||
m_EntityCommandBuffer512.Playback(entityManager);
|
||||
m_EntityCommandBuffer512.Dispose();
|
||||
m_EntityCommandBuffer4096.Playback(entityManager);
|
||||
m_EntityCommandBuffer4096.Dispose();
|
||||
|
||||
if (m_Results.IsCreated) {
|
||||
if (m_Results[0]) {
|
||||
Active = m_Results[1];
|
||||
Evaluate = m_Results[2];
|
||||
} else {
|
||||
// If the first element is false then no trees executed.
|
||||
Active = Evaluate = false;
|
||||
}
|
||||
m_Results.Dispose();
|
||||
}
|
||||
m_JobScheduled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The system has been destroyed.
|
||||
/// </summary>
|
||||
/// <param name="state">The current state of the system.</param>
|
||||
private void OnDestroy(ref SystemState state)
|
||||
{
|
||||
if (m_Dependency.IsCompleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Results.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
public partial struct DetermineEvaluationJob32 : IJobEntity
|
||||
{
|
||||
[Tooltip("CommandBuffer which sets the component data.")]
|
||||
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
|
||||
[Tooltip("The computed results.")]
|
||||
[NativeDisableParallelForRestriction] public NativeArray<bool> Results;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the job.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param>
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
/// <param name="evaluationComponent">The EvaluationComponent that belongs to the entity.</param>
|
||||
[BurstCompile]
|
||||
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, in DynamicBuffer<TaskComponent> taskComponents, ref EvaluationComponent32 evaluationComponent)
|
||||
{
|
||||
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
|
||||
EvaluationUtility.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
|
||||
evaluationComponent.EvaluatedTasks = evaluatedTasks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
public partial struct DetermineEvaluationJob64 : IJobEntity
|
||||
{
|
||||
[Tooltip("CommandBuffer which sets the component data.")]
|
||||
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
|
||||
[Tooltip("The computed results.")]
|
||||
[NativeDisableParallelForRestriction] public NativeArray<bool> Results;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the job.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param>
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
/// <param name="evaluationComponent">The EvaluationComponent that belongs to the entity.</param>
|
||||
[BurstCompile]
|
||||
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, in DynamicBuffer<TaskComponent> taskComponents, ref EvaluationComponent64 evaluationComponent)
|
||||
{
|
||||
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
|
||||
EvaluationUtility.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
|
||||
evaluationComponent.EvaluatedTasks = evaluatedTasks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
public partial struct DetermineEvaluationJob128 : IJobEntity
|
||||
{
|
||||
[Tooltip("CommandBuffer which sets the component data.")]
|
||||
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
|
||||
[Tooltip("The computed results.")]
|
||||
[NativeDisableParallelForRestriction] public NativeArray<bool> Results;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the job.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param>
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
/// <param name="evaluationComponent">The EvaluationComponent that belongs to the entity.</param>
|
||||
[BurstCompile]
|
||||
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, in DynamicBuffer<TaskComponent> taskComponents, ref EvaluationComponent128 evaluationComponent)
|
||||
{
|
||||
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
|
||||
EvaluationUtility.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
|
||||
evaluationComponent.EvaluatedTasks = evaluatedTasks;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
public partial struct DetermineEvaluationJob512 : IJobEntity
|
||||
{
|
||||
[Tooltip("CommandBuffer which sets the component data.")]
|
||||
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
|
||||
[Tooltip("The computed results.")]
|
||||
[NativeDisableParallelForRestriction] public NativeArray<bool> Results;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the job.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param>
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
/// <param name="evaluationComponent">The EvaluationComponent that belongs to the entity.</param>
|
||||
[BurstCompile]
|
||||
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, in DynamicBuffer<TaskComponent> taskComponents, ref EvaluationComponent512 evaluationComponent)
|
||||
{
|
||||
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
|
||||
EvaluationUtility.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
|
||||
evaluationComponent.EvaluatedTasks = evaluatedTasks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
public partial struct DetermineEvaluationJob4096 : IJobEntity
|
||||
{
|
||||
[Tooltip("CommandBuffer which sets the component data.")]
|
||||
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
|
||||
[Tooltip("The computed results.")]
|
||||
[NativeDisableParallelForRestriction] public NativeArray<bool> Results;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the job.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param>
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
/// <param name="evaluationComponent">The EvaluationComponent that belongs to the entity.</param>
|
||||
[BurstCompile]
|
||||
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, in DynamicBuffer<TaskComponent> taskComponents, ref EvaluationComponent4096 evaluationComponent)
|
||||
{
|
||||
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
|
||||
EvaluationUtility.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
|
||||
evaluationComponent.EvaluatedTasks = evaluatedTasks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility functions for the task evaluation.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
public struct EvaluationUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Is the task at the specified index a parent task.
|
||||
/// </summary>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
/// <param name="index">The index to check if it is a parent.</param>
|
||||
/// <returns>True if the task at the specified index is a parent task.</returns>
|
||||
[BurstCompile]
|
||||
public static bool IsParentTask(ref DynamicBuffer<TaskComponent> taskComponents, int index)
|
||||
{
|
||||
// The last task cannot be a parent.
|
||||
if (index == taskComponents.Length - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The next child will have a parent of the current task.
|
||||
if (taskComponents[index + 1].ParentIndex == index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The parent index is different - the current task is not a parent.
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core evaluation logic that works with any FixedList type for EvaluatedTasks.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFixedList">The type of FixedList for EvaluatedTasks.</typeparam>
|
||||
/// <param name="entity">The entity that is being acted upon.</param>
|
||||
/// <param name="entityIndex">The index of the entity.</param>
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="taskComponents">An array of task components.</param>
|
||||
/// <param name="evaluatedTasks">The evaluated tasks list.</param>
|
||||
/// <param name="evaluationType">The evaluation type.</param>
|
||||
/// <param name="maxEvaluationCount">The maximum evaluation count.</param>
|
||||
/// <param name="entityCommandBuffer">The command buffer for setting component data.</param>
|
||||
/// <param name="results">The computed results array.</param>
|
||||
[BurstCompile]
|
||||
public static void DetermineEvaluation<TFixedList>(Entity entity, int entityIndex, ref DynamicBuffer<BranchComponent> branchComponents, DynamicBuffer<TaskComponent> taskComponents, ref TFixedList evaluatedTasks, EvaluationType evaluationType, ushort maxEvaluationCount, EntityCommandBuffer.ParallelWriter entityCommandBuffer, NativeArray<bool> results) where TFixedList : struct, INativeList<ulong>
|
||||
{
|
||||
results[0] = true; // The first element indicates that the job has been executed.
|
||||
|
||||
// No branches may be active.
|
||||
var active = false;
|
||||
var evaluate = false;
|
||||
var evaluatedMask = new FixedList4096Bytes<ulong>();
|
||||
for (int i = 0; i < branchComponents.Length; ++i) {
|
||||
var branchComponent = branchComponents[i];
|
||||
if (branchComponent.ActiveIndex == ushort.MaxValue || !branchComponent.CanExecute) {
|
||||
continue;
|
||||
}
|
||||
active = true;
|
||||
|
||||
// Interrupts are processed in a separate system that is run outside of the task execution system. As a result the branch should not continue to evaluate.
|
||||
if (branchComponent.InterruptType != InterruptType.None) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var taskComponent = taskComponents[branchComponent.ActiveIndex];
|
||||
var isParentTask = EvaluationUtility.IsParentTask(ref taskComponents, branchComponent.ActiveIndex);
|
||||
// The branch can evaluate if the active task is an outer node (action or conditional) and is not running OR
|
||||
// the task is an inner node (composite or decorator), is running, and is not a parallel task. Parent tasks cannot run without an active child.
|
||||
if ((!isParentTask && taskComponent.Status != TaskStatus.Running && taskComponent.ParentIndex != ushort.MaxValue) ||
|
||||
(isParentTask && (taskComponent.Status == TaskStatus.Queued || taskComponent.Status == TaskStatus.Running))) {
|
||||
|
||||
// Compute active task bit positions.
|
||||
var bitIndex = branchComponent.ActiveIndex + 1;
|
||||
var arrayIndex = bitIndex / ComponentUtility.ulongBitSize;
|
||||
var bitInUlong = bitIndex % ComponentUtility.ulongBitSize;
|
||||
while (evaluatedMask.Length <= arrayIndex) evaluatedMask.Add(0UL);
|
||||
evaluatedMask[arrayIndex] |= (1UL << bitInUlong);
|
||||
|
||||
// Prevent evaluating the same task again within the same tick.
|
||||
if (branchComponent.ActiveIndex == branchComponent.LastActiveIndex) {
|
||||
branchComponent.CanExecute = false;
|
||||
branchComponents.ElementAt(i) = branchComponent;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the task has already been evaluated this tick.
|
||||
var alreadyEvaluated = (evaluatedTasks[arrayIndex] & (1UL << bitInUlong)) != 0;
|
||||
|
||||
// Decision to evaluate:
|
||||
// - For parent tasks: always evaluate. The parent task should never be the last executing task.
|
||||
// - For non-parent tasks: evaluate if this task hasn't been evaluated yet.
|
||||
if (isParentTask || !alreadyEvaluated) {
|
||||
evaluate = true;
|
||||
branchComponent.LastActiveIndex = branchComponent.ActiveIndex;
|
||||
} else {
|
||||
branchComponent.CanExecute = false;
|
||||
}
|
||||
branchComponents.ElementAt(i) = branchComponent;
|
||||
|
||||
evaluatedTasks[arrayIndex] |= evaluatedMask[arrayIndex];
|
||||
} else {
|
||||
branchComponent.CanExecute = false;
|
||||
branchComponents.ElementAt(i) = branchComponent;
|
||||
}
|
||||
}
|
||||
|
||||
// If a branch is active then at least one task within that branch is active.
|
||||
if (active) {
|
||||
results[1] = true; // Active result.
|
||||
|
||||
if (evaluate) {
|
||||
if (evaluationType == EvaluationType.Count) {
|
||||
// Use the last element of EvaluatedTasks as the counter.
|
||||
evaluatedTasks[evaluatedTasks.Length - 1]++;
|
||||
if (evaluatedTasks[evaluatedTasks.Length - 1] >= maxEvaluationCount) {
|
||||
// Reset the counter and bitmask elements.
|
||||
for (int i = 0; i < evaluatedTasks.Length; ++i) {
|
||||
evaluatedTasks[i] = 0;
|
||||
}
|
||||
entityCommandBuffer.SetComponentEnabled<EvaluateFlag>(entityIndex, entity, false);
|
||||
// Set the bitmask for current active tasks to prevent one extra task from being executed on subsequent frames.
|
||||
SetActiveBranchBits(ref branchComponents, ref evaluatedTasks);
|
||||
} else {
|
||||
results[2] = true; // Evaluate result.
|
||||
}
|
||||
} else {
|
||||
results[2] = true; // Evaluate result - continue the loop.
|
||||
}
|
||||
} else {
|
||||
entityCommandBuffer.SetComponentEnabled<EvaluateFlag>(entityIndex, entity, false);
|
||||
// Reset the evaluated tasks bitmask.
|
||||
for (int i = 0; i < evaluatedTasks.Length; ++i) {
|
||||
evaluatedTasks[i] = 0;
|
||||
}
|
||||
// The system is going to stop evaluating this entity. It will be resumed immediately the next update. Because the DetermineEvaluationJob is run after the tasks
|
||||
// update the EvaluatedTasks value should be set to the next active task. If this value is set to 0 then one extra task will always be executed with subsequent frames.
|
||||
SetActiveBranchBits(ref branchComponents, ref evaluatedTasks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the bitmask bits for all active branches. This prevents one extra task from being executed on subsequent frames.
|
||||
/// </summary>
|
||||
/// <param name="branchComponents">An array of branch components.</param>
|
||||
/// <param name="evaluatedTasks">The evaluated tasks list to update.</param>
|
||||
[BurstCompile]
|
||||
private static void SetActiveBranchBits<TFixedList>(ref DynamicBuffer<BranchComponent> branchComponents, ref TFixedList evaluatedTasks) where TFixedList : struct, INativeList<ulong>
|
||||
{
|
||||
for (int i = 0; i < branchComponents.Length; ++i) {
|
||||
var branchComponent = branchComponents[i];
|
||||
if (branchComponent.ActiveIndex == ushort.MaxValue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute active task bit positions.
|
||||
var bitIndex = branchComponent.ActiveIndex + 1;
|
||||
var arrayIndex = bitIndex / ComponentUtility.ulongBitSize;
|
||||
var bitInUlong = bitIndex % ComponentUtility.ulongBitSize;
|
||||
evaluatedTasks[arrayIndex] |= (1UL << bitInUlong);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e38e2be36422f54fa65b67063d03366
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user