#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.Core;
using Opsive.BehaviorDesigner.Runtime.Groups;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using UnityEngine;
///
/// Traverses and ensures the correct tasks are active.
///
[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;
///
/// The system has been created.
///
/// The state of the system.
private void OnCreate(ref SystemState state)
{
m_JobScheduled = false;
m_Query = SystemAPI.QueryBuilder().WithAllRW().WithAbsent().Build();
}
///
/// Starts the job which traverses the tree.
///
/// The current state of the system.
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);
}
///
/// Completes the job and releases any memory.
///
/// The running EntityManager.
/// Has the system been stopped?
#if !UNITY_EDITOR
[BurstCompile]
#endif
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;
}
///
/// Job which traverses the tree.
///
#if !UNITY_EDITOR
[BurstCompile]
#endif
public partial struct EvaluationJob : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
///
/// Executes the job.
///
/// The entity that is being acted upon.
/// The index of the entity.An array of branch components.
/// An array of task components.
#if !UNITY_EDITOR
[BurstCompile]
#endif
public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer branchComponents, ref DynamicBuffer taskComponents)
{
BehaviorTraversalCore.Evaluate(entity, entityIndex, ref branchComponents, ref taskComponents, EntityCommandBuffer);
}
}
}
///
/// Loops through the active tasks to determine if the system should stay active for the current tick.
///
[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_EntityCommandBuffer;
private NativeArray m_Results;
///
/// The system has been created.
///
/// The state of the system.
private void OnCreate(ref SystemState state)
{
Active = Evaluate = true;
m_JobScheduled = false;
m_Query32 = SystemAPI.QueryBuilder().WithAllRW().WithAll().WithAbsent().Build();
m_Query64 = SystemAPI.QueryBuilder().WithAllRW().WithAll().WithAbsent().Build();
m_Query128 = SystemAPI.QueryBuilder().WithAllRW().WithAll().WithAbsent().Build();
m_Query512 = SystemAPI.QueryBuilder().WithAllRW().WithAll().WithAbsent().Build();
m_Query4096 = SystemAPI.QueryBuilder().WithAllRW().WithAll().WithAbsent().Build();
}
///
/// Executes the job to determine if the system should stay active and evaluating.
///
/// The current state of the system.
#if !UNITY_EDITOR
[BurstCompile]
#endif
private void OnUpdate(ref SystemState state)
{
Active = Evaluate = true;
m_JobScheduled = true;
if (m_Query32.IsEmptyIgnoreFilter && m_Query64.IsEmptyIgnoreFilter && m_Query128.IsEmptyIgnoreFilter && m_Query512.IsEmptyIgnoreFilter && m_Query4096.IsEmptyIgnoreFilter) {
Active = Evaluate = false;
m_JobScheduled = false;
return;
}
m_EntityCommandBuffer = new EntityCommandBuffer(Allocator.TempJob);
m_Results = new NativeArray(3, Allocator.TempJob, NativeArrayOptions.ClearMemory);
var entityCommandBufferParallelWriter = m_EntityCommandBuffer.AsParallelWriter();
// Chain jobs sequentially since they all write to the shared Results array.
m_Dependency = new DetermineEvaluationJob32()
{
EntityCommandBuffer = entityCommandBufferParallelWriter,
Results = m_Results
}.ScheduleParallel(m_Query32, state.Dependency);
m_Dependency = new DetermineEvaluationJob64()
{
EntityCommandBuffer = entityCommandBufferParallelWriter,
Results = m_Results
}.ScheduleParallel(m_Query64, m_Dependency);
m_Dependency = new DetermineEvaluationJob128()
{
EntityCommandBuffer = entityCommandBufferParallelWriter,
Results = m_Results
}.ScheduleParallel(m_Query128, m_Dependency);
m_Dependency = new DetermineEvaluationJob512()
{
EntityCommandBuffer = entityCommandBufferParallelWriter,
Results = m_Results
}.ScheduleParallel(m_Query512, m_Dependency);
m_Dependency = new DetermineEvaluationJob4096()
{
EntityCommandBuffer = entityCommandBufferParallelWriter,
Results = m_Results
}.ScheduleParallel(m_Query4096, m_Dependency);
state.Dependency = m_Dependency;
}
///
/// Completes the job and releases any memory.
///
/// The running EntityManager.
#if !UNITY_EDITOR
[BurstCompile]
#endif
public void Complete(EntityManager entityManager)
{
if (!m_JobScheduled) {
return;
}
m_Dependency.Complete();
if (m_EntityCommandBuffer.IsCreated) {
m_EntityCommandBuffer.Playback(entityManager);
m_EntityCommandBuffer.Dispose();
m_EntityCommandBuffer = default;
}
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_Results = default;
}
m_JobScheduled = false;
}
///
/// The system has been destroyed.
///
/// The current state of the system.
private void OnDestroy(ref SystemState state)
{
if (!m_JobScheduled) {
return;
}
// During world teardown these containers may already be released by ECS internals.
m_Dependency.Complete();
m_EntityCommandBuffer = default;
m_Results = default;
m_JobScheduled = false;
}
///
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
///
#if !UNITY_EDITOR
[BurstCompile]
#endif
public partial struct DetermineEvaluationJob32 : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
[Tooltip("The computed results.")]
[NativeDisableParallelForRestriction] public NativeArray Results;
///
/// Executes the job.
///
/// The entity that is being acted upon.
/// The index of the entity.
/// An array of branch components.
/// An array of task components.
/// The EvaluationComponent that belongs to the entity.
#if !UNITY_EDITOR
[BurstCompile]
#endif
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer branchComponents, in DynamicBuffer taskComponents, ref EvaluationComponent32 evaluationComponent)
{
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
BehaviorTraversalCore.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
evaluationComponent.EvaluatedTasks = evaluatedTasks;
}
}
///
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
///
#if !UNITY_EDITOR
[BurstCompile]
#endif
public partial struct DetermineEvaluationJob64 : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
[Tooltip("The computed results.")]
[NativeDisableParallelForRestriction] public NativeArray Results;
///
/// Executes the job.
///
/// The entity that is being acted upon.
/// The index of the entity.
/// An array of branch components.
/// An array of task components.
/// The EvaluationComponent that belongs to the entity.
#if !UNITY_EDITOR
[BurstCompile]
#endif
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer branchComponents, in DynamicBuffer taskComponents, ref EvaluationComponent64 evaluationComponent)
{
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
BehaviorTraversalCore.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
evaluationComponent.EvaluatedTasks = evaluatedTasks;
}
}
///
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
///
#if !UNITY_EDITOR
[BurstCompile]
#endif
public partial struct DetermineEvaluationJob128 : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
[Tooltip("The computed results.")]
[NativeDisableParallelForRestriction] public NativeArray Results;
///
/// Executes the job.
///
/// The entity that is being acted upon.
/// The index of the entity.
/// An array of branch components.
/// An array of task components.
/// The EvaluationComponent that belongs to the entity.
#if !UNITY_EDITOR
[BurstCompile]
#endif
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer branchComponents, in DynamicBuffer taskComponents, ref EvaluationComponent128 evaluationComponent)
{
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
BehaviorTraversalCore.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
evaluationComponent.EvaluatedTasks = evaluatedTasks;
}
}
///
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
///
#if !UNITY_EDITOR
[BurstCompile]
#endif
public partial struct DetermineEvaluationJob512 : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
[Tooltip("The computed results.")]
[NativeDisableParallelForRestriction] public NativeArray Results;
///
/// Executes the job.
///
/// The entity that is being acted upon.
/// The index of the entity.
/// An array of branch components.
/// An array of task components.
/// The EvaluationComponent that belongs to the entity.
#if !UNITY_EDITOR
[BurstCompile]
#endif
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer branchComponents, in DynamicBuffer taskComponents, ref EvaluationComponent512 evaluationComponent)
{
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
BehaviorTraversalCore.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
evaluationComponent.EvaluatedTasks = evaluatedTasks;
}
}
///
/// Job which determine if the system should stay active. If any behavior tree should stay active then the entire system must remain active.
///
#if !UNITY_EDITOR
[BurstCompile]
#endif
public partial struct DetermineEvaluationJob4096 : IJobEntity
{
[Tooltip("CommandBuffer which sets the component data.")]
public EntityCommandBuffer.ParallelWriter EntityCommandBuffer;
[Tooltip("The computed results.")]
[NativeDisableParallelForRestriction] public NativeArray Results;
///
/// Executes the job.
///
/// The entity that is being acted upon.
/// The index of the entity.
/// An array of branch components.
/// An array of task components.
/// The EvaluationComponent that belongs to the entity.
#if !UNITY_EDITOR
[BurstCompile]
#endif
private void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer branchComponents, in DynamicBuffer taskComponents, ref EvaluationComponent4096 evaluationComponent)
{
var evaluatedTasks = evaluationComponent.EvaluatedTasks;
BehaviorTraversalCore.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationComponent.EvaluationType, evaluationComponent.MaxEvaluationCount, EntityCommandBuffer, Results);
evaluationComponent.EvaluatedTasks = evaluatedTasks;
}
}
}
///
/// Utility functions for the task evaluation.
///
public struct EvaluationUtility
{
///
/// Core evaluation logic that works with any FixedList type for EvaluatedTasks.
///
/// The type of FixedList for EvaluatedTasks.
/// The entity that is being acted upon.
/// The index of the entity.
/// An array of branch components.
/// An array of task components.
/// The evaluated tasks list.
/// The evaluation type.
/// The maximum evaluation count.
/// The command buffer for setting component data.
/// The computed results array.
public static void DetermineEvaluation(Entity entity, int entityIndex, ref DynamicBuffer branchComponents, DynamicBuffer taskComponents, ref TFixedList evaluatedTasks, EvaluationType evaluationType, ushort maxEvaluationCount, EntityCommandBuffer.ParallelWriter entityCommandBuffer, NativeArray results) where TFixedList : struct, INativeList
{
BehaviorTraversalCore.DetermineEvaluation(entity, entityIndex, ref branchComponents, taskComponents, ref evaluatedTasks, evaluationType, maxEvaluationCount, entityCommandBuffer, results);
}
///
/// Is the task at the specified index a parent task.
///
/// An array of task components.
/// The index to check if it is a parent.
/// True if the task at the specified index is a parent task.
public static bool IsParentTask(ref DynamicBuffer taskComponents, int index)
{
return BehaviorTraversalCore.IsParentTask(ref taskComponents, index);
}
}
}
#endif