/// --------------------------------------------- /// Behavior Designer /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.BehaviorDesigner.Samples { using Opsive.BehaviorDesigner.Runtime.Components; using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.GraphDesigner.Runtime.Variables; using Opsive.GraphDesigner.Runtime.Variables.ECS; using Unity.Burst; using Unity.Entities; using Unity.Collections; using Unity.Mathematics; using Unity.Transforms; using UnityEngine; [Opsive.Shared.Utility.Description("Uses DOTS to move towards the center point, returns success when the agent is less than the arrive distance.")] [Shared.Utility.Category("Behavior Designer Samples/DOTS")] public class Charge : ECSActionTask { [Tooltip("The speed of the agent.")] [SerializeField] SharedVariable m_Speed; [Tooltip("The distance away from the target when the agent has arrived at the target.")] [SerializeField] float m_ArriveDistance; private ECSSharedVariableIndex m_SpeedIndex; /// /// Resets the task to its default values. /// public override void Reset() { m_Speed = new SharedVariable { Value = 10f }; m_ArriveDistance = 0.1f; } /// /// Registers the speed SharedVariable and adds the buffer element to the entity. /// /// The world that the entity exists in. /// The entity that the IBufferElementData should be assigned to. /// The ECS variable registry for registering SharedVariable fields. /// The GameObject that the entity is attached to. /// The index of the element within the buffer. public override int AddBufferElement(World world, Entity entity, ECSVariableRegistry registry, GameObject gameObject) { m_SpeedIndex = new ECSSharedVariableIndex(registry.Register(m_Speed)); return base.AddBufferElement(world, entity, registry, gameObject); } /// /// Returns a new TBufferElement for use by the system. /// /// A new TBufferElement for use by the system. public override ChargeComponent GetBufferElement() { return new ChargeComponent() { Index = RuntimeIndex, SpeedVariableIndex = m_SpeedIndex.Index, ArriveDistance = m_ArriveDistance, }; } } /// /// The DOTS data structure for the Charge struct. /// public struct ChargeComponent : IBufferElementData { [Tooltip("The index of the node.")] public ushort Index; [Tooltip("Buffer index into SharedVariableElement for the agent's speed.")] public int SpeedVariableIndex; [Tooltip("The distance away from the target when the agent has arrived at the target.")] public float ArriveDistance; } /// /// A DOTS flag indicating when a Charge node is active. /// public struct ChargeFlag : IComponentData, IEnableableComponent { } /// /// Runs the Charge logic. /// [DisableAutoCreation] public partial struct ChargeTaskSystem : ISystem { private EntityQuery m_ChargeQuery; /// /// Creates the required objects for use within the job system. /// /// The current SystemState. [BurstCompile] private void OnCreate(ref SystemState state) { m_ChargeQuery = new EntityQueryBuilder(Allocator.Temp) .WithAllRW().WithAllRW().WithAllRW() .WithAll() .Build(ref state); } /// /// Creates the job. /// /// The current state of the system. [BurstCompile] private void OnUpdate(ref SystemState state) { state.Dependency = new ChargeJob() { DeltaTime = SystemAPI.Time.DeltaTime, }.ScheduleParallel(m_ChargeQuery, state.Dependency); } /// /// Charges towards the center. /// [BurstCompile] private partial struct ChargeJob : IJobEntity { [Tooltip("The current frame's DeltaTime.")] public float DeltaTime; /// /// Updates the logic. /// /// An array of BranchComponents. /// An array of TaskComponents. /// An array of ChargeComponents. /// The shared variable buffer for this entity. /// The entity's transform. [BurstCompile] public void Execute(ref DynamicBuffer branchComponents, ref DynamicBuffer taskComponents, ref DynamicBuffer chargeComponents, DynamicBuffer sharedVariables, ref LocalTransform transform) { for (int i = 0; i < chargeComponents.Length; ++i) { var chargeComponent = chargeComponents[i]; var taskComponent = taskComponents[chargeComponent.Index]; var branchComponent = branchComponents[taskComponent.BranchIndex]; if (!branchComponent.CanExecute) { continue; } if (taskComponent.Status == TaskStatus.Queued) { taskComponent.Status = TaskStatus.Running; taskComponents[chargeComponent.Index] = taskComponent; } if (taskComponent.Status != TaskStatus.Running) { continue; } // The task should return success as soon as the agent has arrived. var direction = -transform.Position; if (math.length(direction) < chargeComponent.ArriveDistance) { taskComponent.Status = TaskStatus.Success; taskComponents[chargeComponent.Index] = taskComponent; continue; } // Read the speed from the shared variable buffer and move toward the center. var speed = sharedVariables.Get(chargeComponent.SpeedVariableIndex); transform.Position += (direction * speed * DeltaTime); } } } } }