using System.Collections.Generic; using UnityEngine; public class OrbitalPosition { public int m_AttachedRing; public float m_DistanceFromCentre; public float m_RadiusDistance; public float m_ObjectRadius = 0.1f; public OrbitalPositionBehaviour m_Behaviour; public readonly Transform m_Owner; public int m_SpinSpeed = 1; public OrbitalPosition(GameObject owner) { m_AttachedRing = GlobalOrbitalPositionManager.LastRingID; m_Owner = owner.transform; } public Vector3 TranslateToVec3() => new ( x: m_DistanceFromCentre * Mathf.Cos(m_RadiusDistance), y: m_DistanceFromCentre * Mathf.Sin(m_RadiusDistance) ); } 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(); private OrbitalPosition m_PlayerOrbitalPosition; public static void SetPlayer(OrbitalPosition player) => s_Instance.m_PlayerOrbitalPosition = player; private float m_TimeOfLastRingSpawn = float.NegativeInfinity; private int m_LastGeneratedRing; public static int LastRingID => s_Instance.m_LastGeneratedRing; private const int CirclePoints = 100; 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 ); } } private void Update() { if (Time.time - m_TimeOfLastRingSpawn > GlobalOrbitalSettings.GapDistance) { GameObject newRing = Instantiate(GlobalOrbitalSettings.RingPrefab); newRing.transform.localScale = new Vector3(10f, 10f, 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 = Time.deltaTime * 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 opacity = (ring.transform.localScale.x - 0.5f) / 4f; opacity = Mathf.Clamp(opacity, 0, 0.5f); if (ring.transform.localScale.x < 0.5f) toRemove.Add(ring); Color c = Color.white; c.a = opacity; 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 (!m_Distances.ContainsKey(orbitalPosition.m_AttachedRing)) { orbitalPosition.m_Behaviour.OnReachCentre(); orbitalPosition.m_AttachedRing = m_LastGeneratedRing; } float distance = m_Distances[orbitalPosition.m_AttachedRing]; orbitalPosition.m_DistanceFromCentre = distance; orbitalPosition.m_RadiusDistance = (orbitalPosition.m_RadiusDistance + Time.deltaTime * GlobalOrbitalSettings.RadiusSpeed) % (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); } } } 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 { 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) { } }