using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using TMPro; public class RocketLauncher : MonoBehaviour { [Header("Ракета")] [SerializeField] private Transform rocketRoot; [SerializeField] private Transform[] flameParts; [SerializeField] private Button launchButton; [SerializeField] private GameObject materialPanel; [SerializeField] private GameObject infoPanel; [SerializeField] private TextMeshProUGUI infoPanelText; [Header("Вибухи")] [SerializeField] private GameObject explosionBig; [SerializeField] private GameObject explosionMedium; [SerializeField] private GameObject explosionSmall; [SerializeField] private GameObject debrisPrefab; [Header("Селектори")] [SerializeField] private RocketMaterialSelector materialSelector; [SerializeField] private PlanetSelector planetSelector; [SerializeField] private FuelSelector fuelSelector; [Header("Підсистеми")] [SerializeField] private RocketFlightCore flightCore; [SerializeField] private RocketCameraController cameraController; [SerializeField] private RocketEffectsController effectsController; private bool _isLaunched = false; private bool _sonicBoomDone = false; private Vector3 _rocketStartPos; private Quaternion _rocketStartRot; private static readonly HashSet _explodeOnLaunch = new HashSet { "Папір", "Сніжок", "Авокадо", "Лічі", "Гумова качечка", "Лід", "Скло", "Скляна банка", "Блокнот", "Пластикова дошка", "Цегла", "Граніт", "Мармур", "Чавун", "Гума", "Пластик" }; private static readonly HashSet _explodeSoon = new HashSet { "Дерево", "Асфальт", "Золото", "Дерев'яна дошка", "Каміння з мохом" }; private static readonly HashSet _planetExplode = new HashSet { "Венера", "Юпітер" }; private static readonly Dictionary _materialExplosionReason = new Dictionary { { "Папір", "Папір спалахує при +233C. Двигуни розжарюються до 3000C -- папір перетворився на попіл ще на старті." }, { "Сніжок", "Сніг тане при 0C. Полум'я двигуна миттєво перетворило корпус на пару ще до відриву від поверхні." }, { "Авокадо", "Органічні матеріали не витримують температури старту. Авокадо зайнялося ще до підйому." }, { "Лічі", "М'яка органіка не здатна витримати тиск і температуру навіть нижнього шару атмосфери." }, { "Гумова качечка", "Гума розплавляється при +300C. Від тяги двигунів вона розтеклась ще на старті." }, { "Лід", "Лід тане при 0C. Полум'я двигунів перетворило корпус на воду ще до відриву від землі." }, { "Скло", "Скло крихке і не витримує вібрації при старті. Термічний удар розтріскує корпус миттєво." }, { "Дерево", "Дерево займається при +300C. Аеродинамічне нагрівання перетворює корпус на факел вже на висоті 10 км." }, { "Асфальт", "Асфальт розм'якшується при +60C. Від тертя з повітрям він втрачає форму і ракета руйнується." }, { "Золото", "Золото плавиться при +1064C. Аеродинамічне нагрівання досягає цього порогу -- корпус деформувався." }, { "Цегла", "Цегла важить 1800 кг/м3 -- в 4 рази важча за алюміній. Двигун фізично не підняв конструкцію. Зруйнувалась від вібрації на старті." }, { "Граніт", "Граніт важить 2700 кг/м3 -- найважчий матеріал в списку. Жоден існуючий двигун не підніме ракету з таким корпусом." }, { "Мармур", "Мармур важить 2700 кг/м3 і крихкий. Розсипався від вібрації двигунів ще до відриву від землі." }, { "Чавун", "Чавун важить 7200 кг/м3 -- в 2.5 рази важчий за сталь. Конструкція розламалась від власної ваги при запуску двигунів." }, { "Гума", "Гума плавиться при +300C. Двигуни дають +3000C -- корпус розтікся ще до старту." }, { "Пластик", "Пластик плавиться при +150-300C і занадто слабкий для тиску при запуску. Деформувався і зруйнувався на старті." }, }; private static readonly Dictionary _planetFacts = new Dictionary { { "Земля", "Атмосфера сягає 100 км (лінія Кармана). Тиск на поверхні 1 атм, температура падає до -56C на висоті 11 км." }, { "Марс", "Атмосфера в 100 разів рідкіша за земну. Без скафандра кров закипіла б миттєво. Гравітація 3.7 м/с2." }, { "Місяць", "Без атмосфери температура від +127C вдень до -173C вночі. Гравітація 1.6 м/с2 -- в 6 разів менше земної." }, { "Юпітер", "Великий Червоний Плям -- шторм що триває понад 350 років. Гравітація 24.8 м/с2 -- у 2.5 рази більше земної." }, { "Венера", "Обертається у зворотний бік. Хмари з сірчаної кислоти. Тиск 92 атм -- як на глибині 900 м океану." }, { "Сатурн", "Кільця завтовшки лише 20 метрів але завширшки 282000 км. Вітри до 1800 км/год." }, { "Меркурій", "Рік 88 днів але день довший за рік -- 176 земних діб. Гравітація 3.7 м/с2 як на Марсі." }, { "Уран", "Обертається лежачи на боці -- вісь нахилена на 98 градусів. Гравітація 8.7 м/с2." }, { "Нептун", "Найсильніші вітри в Сонячній системі -- 2100 км/год. Гравітація 11.2 м/с2." }, { "Плутон", "Серце Плутона -- рівнина Томбо з льодовиків азоту. Гравітація лише 0.62 м/с2." } }; private static readonly Dictionary _planetExplosionReason = new Dictionary { { "Венера", "Венера вбиває потрійно: тиск 92 атм роздавлює корпус, +465C плавить метали, хмари сірчаної кислоти роз'їдають решту." }, { "Юпітер", "Юпітер не має поверхні -- ракета тоне в атмосфері. На глибині 1000 км тиск перевищує 100000 атм." } }; private static readonly Dictionary _fuelAmounts = new Dictionary { { "10%", 10f }, { "25%", 25f }, { "50%", 50f }, { "75%", 75f }, { "100%", 100f } }; private void Start() { if (launchButton != null) launchButton.onClick.AddListener(Launch); _rocketStartPos = rocketRoot.position; _rocketStartRot = rocketRoot.rotation; if (infoPanel != null) infoPanel.SetActive(false); flightCore.Initialize(); cameraController.Initialize(flightCore); effectsController.Initialize(flightCore); } private void Update() { if (!_isLaunched || flightCore == null) return; bool shouldExplode, flightFinished; flightCore.UpdateFlight(Time.deltaTime, out shouldExplode, out flightFinished); UpdateRocketTransform(); if (!_sonicBoomDone && flightCore.AnimTime >= 3.5f && flightCore.GetAtmosphereProgress() < 0.7f) { _sonicBoomDone = true; cameraController.TriggerSonicBoom(); } if (flightCore.FuelCutoff) SetFlamesActive(false); if (shouldExplode) { Explode(); return; } if (flightFinished) { effectsController.StopFlight(); cameraController.StopFlight(); if (flightCore.RocketEscaped) StartCoroutine(SpaceDriftAndInfo()); else StartCoroutine(ShowInfoAndReset()); } } private void UpdateRocketTransform() { float visualH = flightCore.CurrentHeight * Mathf.Lerp(0.00005f, 0.00015f, Mathf.Clamp01(flightCore.AnimTime / 8f)); rocketRoot.position = _rocketStartPos + Vector3.up * visualH; if (!flightCore.FuelCutoff) { float tilt = Mathf.Clamp(flightCore.Velocity * 0.002f, -12f, 15f); rocketRoot.rotation = Quaternion.Lerp(rocketRoot.rotation, _rocketStartRot * Quaternion.Euler(-tilt, 0f, 0f), Time.deltaTime * 4f); } else { float cutoffTime = flightCore.GetFuelCutoffTime(); float gentleTilt = Mathf.Lerp(0f, 6f, Mathf.Clamp01(cutoffTime / 8f)); float gentleRoll = Mathf.Sin(cutoffTime * 0.3f) * 2f; rocketRoot.rotation = Quaternion.Slerp(rocketRoot.rotation, _rocketStartRot * Quaternion.Euler(gentleTilt, 0f, gentleRoll), Time.deltaTime * 0.3f); } } private void Launch() { if (_isLaunched) return; _isLaunched = true; _sonicBoomDone = false; if (materialPanel != null) materialPanel.SetActive(false); var mat = materialSelector.GetCurrentMaterial(); var pd = planetSelector.GetCurrentPlanet(); string matName = mat.name; string planet = pd.name; bool willExplode = _explodeOnLaunch.Contains(matName) || _explodeSoon.Contains(matName) || _planetExplode.Contains(planet); float explodeDelay = _explodeOnLaunch.Contains(matName) ? 0.7f : (_explodeSoon.Contains(matName) ? 4.0f : 5.0f); if (!willExplode) explodeDelay = 0f; string fuelName = fuelSelector.GetFuelName(); float fuelAmount = _fuelAmounts.ContainsKey(fuelName) ? _fuelAmounts[fuelName] : 50f; float shakeMultiplier = GetShakeMultiplier(planet); flightCore.StartFlight( fuelAmount, pd.gravity, pd.atmosphereDensity, mat.dragCoefficient, willExplode, explodeDelay); cameraController.StartFlight(); cameraController.StartIgnitionShake(shakeMultiplier); effectsController.StartFlight(planet, fuelSelector.GetFuelLevel(), shakeMultiplier); } private static readonly Dictionary _materialFacts = new Dictionary { { "Вуглецеве волокно", "Саме з нього роблять корпуси Falcon 9 та Starship. Легке і міцне -- ідеал для ракет." }, { "Титан", "NASA використовує титан для корпусів двигунів. Витримує до +1650C." }, { "Полірована сталь", "Starship від SpaceX зроблений зі сталі. Маск обрав сталь бо вона дешева і міцна." }, { "Алюміній", "Apollo і Saturn V мали алюмінієві корпуси. Легкий але плавиться при +660C." }, { "Кераміка", "Space Shuttle мав 24000 керамічних плиток для захисту від жару при посадці (+1600C)." }, { "Сталь", "Більшість радянських ракет Союз зроблені зі сталі. Важка але надійна." }, { "Мідь", "Мідь використовують у соплах двигунів -- вона добре відводить жар від згоряння." }, { "Пластик", "Пластик занадто легкий і слабкий. Реальні ракети не мають пластикових корпусів." }, { "Скло", "Скло крихке при вібрації старту. Використовується тільки у вікнах кабін астронавтів." }, { "Чавун", "Чавун у 3 рази важчий за алюміній. Жодна реальна ракета не зроблена з чавуну." }, { "Мармур", "Мармур крихкий і важкий. У космонавтиці не використовується взагалі." }, { "Граніт", "Граніт у 4 рази важчий за алюміній. Ракета з граніту не злетить з Землі." }, { "Цегла", "Цегла важка і крихка. Максимум що може -- кілька метрів вгору до вибуху." }, { "Гума", "Гума плавиться при +300C. Двигуни дають +3000C -- не витримає навіть секунди." }, }; private float GetRealHeightKm(string planetName, string matName, int fuelLevel) { float[] baseFuelHeights = { 15f, 100f, 408f, 36000f, 384000f }; float baseKm = baseFuelHeights[Mathf.Clamp(fuelLevel, 0, 4)]; float planetMult = GetPlanetMultiplier(planetName); float matMult = GetMaterialMultiplier(matName); return baseKm * planetMult * matMult; } private float GetPlanetMultiplier(string planet) { switch (planet) { case "Місяць": return 6.0f; case "Плутон": return 15.0f; case "Марс": return 2.5f; case "Меркурій": return 2.5f; case "Уран": return 1.1f; case "Земля": return 1.0f; case "Сатурн": return 0.9f; case "Нептун": return 0.85f; case "Венера": return 0.3f; case "Юпітер": return 0.1f; default: return 1.0f; } } private float GetMaterialMultiplier(string mat) { switch (mat) { case "Вуглецеве волокно": return 1.0f; case "Титан": return 0.95f; case "Полірована сталь": return 0.9f; case "Алюміній": return 0.88f; case "Кераміка": return 0.75f; case "Сталь": return 0.7f; case "Мідь": return 0.6f; case "Пластик": return 0.55f; case "Скло": return 0.5f; case "Чавун": return 0.15f; case "Мармур": return 0.45f; case "Граніт": return 0.35f; case "Цегла": return 0.25f; case "Гума": return 0.2f; default: return 0.5f; } } private string FormatRealHeight(float km) { if (km < 1f) return (km * 1000f).ToString("F0") + " м"; if (km < 1000f) return km.ToString("F0") + " км"; if (km < 1000000f) return (km / 1000f).ToString("F1") + " тис. км"; return (km / 1000000f).ToString("F2") + " млн. км"; } private string GetHeightContext(float km) { if (km < 12f) return "Це висота польоту пасажирського літака."; if (km < 50f) return "Стратосфера. Вище хмар але ще не космос."; if (km < 100f) return "Майже лінія Кармана -- офіційна межа космосу (100 км)."; if (km < 408f) return "Офіційно в космосі! Як висота польоту Space Shuttle."; if (km < 36000f) return "Орбіта МКС або вище. Ти справжній астронавт!"; if (km < 384000f) return "Геостаціонарна орбіта -- тут висять супутники GPS і телебачення."; return "Відстань як до Місяця або далі. Міжпланетний простір!"; } private string GetRealFlightTime(float km) { float speedKmH = 28000f; float hours = km / speedKmH; if (hours < 0.017f) return (hours * 3600f).ToString("F0") + " секунд"; if (hours < 1f) return (hours * 60f).ToString("F0") + " хвилин"; if (hours < 24f) return hours.ToString("F1") + " годин"; if (hours < 720f) return (hours / 24f).ToString("F1") + " діб"; if (hours < 8760f) return (hours / 720f).ToString("F1") + " місяців"; return (hours / 8760f).ToString("F1") + " років"; } private float GetShakeMultiplier(string planet) { switch (planet) { case "Юпітер": return 2.5f; case "Венера": return 1.8f; case "Нептун": return 2.0f; case "Сатурн": return 1.2f; case "Земля": return 1.0f; case "Уран": return 0.8f; case "Марс": return 0.6f; case "Меркурій": return 0.4f; case "Місяць": return 0.3f; case "Плутон": return 0.2f; default: return 1.0f; } } private void Explode() { effectsController.StopFlight(); cameraController.StopFlight(); Vector3 pos = rocketRoot.position; MeshRenderer[] meshes = rocketRoot.GetComponentsInChildren(); Vector3 meshCenter = pos; if (meshes.Length > 0) { meshCenter = Vector3.zero; foreach (var mr in meshes) meshCenter += mr.bounds.center; meshCenter /= meshes.Length; } if (explosionBig != null) { var big = Instantiate(explosionBig, meshCenter, Quaternion.identity); big.transform.localScale = Vector3.one * 5f; RemoveMissingScripts(big); } if (explosionMedium != null) { for (int i = 0; i < 3; i++) { var med = Instantiate(explosionMedium, meshCenter + new Vector3(Random.Range(-1.2f, 1.2f), Random.Range(-0.5f, 0.8f), 0f), Quaternion.identity); med.transform.localScale = Vector3.one * 3f; RemoveMissingScripts(med); } } if (explosionSmall != null) { for (int i = 0; i < 6; i++) { var sm = Instantiate(explosionSmall, meshCenter + new Vector3(Random.Range(-2f, 2f), Random.Range(-1f, 2f), Random.Range(-1f, 1f)), Quaternion.identity); sm.transform.localScale = Vector3.one * 2.2f; RemoveMissingScripts(sm); } } if (debrisPrefab != null) { var debris = Instantiate(debrisPrefab, meshCenter, Quaternion.identity); debris.SetActive(true); foreach (var piece in debris.GetComponentsInChildren()) { piece.transform.position = meshCenter + Random.insideUnitSphere * 0.8f; piece.Launch(meshCenter, Random.Range(15f, 30f), materialSelector.GetCurrentMaterial().material); } } SetFlamesActive(false); rocketRoot.gameObject.SetActive(false); StartCoroutine(ShowInfoAndReset()); } private void RemoveMissingScripts(GameObject obj) { foreach (var b in obj.GetComponentsInChildren()) if (b == null) DestroyImmediate(b); } private IEnumerator SpaceDriftAndInfo() { cameraController.SwitchToSpaceDrift(); effectsController.PlaySpaceDriftParticles(); float driftTime = 0f; float driftAngle = 0f; Vector3 driftCenter = rocketRoot.position; while (driftTime < 3f) { driftTime += Time.deltaTime; driftAngle += Time.deltaTime * 15f; rocketRoot.position = driftCenter + new Vector3( Mathf.Sin(driftAngle * Mathf.Deg2Rad) * 0.3f, Mathf.Cos(driftAngle * Mathf.Deg2Rad * 0.5f) * 0.15f, 0f); rocketRoot.rotation = Quaternion.Lerp(rocketRoot.rotation, _rocketStartRot * Quaternion.Euler(25f, driftAngle * 0.3f, 10f), Time.deltaTime * 0.5f); yield return null; } SetFlamesActive(false); yield return new WaitForSeconds(1.5f); rocketRoot.gameObject.SetActive(false); effectsController.StopAllParticles(); StartCoroutine(ShowInfoAndReset()); } private IEnumerator ShowInfoAndReset() { yield return new WaitForSeconds(1.5f); int fuelLevel = fuelSelector.GetFuelLevel(); string planetName = planetSelector.GetCurrentPlanet().name; string matName = materialSelector.GetCurrentMaterial().name; bool exploded = _explodeOnLaunch.Contains(matName) || _explodeSoon.Contains(matName) || _planetExplode.Contains(planetName); float realKm = GetRealHeightKm(planetName, matName, fuelLevel); string realHeightStr = FormatRealHeight(realKm); string heightContext = GetHeightContext(realKm); string info; if (exploded) { string reason = _planetExplode.Contains(planetName) ? (_planetExplosionReason.ContainsKey(planetName) ? _planetExplosionReason[planetName] : "Планета знищила ракету.") : (_materialExplosionReason.ContainsKey(matName) ? _materialExplosionReason[matName] : "Матеріал не витримав навантажень польоту."); string planetInfo = _planetFacts.ContainsKey(planetName) ? _planetFacts[planetName] : ""; info = "РЕЗУЛЬТАТ ПОЛЬОТУ\n\n" + "Матеріал: " + matName + "\nПланета: " + planetName + "\n" + "Пальне: " + fuelSelector.GetFuelName() + "\n\n" + "ПРИЧИНА КАТАСТРОФИ:\n" + reason + "\n\n" + "ПРО ПЛАНЕТУ:\n" + planetInfo; } else { string escapeNote = flightCore.RocketEscaped ? "Ракета досягла другої космічної швидкості!\n\n" : ""; string planetInfo = _planetFacts.ContainsKey(planetName) ? _planetFacts[planetName] : ""; string matFact = _materialFacts.ContainsKey(matName) ? _materialFacts[matName] : ""; string flightTime = GetRealFlightTime(realKm); info = "РЕЗУЛЬТАТ ПОЛЬОТУ\n\n" + "Матеріал: " + matName + "\n" + "Планета: " + planetName + "\n" + "Пальне: " + fuelSelector.GetFuelName() + "\n\n" + escapeNote + "У РЕАЛЬНОСТІ ТАКА РАКЕТА ЗЛЕТІЛА Б НА:\n" + realHeightStr + "\n" + heightContext + "\n" + "Час польоту: " + flightTime + "\n\n" + "ФАКТ ПРО МАТЕРІАЛ:\n" + matFact + "\n\n" + "ПРО ПЛАНЕТУ:\n" + planetInfo + "\n\n" + "ЦІКАВО: До Місяця 3 доби. До Нептуна 12 років!"; } if (infoPanelText != null) infoPanelText.text = info; if (infoPanel != null) infoPanel.SetActive(true); yield return new WaitForSeconds(18f); ResetAll(); } private void ResetAll() { _isLaunched = false; _sonicBoomDone = false; GameObject[] allObjects = GameObject.FindObjectsOfType(); foreach (var obj in allObjects) { if (obj != null && obj.name.Contains("(Clone)")) Destroy(obj); } flightCore.ResetFlight(); cameraController.ResetCamera(); rocketRoot.position = _rocketStartPos; rocketRoot.rotation = _rocketStartRot; rocketRoot.gameObject.SetActive(true); SetFlamesActive(true); var pd = planetSelector.GetCurrentPlanet(); effectsController.ResetEffects(new RocketEffectsController.PlanetData { atmosphereThickness = pd.atmosphereThickness, exposure = pd.exposure, skyTint = pd.skyTint, groundColor = pd.groundColor, hasFog = pd.hasFog, fogDensity = pd.fogDensity, fogColor = pd.fogColor, sunLightIntensity = pd.sunLightIntensity }); planetSelector.RestoreCurrentPlanet(); if (infoPanel != null) infoPanel.SetActive(false); if (materialPanel != null) materialPanel.SetActive(true); } private void SetFlamesActive(bool active) { foreach (var flame in flameParts) if (flame != null) flame.gameObject.SetActive(active); } }