using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class RocketEffectsController : MonoBehaviour { [Header("Частинки")] [SerializeField] private Transform rocketRoot; [Header("Skybox і освітлення")] [SerializeField] private Material skyboxMaterial; [SerializeField] private Light sunLight; [Header("UI")] [SerializeField] private Image vignetteUI; private ParticleSystem _atmParticles; private ParticleSystem _starParticles; private ParticleSystem _speedLines; private ParticleSystem _cloudParticles; private ParticleSystem _heatParticles; private ParticleSystem _debrisParticles; private ParticleSystem _layer1Particles; private ParticleSystem _layer2Particles; private ParticleSystem _layer3Particles; private ParticleSystem _galaxyParticles; private ParticleSystem _nebulaParticles; private ParticleSystem _meteorParticles; private ParticleSystem _cosmicDustParticles; private Light _engineLight; private Light _rocketSpotLight; private Color _startSkyTint; private float _startAtmThickness; private float _startExposure; private float _startSunIntensity; private string _currentPlanet = "Земля"; private float[] _starVelByFuel = { -120f, -180f, -260f, -350f, -500f }; private int _currentFuelLevel; public void Initialize(RocketFlightCore core) { _core = core; CreateAllParticles(); } private float _enginePulse; private bool _isActive; private RocketFlightCore _core; public void StartFlight(string planetName, int fuelLevel, float shakeMultiplier) { _currentPlanet = planetName; _currentFuelLevel = fuelLevel; _isActive = true; _enginePulse = 0f; if (skyboxMaterial != null) { _startSkyTint = skyboxMaterial.GetColor("_SkyTint"); _startAtmThickness = skyboxMaterial.GetFloat("_AtmosphereThickness"); _startExposure = skyboxMaterial.GetFloat("_Exposure"); } _startSunIntensity = sunLight != null ? sunLight.intensity : 1f; _atmParticles.Play(); _speedLines.Play(); StartCoroutine(AtmosphereLayerFlash(planetName)); if (IsLightningPlanet(planetName)) StartCoroutine(LightningEffect()); } public void StopFlight() { _isActive = false; StopAllParticles(); } public void ResetEffects(PlanetData pd) { _isActive = false; StopAllParticles(); if (_engineLight != null) _engineLight.intensity = 0f; if (_rocketSpotLight != null) _rocketSpotLight.intensity = 0f; if (vignetteUI != null) vignetteUI.color = new Color(0f, 0f, 0f, 0f); if (skyboxMaterial != null) { skyboxMaterial.SetFloat("_AtmosphereThickness", pd.atmosphereThickness); skyboxMaterial.SetFloat("_Exposure", pd.exposure); skyboxMaterial.SetColor("_SkyTint", pd.skyTint); skyboxMaterial.SetColor("_GroundColor", pd.groundColor); } RenderSettings.fog = pd.hasFog; if (pd.hasFog) { RenderSettings.fogDensity = pd.fogDensity; RenderSettings.fogColor = pd.fogColor; } if (sunLight != null) sunLight.intensity = pd.sunLightIntensity; } private void Update() { if (!_isActive || _core == null) return; _enginePulse += Time.deltaTime * 18f; float pulse = Mathf.Sin(_enginePulse) * 0.5f + 0.5f; float speedNorm = _core.GetSpeedNormalized(); float inAtm = 1f - _core.GetAtmosphereProgress(); float inSpace = _core.GetSpaceProgress(); float animTime = _core.AnimTime; float maxHeight = GetApproxMaxHeight(); float atmosphereHeight = maxHeight * 0.35f; float currentHeight = _core.CurrentHeight; UpdateEngineLight(pulse, speedNorm); UpdateSpotLight(inSpace, pulse); UpdateVignette(speedNorm, _core.FuelCutoff); UpdateAtmosphereParticles(inAtm, animTime, speedNorm, pulse); UpdateSpaceParticles(inSpace, speedNorm); UpdateLayerParticles(currentHeight, atmosphereHeight, inAtm); UpdateSkybox(inAtm, inSpace, animTime); } private void UpdateEngineLight(float pulse, float speedNorm) { if (_engineLight == null || _core.FuelCutoff) return; _engineLight.intensity = Mathf.Lerp(3f, 8f, pulse) * Mathf.Clamp01(speedNorm + 0.3f); _engineLight.color = Color.Lerp(new Color(1f, 0.4f, 0.05f), new Color(1f, 0.85f, 0.4f), pulse); _engineLight.range = Mathf.Lerp(8f, 16f, pulse); } private void UpdateSpotLight(float inSpace, float pulse) { if (_rocketSpotLight == null) return; _rocketSpotLight.intensity = Mathf.Lerp(0f, 4f, inSpace); _rocketSpotLight.color = Color.Lerp(new Color(0.8f, 0.9f, 1f), new Color(1f, 1f, 0.9f), pulse * 0.3f); } private void UpdateVignette(float speedNorm, bool fuelCutoff) { if (vignetteUI == null) return; float targetAlpha = fuelCutoff ? 0f : speedNorm * 0.45f; Color c = vignetteUI.color; vignetteUI.color = new Color(0f, 0f, 0f, Mathf.Lerp(c.a, targetAlpha, Time.deltaTime * 3f)); } private void UpdateAtmosphereParticles(float inAtm, float animTime, float speedNorm, float pulse) { if (_atmParticles != null) { var ae = _atmParticles.emission; ae.rateOverTime = inAtm * 110f; } if (_cloudParticles != null) { float cloudFactor = (animTime > 1.5f && animTime < 5f) ? Mathf.Sin((animTime - 1.5f) / 3.5f * Mathf.PI) * 35f : 0f; var ce = _cloudParticles.emission; ce.rateOverTime = cloudFactor * inAtm; if (cloudFactor > 1f && !_cloudParticles.isPlaying) _cloudParticles.Play(); } if (_heatParticles != null) { var he = _heatParticles.emission; he.rateOverTime = speedNorm * inAtm * 60f; if (speedNorm > 0.1f && !_heatParticles.isPlaying) _heatParticles.Play(); } if (_debrisParticles != null) { var deb = _debrisParticles.emission; deb.rateOverTime = speedNorm * inAtm * 20f; if (speedNorm > 0.05f && !_debrisParticles.isPlaying) _debrisParticles.Play(); } } private void UpdateSpaceParticles(float inSpace, float speedNorm) { if (_starParticles != null) { float starVelTarget = _starVelByFuel[Mathf.Clamp(_currentFuelLevel, 0, 4)]; float starSpeed = Mathf.Lerp(starVelTarget * 0.5f, starVelTarget * 1.2f, speedNorm); var sv = _starParticles.velocityOverLifetime; sv.y = new ParticleSystem.MinMaxCurve(starSpeed, starSpeed); var se = _starParticles.emission; float starRate = inSpace * 250f + speedNorm * 100f; se.rateOverTime = starRate; if (starRate > 5f && !_starParticles.isPlaying) _starParticles.Play(); } if (_speedLines != null) { float starVelTarget = _starVelByFuel[Mathf.Clamp(_currentFuelLevel, 0, 4)]; var spe = _speedLines.emission; spe.rateOverTime = speedNorm * (100f + _currentFuelLevel * 40f); var spv = _speedLines.velocityOverLifetime; float spVal = starVelTarget * 2f * Mathf.Max(speedNorm, 0.4f); spv.y = new ParticleSystem.MinMaxCurve(spVal, spVal); } ApplySpaceSky(_currentPlanet, inSpace); } private void UpdateLayerParticles(float currentHeight, float atmosphereHeight, float inAtm) { float layer1T = Mathf.Clamp01((currentHeight - atmosphereHeight * 0.1f) / (atmosphereHeight * 0.25f)); float layer1End = Mathf.Clamp01((currentHeight - atmosphereHeight * 0.3f) / (atmosphereHeight * 0.1f)); float layer1Factor = Mathf.Clamp01(layer1T - layer1End) * inAtm; float layer2T = Mathf.Clamp01((currentHeight - atmosphereHeight * 0.4f) / (atmosphereHeight * 0.2f)); float layer2End = Mathf.Clamp01((currentHeight - atmosphereHeight * 0.65f) / (atmosphereHeight * 0.1f)); float layer2Factor = Mathf.Clamp01(layer2T - layer2End) * inAtm; float layer3T = Mathf.Clamp01((currentHeight - atmosphereHeight * 0.7f) / (atmosphereHeight * 0.2f)); float layer3End = Mathf.Clamp01((currentHeight - atmosphereHeight * 0.95f) / (atmosphereHeight * 0.05f)); float layer3Factor = Mathf.Clamp01(layer3T - layer3End) * inAtm; if (_layer1Particles != null) { var l1e = _layer1Particles.emission; l1e.rateOverTime = layer1Factor * 25f; if (layer1Factor > 0.05f && !_layer1Particles.isPlaying) _layer1Particles.Play(); } if (_layer2Particles != null) { var l2e = _layer2Particles.emission; l2e.rateOverTime = layer2Factor * 18f; if (layer2Factor > 0.05f && !_layer2Particles.isPlaying) _layer2Particles.Play(); } if (_layer3Particles != null) { var l3e = _layer3Particles.emission; l3e.rateOverTime = layer3Factor * 12f; if (layer3Factor > 0.05f && !_layer3Particles.isPlaying) _layer3Particles.Play(); } } private void UpdateSkybox(float inAtm, float inSpace, float animTime) { if (skyboxMaterial == null) return; float atmProgress = _core.GetAtmosphereProgress(); float spaceProgress = _core.GetSpaceProgress(); float horizonT = Mathf.Clamp01(atmProgress * 1.4f); float skyT = Mathf.Clamp01(atmProgress * 1.1f); float spaceT = Mathf.Clamp01(spaceProgress * 1.5f); float t = Mathf.Max(skyT, spaceT); skyboxMaterial.SetFloat("_AtmosphereThickness", Mathf.Lerp(_startAtmThickness, 0f, horizonT)); skyboxMaterial.SetFloat("_Exposure", Mathf.Lerp(_startExposure, 0.25f, t)); skyboxMaterial.SetColor("_SkyTint", Color.Lerp(_startSkyTint, Color.black, t)); if (sunLight != null) sunLight.intensity = Mathf.Lerp(_startSunIntensity, 0.08f, t); DynamicGI.UpdateEnvironment(); } private void ApplySpaceSky(string planetName, float spaceT) { if (spaceT < 0.01f) return; Color galaxyTint = GetGalaxyTint(planetName); float galaxyRate = spaceT * 150f; if (_galaxyParticles != null) { var gm = _galaxyParticles.main; gm.startColor = new ParticleSystem.MinMaxGradient( new Color(galaxyTint.r, galaxyTint.g, galaxyTint.b, 0.9f), new Color(galaxyTint.r * 0.6f, galaxyTint.g * 0.6f, galaxyTint.b * 0.6f, 0.4f)); var ge = _galaxyParticles.emission; ge.rateOverTime = galaxyRate; if (galaxyRate > 1f && !_galaxyParticles.isPlaying) _galaxyParticles.Play(); } if (_cosmicDustParticles != null) { var de = _cosmicDustParticles.emission; de.rateOverTime = spaceT * 60f; if (spaceT * 60f > 1f && !_cosmicDustParticles.isPlaying) _cosmicDustParticles.Play(); } } private Color GetGalaxyTint(string planetName) { switch (planetName) { case "Марс": return new Color(0.9f, 0.5f, 0.2f); case "Місяць": return new Color(0.95f, 0.95f, 1f); case "Юпітер": return new Color(1f, 0.7f, 0.3f); case "Венера": return new Color(1f, 0.85f, 0.2f); case "Сатурн": return new Color(1f, 0.95f, 0.6f); case "Меркурій": return new Color(1f, 0.95f, 0.8f); case "Уран": return new Color(0.3f, 1f, 0.9f); case "Нептун": return new Color(0.2f, 0.3f, 1f); case "Плутон": return new Color(0.6f, 0.5f, 0.9f); default: return new Color(0.7f, 0.85f, 1f); } } private bool IsLightningPlanet(string name) { return name == "Венера" || name == "Юпітер" || name == "Нептун"; } private float GetApproxMaxHeight() { float[] heights = { 500f, 8000f, 50000f, 200000f, 800000f }; return heights[Mathf.Clamp(_currentFuelLevel, 0, 4)]; } public void StopAllParticles() { ParticleSystem[] all = { _atmParticles, _starParticles, _speedLines, _cloudParticles, _heatParticles, _debrisParticles, _layer1Particles, _layer2Particles, _layer3Particles, _galaxyParticles, _nebulaParticles, _meteorParticles, _cosmicDustParticles }; foreach (var ps in all) if (ps != null) ps.Stop(); } public void PlaySpaceDriftParticles() { if (_starParticles != null) { _starParticles.Play(); var se = _starParticles.emission; se.rateOverTime = 120f; var sv = _starParticles.velocityOverLifetime; sv.y = new ParticleSystem.MinMaxCurve(-80f, -80f); } } private ParticleSystem MakeParticles(string goName, int maxP, Color c1, Color c2, float sMin, float sMax, float ltMin, float ltMax, float spMin, float spMax, float radius, float velY) { GameObject go = new GameObject(goName); go.transform.SetParent(rocketRoot); go.transform.localPosition = Vector3.zero; ParticleSystem ps = go.AddComponent(); var m = ps.main; m.startLifetime = new ParticleSystem.MinMaxCurve(ltMin, ltMax); m.startSpeed = new ParticleSystem.MinMaxCurve(spMin, spMax); m.startSize = new ParticleSystem.MinMaxCurve(sMin, sMax); m.startColor = new ParticleSystem.MinMaxGradient(c1, c2); m.maxParticles = maxP; m.simulationSpace = ParticleSystemSimulationSpace.World; var em = ps.emission; em.rateOverTime = 0f; var sh = ps.shape; sh.shapeType = ParticleSystemShapeType.Sphere; sh.radius = radius; var v = ps.velocityOverLifetime; v.enabled = true; v.space = ParticleSystemSimulationSpace.World; v.x = new ParticleSystem.MinMaxCurve(0f, 0f); v.y = new ParticleSystem.MinMaxCurve(velY, velY); v.z = new ParticleSystem.MinMaxCurve(0f, 0f); ps.Stop(); return ps; } private void CreateAllParticles() { _atmParticles = MakeParticles("_Atm", 600, new Color(1f, 1f, 1f, 0.95f), new Color(0.8f, 0.9f, 1f, 0.7f), 0.06f, 0.35f, 0.3f, 0.9f, 15f, 35f, 5f, -70f); _starParticles = MakeParticles("_Stars", 600, new Color(1f, 1f, 1f, 1f), new Color(0.6f, 0.8f, 1f, 0.8f), 0.02f, 0.14f, 0.3f, 1.2f, 30f, 80f, 12f, -200f); _speedLines = MakeParticles("_Speed", 300, new Color(1f, 1f, 1f, 0.9f), new Color(0.4f, 0.7f, 1f, 0.4f), 0.006f, 0.025f, 0.08f, 0.22f, 50f, 100f, 1.5f, -220f); _cloudParticles = MakeParticles("_Clouds", 100, new Color(1f, 1f, 1f, 1f), new Color(0.9f, 0.95f, 1f, 0.8f), 3f, 7f, 0.4f, 0.9f, 5f, 15f, 7f, -25f); _heatParticles = MakeParticles("_Heat", 150, new Color(1f, 0.6f, 0.1f, 0.9f), new Color(1f, 0.3f, 0.05f, 0.3f), 0.04f, 0.25f, 0.15f, 0.5f, 2f, 8f, 1f, -10f); _debrisParticles = MakeParticles("_Debris", 80, new Color(0.9f, 0.7f, 0.3f, 1f), new Color(0.5f, 0.5f, 0.5f, 0.6f), 0.03f, 0.12f, 0.3f, 0.8f, 5f, 20f, 1f, -12f); _layer1Particles = MakeParticles("_Layer1", 60, new Color(1f, 1f, 1f, 1f), new Color(0.85f, 0.9f, 1f, 0.7f), 4f, 10f, 0.4f, 0.9f, 3f, 10f, 7f, -22f); _layer2Particles = MakeParticles("_Layer2", 40, new Color(0.7f, 0.85f, 1f, 0.95f), new Color(0.55f, 0.7f, 1f, 0.6f), 2.5f, 7f, 0.3f, 0.7f, 2f, 8f, 8f, -16f); _layer3Particles = MakeParticles("_Layer3", 30, new Color(0.3f, 0.4f, 0.8f, 0.9f), new Color(0.2f, 0.3f, 0.6f, 0.5f), 2f, 5f, 0.25f, 0.6f, 2f, 7f, 9f, -13f); _galaxyParticles = MakeParticles("_Galaxy", 400, new Color(1f, 1f, 1f, 1f), new Color(0.5f, 0.6f, 1f, 0.5f), 0.04f, 0.18f, 1.5f, 4f, 10f, 40f, 18f, -60f); _nebulaParticles = MakeParticles("_Nebula", 80, new Color(0.6f, 0.2f, 1f, 0.7f), new Color(1f, 0.3f, 0.1f, 0.3f), 1.5f, 5f, 1f, 3f, 2f, 8f, 15f, -20f); _cosmicDustParticles = MakeParticles("_CosmicDust", 300, new Color(0.8f, 0.7f, 1f, 0.6f), new Color(0.4f, 0.3f, 0.8f, 0.2f), 0.008f, 0.035f, 0.5f, 2f, 5f, 20f, 25f, -30f); _meteorParticles = MakeParticles("_Meteors", 60, new Color(1f, 0.9f, 0.6f, 1f), new Color(0.8f, 0.5f, 0.2f, 0.4f), 0.01f, 0.04f, 0.05f, 0.15f, 80f, 160f, 20f, -300f); var mv = _meteorParticles.velocityOverLifetime; mv.x = new ParticleSystem.MinMaxCurve(-80f, 80f); mv.y = new ParticleSystem.MinMaxCurve(-300f, -100f); GameObject elGO = new GameObject("_EngineLight"); elGO.transform.SetParent(rocketRoot); elGO.transform.localPosition = Vector3.down * 1.5f; _engineLight = elGO.AddComponent(); _engineLight.type = LightType.Point; _engineLight.color = new Color(1f, 0.55f, 0.1f); _engineLight.intensity = 0f; _engineLight.range = 10f; GameObject spotGO = new GameObject("_RocketSpotLight"); spotGO.transform.SetParent(rocketRoot); spotGO.transform.localPosition = Vector3.up * 2f; spotGO.transform.localRotation = Quaternion.Euler(90f, 0f, 0f); _rocketSpotLight = spotGO.AddComponent(); _rocketSpotLight.type = LightType.Spot; _rocketSpotLight.color = new Color(0.8f, 0.9f, 1f); _rocketSpotLight.intensity = 0f; _rocketSpotLight.range = 15f; _rocketSpotLight.spotAngle = 80f; } private System.Collections.IEnumerator AtmosphereLayerFlash(string planetName) { yield return new WaitForSeconds(1.5f); yield return StartCoroutine(DoFlash(GetLayerColor(planetName, 0), 0.12f)); yield return new WaitForSeconds(1.8f); yield return StartCoroutine(DoFlash(GetLayerColor(planetName, 1), 0.15f)); yield return new WaitForSeconds(2f); yield return StartCoroutine(DoFlash(GetLayerColor(planetName, 2), 0.25f)); } private System.Collections.IEnumerator DoFlash(Color c, float duration) { if (vignetteUI == null) yield break; float t = 0f; while (t < duration) { t += Time.deltaTime; float alpha = Mathf.Sin(t / duration * Mathf.PI) * c.a; vignetteUI.color = new Color(c.r, c.g, c.b, alpha); yield return null; } vignetteUI.color = new Color(0f, 0f, 0f, 0f); } private System.Collections.IEnumerator LightningEffect() { float timer = 0f; while (_isActive) { timer += Time.deltaTime; if (timer > Random.Range(0.3f, 1.2f)) { timer = 0f; if (vignetteUI != null) { vignetteUI.color = new Color(0.9f, 0.95f, 1f, 0.6f); yield return new WaitForSeconds(0.05f); vignetteUI.color = new Color(0f, 0f, 0f, 0f); yield return new WaitForSeconds(0.04f); vignetteUI.color = new Color(0.9f, 0.95f, 1f, 0.3f); yield return new WaitForSeconds(0.03f); vignetteUI.color = new Color(0f, 0f, 0f, 0f); } } yield return null; } } private Color GetLayerColor(string planetName, int layer) { switch (planetName) { case "Земля": { Color[] c = { new Color(0.5f, 0.8f, 1f, 0.3f), new Color(0.3f, 0.5f, 1f, 0.4f), new Color(0.1f, 0.1f, 0.4f, 0.5f) }; return c[layer]; } case "Марс": { Color[] c = { new Color(0.9f, 0.5f, 0.2f, 0.3f), new Color(0.7f, 0.3f, 0.1f, 0.35f), new Color(0.5f, 0.1f, 0.05f, 0.4f) }; return c[layer]; } case "Венера": { Color[] c = { new Color(1f, 0.8f, 0.1f, 0.5f), new Color(0.9f, 0.6f, 0.05f, 0.6f), new Color(0.7f, 0.4f, 0f, 0.7f) }; return c[layer]; } case "Юпітер": { Color[] c = { new Color(1f, 0.6f, 0.2f, 0.4f), new Color(0.9f, 0.4f, 0.1f, 0.5f), new Color(0.7f, 0.2f, 0.05f, 0.6f) }; return c[layer]; } case "Нептун": { Color[] c = { new Color(0.2f, 0.4f, 1f, 0.4f), new Color(0.1f, 0.2f, 0.9f, 0.5f), new Color(0.05f, 0.1f, 0.7f, 0.65f) }; return c[layer]; } default: { Color[] c = { new Color(0.5f, 0.7f, 1f, 0.3f), new Color(0.3f, 0.5f, 0.9f, 0.4f), new Color(0.1f, 0.2f, 0.6f, 0.5f) }; return c[layer]; } } } public struct PlanetData { public string name; public float atmosphereThickness; public float exposure; public Color skyTint; public Color groundColor; public bool hasFog; public float fogDensity; public Color fogColor; public float sunLightIntensity; } }