using System.Collections; using System.Collections.Generic; using UnityEngine; [System.Serializable] public class OrbitalPosition { public int m_AttachedRing; public float m_DistanceFromCentre; public float m_DistanceAlongRadius; public float m_ObjectRadius = 0.1f; public bool m_IsAttachedToRings = true; public OrbitalPositionBehaviour m_Behaviour; public readonly Transform m_Owner; public float m_SpinSpeed = 1f; public OrbitalPosition(GameObject owner) { m_AttachedRing = GlobalOrbitalPositionManager.LastRingID; m_Owner = owner.transform; } public Vector3 TranslateToVec3() => new ( x: m_DistanceFromCentre * Mathf.Cos(m_DistanceAlongRadius), y: m_DistanceFromCentre * Mathf.Sin(m_DistanceAlongRadius) ); } public class Ring : MonoBehaviour { public int m_ID; } public class GlobalOrbitalPositionManager : MonoBehaviour { private static GlobalOrbitalPositionManager s_Instance = null; private readonly List m_ObjectInstances = new(); private readonly List m_Rings = new(); private readonly Dictionary m_Distances = new(); public static bool IsSimulationRunning = true; private OrbitalPosition m_PlayerOrbitalPosition; public static void SetPlayer(OrbitalPosition player) => s_Instance.m_PlayerOrbitalPosition = player; public static bool AllowPlayerInput { get; private set; } = true; private float m_TimeOfLastRingSpawn = float.NegativeInfinity; private int m_LastGeneratedRing; public static int LastRingID => s_Instance.m_LastGeneratedRing; private const int CirclePoints = 100; private float m_LocalDeltaTimeScale = 1f; private float LocalDeltaTime => Time.deltaTime * m_LocalDeltaTimeScale; private Vector3[] m_PrecalculatedPositions; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void OnApplicationStart() { GameObject manager = new("GLOBAL ORBITAL MANAGER"); DontDestroyOnLoad(manager); s_Instance = manager.AddComponent(); s_Instance.m_PrecalculatedPositions = new Vector3[CirclePoints]; for (int i = 0; i < s_Instance.m_PrecalculatedPositions.Length; i++) { s_Instance.m_PrecalculatedPositions[i] = new Vector3 ( x: Mathf.Cos(Mathf.Deg2Rad * (359f / CirclePoints * i)), y: Mathf.Sin(Mathf.Deg2Rad * (359f / CirclePoints * i)), z: 0 ); } RestartSimulation(); } private static IEnumerator RestartSimulationInternal() { IsSimulationRunning = false; s_Instance.m_LocalDeltaTimeScale = 7f; AllowPlayerInput = false; for (int i = 0; i < 100; i++) { yield return new WaitForFixedUpdate(); if (PlayerController.IsPlayerAttached) s_Instance.m_PlayerOrbitalPosition.m_AttachedRing = s_Instance.m_LastGeneratedRing; } PlayerController.AttachPlayer(); foreach (OrbitalPosition behaviour in s_Instance.m_ObjectInstances) { behaviour.m_Behaviour.OnSimulationRestart(); } yield return new WaitForSeconds(1.3f); AllowPlayerInput = true; s_Instance.m_LocalDeltaTimeScale = 1f; IsSimulationRunning = true; } public static void RestartSimulation() => s_Instance.StartCoroutine(RestartSimulationInternal()); private void Update() { if (Time.time - m_TimeOfLastRingSpawn > (GlobalOrbitalSettings.GapDistance / m_LocalDeltaTimeScale)) { GameObject newRing = Instantiate(GlobalOrbitalSettings.RingPrefab); newRing.transform.localScale = new Vector3(12f, 12f, 1); Ring ring = newRing.AddComponent(); ring.m_ID = m_LastGeneratedRing + 1; m_Rings.Add(ring); LineRenderer lineRenderer = newRing.GetComponentInChildren(); lineRenderer.loop = true; lineRenderer.positionCount = CirclePoints; lineRenderer.startWidth = 0.1f; lineRenderer.endWidth = 0.1f; lineRenderer.material = new Material(Shader.Find("Sprites/Default")); m_LastGeneratedRing = ring.m_ID; m_TimeOfLastRingSpawn = Time.time; } List toRemove = new(); m_Distances.Clear(); foreach (Ring ring in m_Rings) { float diff = LocalDeltaTime * GlobalOrbitalSettings.DistanceSpeed; ring.transform.localScale -= new Vector3(diff, diff, 0f); LineRenderer lineRenderer = ring.GetComponentInChildren(); for (int vert = 0; vert < CirclePoints; vert++) { lineRenderer.SetPosition(vert, m_PrecalculatedPositions[vert] * ring.transform.localScale.x); } float lerp = (ring.transform.localScale.x - 0.5f) / 4f; Color c = Color.Lerp(Color.black, Color.white, Mathf.Clamp01(lerp)); if (ring.transform.localScale.x < 0.5f) toRemove.Add(ring); lineRenderer.startColor = c; lineRenderer.endColor = c; m_Distances[ring.m_ID] = ring.transform.localScale.x; } foreach (Ring ring in toRemove) { m_Rings.Remove(ring); Destroy(ring.gameObject); } foreach (OrbitalPosition orbitalPosition in m_ObjectInstances) { if (!orbitalPosition.m_IsAttachedToRings) continue; if (!m_Distances.ContainsKey(orbitalPosition.m_AttachedRing)) { orbitalPosition.m_AttachedRing = m_LastGeneratedRing; orbitalPosition.m_Behaviour.OnReachCentre(); } float distance = m_Distances[orbitalPosition.m_AttachedRing]; orbitalPosition.m_DistanceFromCentre = distance; orbitalPosition.m_DistanceAlongRadius = (orbitalPosition.m_DistanceAlongRadius + LocalDeltaTime * GlobalOrbitalSettings.RadiusSpeed * orbitalPosition.m_SpinSpeed) % (Mathf.PI * 2); orbitalPosition.m_Owner.position = orbitalPosition.TranslateToVec3(); } } private void FixedUpdate() { foreach (OrbitalPosition orbital in m_ObjectInstances) { if (orbital == m_PlayerOrbitalPosition) continue; Vector2 a = orbital.TranslateToVec3(); Vector2 b = m_PlayerOrbitalPosition.TranslateToVec3(); float distance = (a - b).magnitude; float radii = orbital.m_ObjectRadius + m_PlayerOrbitalPosition.m_ObjectRadius; if (distance < radii) { orbital.m_Behaviour.OnCollision(m_PlayerOrbitalPosition.m_Behaviour); m_PlayerOrbitalPosition.m_Behaviour.OnCollision(orbital.m_Behaviour); } } if (Random.Range(0, 20) == 0 && AllowPlayerInput) Instantiate(GlobalOrbitalSettings.EnemyPrefab); } public static void RegisterOrbitalPositionInstance(OrbitalPosition newInstance) => s_Instance.m_ObjectInstances.Add(newInstance); public static void UnregisterOrbitalPositionInstance(OrbitalPosition removedInstance) => s_Instance.m_ObjectInstances.Remove(removedInstance); } public abstract class OrbitalPositionBehaviour : MonoBehaviour { [SerializeField] protected OrbitalPosition m_OrbitalPosition; private void Start() { m_OrbitalPosition = new OrbitalPosition(gameObject) { m_Behaviour = this }; GlobalOrbitalPositionManager.RegisterOrbitalPositionInstance(m_OrbitalPosition); OnStart(); } private void OnDestroy() => GlobalOrbitalPositionManager.UnregisterOrbitalPositionInstance(m_OrbitalPosition); protected virtual void OnStart() { } public virtual void OnReachCentre() { } public virtual void OnCollision(OrbitalPositionBehaviour other) { } public virtual void OnSimulationRestart() { } }