using System.Collections; using System.Collections.Generic; using UnityEngine; public partial class OrbitalBehaviour { public class BehaviourManager : MonoBehaviour { private static BehaviourManager Instance; private Vector3[] m_PrecalculatedPositions; private bool m_CurrentRingAllowsSpawning; private bool m_IsSimulationRunning; private bool m_AllowPlayerInput; public static bool IsSimulationRunning => Instance.m_IsSimulationRunning; public static bool AllowPlayerInput => Instance.m_AllowPlayerInput; private float m_LocalDeltaTimeScale = 1f; private float m_TimeOfLastRingSpawn; private int m_LastGeneratedRing; private readonly Dictionary m_Distances = new(); private readonly List m_ObjectInstances = new(); private readonly List m_Rings = new(); private OrbitalBehaviour m_PlayerInstance; private GameObject m_RingParent; private const int CirclePoints = 100; public static IEnumerator StartPlayerSpeedupModifier() { Instance.m_LocalDeltaTimeScale *= 1.5f; yield return new WaitForSeconds(10f); Instance.m_LocalDeltaTimeScale /= 1.5f; Instance.m_LocalDeltaTimeScale *= 2f; yield return new WaitForSeconds(5f); Instance.m_LocalDeltaTimeScale /= 2f; } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void OnApplicationStart() { GameObject manager = new("OrbitalManager"); DontDestroyOnLoad(manager); Instance = manager.AddComponent(); Instance.PrecalculatePositions(); Instance.StartCoroutine(RestartSimulation()); Instance.m_RingParent = new GameObject("RingHolder"); } private void PrecalculatePositions() { m_PrecalculatedPositions = new Vector3[CirclePoints]; for (int idx = 0; idx < m_PrecalculatedPositions.Length; idx++) { m_PrecalculatedPositions[idx] = new Vector3 ( x: Mathf.Cos(Mathf.Deg2Rad * (359f / CirclePoints * idx)), y: Mathf.Sin(Mathf.Deg2Rad * (359f / CirclePoints * idx)), z: 0 ); } } public static IEnumerator RestartSimulation() { Instance.m_IsSimulationRunning = false; Instance.m_AllowPlayerInput = false; Instance.m_LocalDeltaTimeScale = 7f; for (int idx = 0; idx < 100; idx++) // 100 = 5 seconds of fixed updates { if (Instance.m_PlayerInstance != null) { Instance.m_PlayerInstance.m_AttachedRing = Instance.m_LastGeneratedRing; } yield return new WaitForFixedUpdate(); } PlayerController.AttachPlayer(); foreach (OrbitalBehaviour behaviour in Instance.m_ObjectInstances) { behaviour.OnSimulationRestart(); } yield return new WaitForSeconds(1.3f); Instance.m_IsSimulationRunning = true; Instance.m_AllowPlayerInput = true; Instance.m_LocalDeltaTimeScale = 1f; } private void SpawnNewRing() { GameObject newRing = Instantiate(Settings.Instance.RingPrefab, m_RingParent.transform); newRing.transform.localScale = new Vector3(12f, 12f, 1f); 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; m_CurrentRingAllowsSpawning = Random.Range(0, 25) != 0; if (!m_CurrentRingAllowsSpawning && m_AllowPlayerInput) { Instantiate(Settings.Instance.ModifierPrefab); } } private void Update() { if (Time.time - m_TimeOfLastRingSpawn > Settings.Instance.GapDistance / m_LocalDeltaTimeScale) { SpawnNewRing(); } List toRemove = new(); m_Distances.Clear(); foreach (Ring ring in m_Rings) { float diff = (m_LocalDeltaTimeScale * Time.deltaTime) * Settings.Instance.DistanceSpeed; ring.transform.localScale -= new Vector3(diff, diff, 0f); LineRenderer lineRenderer = ring.GetComponentInChildren(); for (int vert = 0; vert < CirclePoints; vert++) { lineRenderer.SetPosition(vert, new Vector3 ( x: m_PrecalculatedPositions[vert].x * ring.transform.localScale.x, y: m_PrecalculatedPositions[vert].y * ring.transform.localScale.y, z: 10 )); } 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) { if (m_IsSimulationRunning) PlayerController.s_PlayerScore++; 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 (OrbitalBehaviour orbitalPosition in m_ObjectInstances) { if (!orbitalPosition.IsAttachedToRings) continue; if (!m_Distances.TryGetValue(orbitalPosition.m_AttachedRing, out float distance)) { orbitalPosition.m_AttachedRing = m_LastGeneratedRing; orbitalPosition.OnReachCentre(); continue; } orbitalPosition.m_DistanceFromCentre = distance; float deltaTime = Time.deltaTime * m_LocalDeltaTimeScale; float movement = deltaTime * Settings.Instance.RadiusSpeed * orbitalPosition.m_SpinSpeed; orbitalPosition.m_DistanceAlongCircumference += movement; orbitalPosition.m_DistanceAlongCircumference %= Mathf.PI * 2; orbitalPosition.transform.position = orbitalPosition.TranslateToVector3(); } } private void FixedUpdate() { foreach (OrbitalBehaviour orbital in m_ObjectInstances) { if (orbital == m_PlayerInstance) continue; Vector2 a = orbital.TranslateToVector3(); Vector2 b = m_PlayerInstance.TranslateToVector3(); float distance = (a - b).magnitude; float radii = orbital.m_ObjectRadius + m_PlayerInstance.m_ObjectRadius; if (distance > radii) continue; m_PlayerInstance.OnOrbitalCollision(orbital); orbital.OnOrbitalCollision(m_PlayerInstance); } if (Random.Range(0, 20) == 0 && m_AllowPlayerInput && m_CurrentRingAllowsSpawning) { Instantiate(Settings.Instance.EnemyPrefab); } } public static void UnregisterOrbitalInstance(OrbitalBehaviour instance) { Instance.m_ObjectInstances.Remove(instance); } public static void RegisterOrbitalInstance(OrbitalBehaviour instance) { instance.m_AttachedRing = Instance.m_LastGeneratedRing; // Connects it to last generated ring Instance.m_ObjectInstances.Add(instance); } public static void SetPlayer(OrbitalBehaviour player) { Instance.m_PlayerInstance = player; } } }