#if SSC_ENTITIES using System; using Unity.Entities; using Unity.Transforms; using Unity.Mathematics; using Unity.Collections; using Unity.Jobs; using UnityEngine; using Unity.Burst; using Unity.Rendering; // Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved. namespace SciFiShipController { [Serializable] #if UNITY_2020_3_OR_NEWER public partial struct Asteroid : IComponentData #else public struct Asteroid : IComponentData #endif { public float rotationRate; public float3 rotationDirection; } /// /// A DOTS system for creating and rotating asteroids. When Unity.Physics (for DOTS) /// is installed, box colliders are added which can be hit by Projectiles with DOTS enabled. /// NOTE: Ships currently don't detect Entities in a scene. /// Unity 2020.3 LTS or newer requires Entities 0.51-preview.32+ /// #if UNITY_2020_3_OR_NEWER public partial class AsteroidSystem : SystemBase #elif UNITY_2020_1_OR_NEWER public class AsteroidSystem : SystemBase #else public class AsteroidSystem : JobComponentSystem #endif { #region Public variables #endregion #region Private variables private static World asteroidWorld; private static EntityQuery asteroidEntityQuery; private static EntityManager entityManager; private static ComponentType compTypeAsteroid; #endregion #region Structures [BurstCompile] private struct RotationJob : IJobChunk { public float deltaTime; #if UNITY_2020_1_OR_NEWER // Renamed from ArchetypeChunkComponentType to ComponentTypeHandle in Entities 0.12.0 public ComponentTypeHandle rotationType; [ReadOnly] public ComponentTypeHandle asteroidType; #else public ArchetypeChunkComponentType rotationType; [ReadOnly] public ArchetypeChunkComponentType asteroidType; #endif public void Execute(ArchetypeChunk archetypeChunk, int chunkIndex, int firstEntityIndex) { var chunkRotations = archetypeChunk.GetNativeArray(rotationType); var chunkAsteriods = archetypeChunk.GetNativeArray(asteroidType); for (int i = 0; i < archetypeChunk.Count; i++) { Rotation rotation = chunkRotations[i]; Asteroid asteroid = chunkAsteriods[i]; quaternion newQuaternion = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(asteroid.rotationDirection, asteroid.rotationRate * deltaTime)); chunkRotations[i] = new Rotation { Value = newQuaternion }; } } } #endregion #region Event Methods protected override void OnCreate() { //base.OnCreate(); // Get the world that will "hold" the entities. if (asteroidWorld == null) { asteroidWorld = DOTSHelper.GetDefaultWorld(); } if (asteroidWorld != null) { entityManager = asteroidWorld.EntityManager; } // Cache the component type to avoid ComponentType.op_Implicit in CreateAsteroid(..) compTypeAsteroid = typeof(Asteroid); // Asteroid entity query code // Get all the entities with a Translation, Rotation and Asteroid components asteroidEntityQuery = GetEntityQuery(new EntityQueryDesc { All = new ComponentType[] { typeof(Translation), typeof(Rotation), compTypeAsteroid } }); } #endregion #region Update Methods // OnUpdate runs on the main thread. #if UNITY_2020_1_OR_NEWER protected override void OnUpdate() #else protected override JobHandle OnUpdate(JobHandle inputDeps) #endif { #if UNITY_2020_1_OR_NEWER // Replace GetArchetypeChunkComponentType() with GetComponentTypeHandle() in Entities 0.12.0 // Rotation component is read-write because we want to update the rotation of the asteroids each frame var rotationType = GetComponentTypeHandle(); // Asteroid data is read-only var asteroidType = GetComponentTypeHandle(true); #else // Rotation component is read-write because we want to update the rotation of the asteroids each frame var rotationType = GetArchetypeChunkComponentType(); // Asteroid data is read-only var asteroidType = GetArchetypeChunkComponentType(true); #endif RotationJob rotationJob = new RotationJob() { #if UNITY_2019_3_OR_NEWER deltaTime = Time.DeltaTime, #else deltaTime = Time.deltaTime, #endif rotationType = rotationType, asteroidType = asteroidType }; #if UNITY_2020_1_OR_NEWER this.Dependency = rotationJob.ScheduleParallel(asteroidEntityQuery, this.Dependency); #else return rotationJob.Schedule(asteroidEntityQuery, inputDeps); #endif } #endregion #region Public static methods /// /// Create a new asteroid as an entity in the scene. /// Currently has no concept of a collider etc. /// /// /// /// /// /// /// public static void CreateAsteroid(Vector3 position, Quaternion rotation, Vector3 scale, float rotationRate, Vector3 rotationDirection, Entity prefabEntity) { // EntityManager is now a struct (was a class). Gets created and destroyed at same time as the World. if (asteroidWorld.IsCreated) { Entity entity = entityManager.Instantiate(prefabEntity); // Add an Asteroid component if it wasn't attached to the prefab that was converted if (!entityManager.HasComponent(entity, compTypeAsteroid)) { entityManager.AddComponent(entity, compTypeAsteroid); } // Add a scale component if (!entityManager.HasComponent(entity, typeof(NonUniformScale))) { entityManager.AddComponent(entity, typeof(NonUniformScale)); } #if SSC_PHYSICS // Add a unique Unity Physics box collider if (entityManager.HasComponent(entity, typeof(Unity.Physics.PhysicsCollider))) { entityManager.RemoveComponent(entity); } var collider = Unity.Physics.BoxCollider.Create(new Unity.Physics.BoxGeometry { Center = float3.zero, Size = (float3)scale, Orientation = quaternion.identity, BevelRadius = 0.1f }); entityManager.AddComponentData(entity, new Unity.Physics.PhysicsCollider { Value = collider }); #endif // Set their position, rotation and asteroid properties entityManager.SetComponentData(entity, new Translation() { Value = (float3)position }); entityManager.SetComponentData(entity, new Rotation() { Value = (quaternion)rotation }); entityManager.SetComponentData(entity, new NonUniformScale() { Value = (float3)scale }); entityManager.SetComponentData(entity, new Asteroid() { rotationRate = rotationRate, rotationDirection = (float3)rotationDirection }); } } #endregion } } #endif