/// --------------------------------------------- /// 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; using Unity.Burst; using Unity.Entities; using Unity.Collections; using Unity.Mathematics; using Unity.Transforms; using UnityEngine; [Opsive.Shared.Utility.Description("Uses DOTS to rotate around the center. This task will always return a status of running.")] [Shared.Utility.Category("Behavior Designer Samples/DOTS")] public class Swarm : ECSActionTask { [Tooltip("The angular speed of the agent.")] [SerializeField] float m_AngularSpeed; /// /// The type of flag that should be enabled when the task is running. /// public override ComponentType Flag { get => typeof(SwarmFlag); } /// /// Returns a new TBufferElement for use by the system. /// /// A new TBufferElement for use by the system. public override SwarmComponent GetBufferElement() { return new SwarmComponent() { Index = RuntimeIndex, AngularSpeed = m_AngularSpeed, }; } /// /// Resets the task to its default values. /// public override void Reset() { m_AngularSpeed = 2; } } /// /// The DOTS data structure for the Swarm struct. /// public struct SwarmComponent : IBufferElementData { [Tooltip("The index of the node.")] public ushort Index; [Tooltip("The angular speed of the agent.")] public float AngularSpeed; } /// /// A DOTS flag indicating when a Swarm node is active. /// public struct SwarmFlag : IComponentData, IEnableableComponent { } /// /// Runs the Swarm logic. /// [DisableAutoCreation] public partial struct SwarmTaskSystem : ISystem { private EntityQuery m_SwarmQuery; /// /// Creates the required objects for use within the job system. /// /// The current SystemState. [BurstCompile] private void OnCreate(ref SystemState state) { m_SwarmQuery = new EntityQueryBuilder(Allocator.Temp) .WithAllRW().WithAllRW().WithAllRW() .WithAll() .Build(ref state); } /// /// Updates the logic. /// /// The current state of the system. [BurstCompile] private void OnUpdate(ref SystemState state) { var deltaTime = SystemAPI.Time.DeltaTime; state.Dependency = new SwarmJob() { DeltaTime = SystemAPI.Time.DeltaTime, }.ScheduleParallel(m_SwarmQuery, state.Dependency); } /// /// Rotates around the center. /// [BurstCompile] private partial struct SwarmJob : IJobEntity { [Tooltip("The current frame's DeltaTime.")] public float DeltaTime; /// /// Updates the logic. /// /// An array of BranchComponents. /// An array of TaskComponents. /// An array of SwarmComponents. /// The entity's transform. [BurstCompile] public void Execute(ref DynamicBuffer branchComponents, ref DynamicBuffer taskComponents, ref DynamicBuffer swarmComponents, ref LocalTransform transform) { for (int i = 0; i < swarmComponents.Length; ++i) { var swarmComponent = swarmComponents[i]; var taskComponent = taskComponents[swarmComponent.Index]; var branchComponent = branchComponents[taskComponent.BranchIndex]; if (!branchComponent.CanExecute) { continue; } if (taskComponent.Status == TaskStatus.Queued) { taskComponent.Status = TaskStatus.Running; taskComponents[swarmComponent.Index] = taskComponent; } // Always swarm. if (taskComponent.Status != TaskStatus.Running) { continue; } var dist = math.length(transform.Position); var radians = GetAngle(transform.Position); radians += swarmComponent.AngularSpeed * DeltaTime; transform.Position.x = math.cos(radians) * dist; transform.Position.z = math.sin(radians) * dist; } } /// /// Returns the angle between the target point and the center. /// /// The target point. /// The angle between the target point and the center. This will be in the range of 0 - 2PI (radians). [BurstCompile] private float GetAngle(float3 target) { var n = 270 - (math.atan2(-target.x, -target.z)) * 180 / math.PI; return (n % 360) * math.TORADIANS; } } } }