#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