using System.Collections; using System.Collections.Generic; using UnityEngine; public class VenusEffects : MonoBehaviour { [Header("Õìàðè")] public Material cloudMaterial; public float planetRadius = 12f; public float cloudHeight = 900f; public float heightMultiplier = 1.5f; [Header("God Ray")] public GameObject godRayPrefab; [Range(0f, 1f)] public float mesosphereLevel = 1f; public float previousLevel = 1f; private List allClusters = new List(); private List groupIndices = new List(); private List godRays = new List(); private bool deadStateApplied = false; private const int TOTAL_CLUSTERS = 30; private const int GROUP_COUNT = 6; public List GetGodRays() => godRays; void Awake() { enabled = false; } public void InitState() { foreach (var ps in allClusters) if (ps != null) Destroy(ps.gameObject); allClusters.Clear(); groupIndices.Clear(); foreach (var obj in godRays) if (obj != null) Destroy(obj); godRays.Clear(); mesosphereLevel = 1f; previousLevel = 1f; deadStateApplied = false; CreateAllClusters(); } void CreateAllClusters() { float orbitRadius = planetRadius + (cloudHeight / 1000f) * heightMultiplier; List groupCenters = new List(); for (int g = 0; g < GROUP_COUNT; g++) { Vector3 dir; int attempts = 0; do { dir = Random.onUnitSphere; attempts++; } while (IsTooClose(dir, groupCenters, 0.6f) && attempts < 30); groupCenters.Add(dir); } int clustersPerGroup = TOTAL_CLUSTERS / GROUP_COUNT; int extraClusters = TOTAL_CLUSTERS - clustersPerGroup * GROUP_COUNT; for (int g = 0; g < GROUP_COUNT; g++) { int count = clustersPerGroup + (g < extraClusters ? 1 : 0); for (int i = 0; i < count; i++) { Vector3 offset = Random.onUnitSphere * Random.Range(0f, 2.5f); Vector3 dir = (groupCenters[g] + offset * 0.3f).normalized; Vector3 pos = transform.position + dir * orbitRadius; ParticleSystem ps = CreateCluster(pos, dir, orbitRadius); allClusters.Add(ps); groupIndices.Add(g); } } } ParticleSystem CreateCluster(Vector3 pos, Vector3 dir, float orbitRadius) { GameObject obj = new GameObject("CloudCluster"); obj.transform.parent = null; obj.transform.position = pos; ParticleSystem ps = obj.AddComponent(); var main = ps.main; main.loop = true; main.playOnAwake = true; main.maxParticles = 100; main.startLifetime = new ParticleSystem.MinMaxCurve(8f, 14f); main.startSpeed = new ParticleSystem.MinMaxCurve(0f, 0.1f); main.startSize = new ParticleSystem.MinMaxCurve(2f, 4f); main.startColor = new ParticleSystem.MinMaxGradient( new Color(1f, 0.85f, 0.2f, 0.7f), new Color(0.9f, 0.6f, 0.1f, 0.5f) ); main.simulationSpace = ParticleSystemSimulationSpace.World; main.gravityModifier = 0f; var emission = ps.emission; emission.rateOverTime = 15f; var shape = ps.shape; shape.enabled = true; shape.shapeType = ParticleSystemShapeType.Sphere; shape.radius = 2f; shape.radiusThickness = 1f; var colorOverLifetime = ps.colorOverLifetime; colorOverLifetime.enabled = true; Gradient gradient = new Gradient(); gradient.SetKeys( new GradientColorKey[] { new GradientColorKey(new Color(1f, 0.9f, 0.3f), 0f), new GradientColorKey(new Color(0.9f, 0.6f, 0.1f), 0.5f), new GradientColorKey(new Color(0.8f, 0.4f, 0.05f), 1f) }, new GradientAlphaKey[] { new GradientAlphaKey(0f, 0f), new GradientAlphaKey(0.7f, 0.2f), new GradientAlphaKey(0.6f, 0.7f), new GradientAlphaKey(0f, 1f) } ); colorOverLifetime.color = new ParticleSystem.MinMaxGradient(gradient); var noise = ps.noise; noise.enabled = true; noise.strength = 0.5f; noise.frequency = 0.4f; noise.scrollSpeed = 0.15f; noise.octaveCount = 2; var renderer = ps.GetComponent(); renderer.renderMode = ParticleSystemRenderMode.Billboard; renderer.sortingFudge = 4f; if (cloudMaterial != null) renderer.material = cloudMaterial; ps.Play(); StartCoroutine(OrbitCluster(obj.transform, dir, orbitRadius)); return ps; } bool IsTooClose(Vector3 dir, List existing, float minDot) { foreach (var d in existing) if (Vector3.Dot(dir, d) > minDot) return true; return false; } IEnumerator OrbitCluster(Transform cluster, Vector3 axis, float radius) { Vector3 orbitAxis = Vector3.Cross(axis, Random.onUnitSphere).normalized; float speed = Random.Range(2f, 5f); float wobbleSpeed = Random.Range(0.15f, 0.4f); float wobbleAmount = Random.Range(0.2f, 0.5f); while (cluster != null) { cluster.RotateAround(transform.position, orbitAxis, speed * Time.deltaTime); float wobble = Mathf.Sin(Time.time * wobbleSpeed) * wobbleAmount; cluster.position = transform.position + (cluster.position - transform.position).normalized * (radius + wobble); yield return null; } } void UpdateClouds() { float level = mesosphereLevel; for (int i = 0; i < allClusters.Count; i++) { var ps = allClusters[i]; if (ps == null) continue; var emission = ps.emission; var main = ps.main; if (level >= 0.7f) { float t = Mathf.InverseLerp(1f, 0.7f, level); emission.rateOverTime = Mathf.Lerp(15f, 8f, t); main.startColor = new ParticleSystem.MinMaxGradient( new Color(1f, 0.85f, 0.2f, Mathf.Lerp(0.7f, 0.5f, t)), new Color(0.9f, 0.6f, 0.1f, Mathf.Lerp(0.5f, 0.35f, t)) ); } else if (level >= 0.5f) { int groupId = groupIndices[i]; bool isGroupCluster = groupId < 3; float t = Mathf.InverseLerp(0.7f, 0.5f, level); if (isGroupCluster) { emission.rateOverTime = Mathf.Lerp(8f, 12f, t); main.startColor = new ParticleSystem.MinMaxGradient( new Color(1f, 0.85f, 0.2f, 0.6f), new Color(0.9f, 0.6f, 0.1f, 0.4f) ); } else { emission.rateOverTime = Mathf.Lerp(8f, 0f, t); main.startColor = new ParticleSystem.MinMaxGradient( new Color(1f, 0.85f, 0.2f, Mathf.Lerp(0.5f, 0f, t)), new Color(0.9f, 0.6f, 0.1f, Mathf.Lerp(0.35f, 0f, t)) ); } } else if (level >= 0.3f) { int groupId = groupIndices[i]; bool isGroupCluster = groupId < 2; float t = Mathf.InverseLerp(0.5f, 0.3f, level); if (isGroupCluster) { emission.rateOverTime = Mathf.Lerp(12f, 5f, t); main.startColor = new ParticleSystem.MinMaxGradient( new Color(1f, 0.85f, 0.2f, Mathf.Lerp(0.6f, 0.2f, t)), new Color(0.9f, 0.6f, 0.1f, Mathf.Lerp(0.4f, 0.1f, t)) ); } else { emission.rateOverTime = 0f; main.startColor = new ParticleSystem.MinMaxGradient( new Color(1f, 0.85f, 0.2f, 0f), new Color(0.9f, 0.6f, 0.1f, 0f) ); } } else { emission.rateOverTime = 0f; main.startColor = new ParticleSystem.MinMaxGradient( new Color(1f, 0.85f, 0.2f, 0f), new Color(0.9f, 0.6f, 0.1f, 0f) ); } } } void SpawnGodRay(float scale) { if (godRayPrefab == null) return; float angle = Random.Range(0f, 360f); Vector3 dir = Quaternion.Euler(0f, angle, Random.Range(-30f, 30f)) * Vector3.right; Vector3 pos = transform.position + dir * (planetRadius * 1.1f); GameObject ray = Instantiate(godRayPrefab, pos, Quaternion.LookRotation(dir)); ray.transform.localScale = Vector3.one * scale; godRays.Add(ray); } void UpdateGodRays(float level) { foreach (var obj in godRays) if (obj != null) Destroy(obj); godRays.Clear(); if (level > 0.5f) return; float t = Mathf.InverseLerp(0.5f, 0f, level); int rayCount = Mathf.RoundToInt(Mathf.Lerp(2f, 8f, t)); float rayScale = Mathf.Lerp(0.5f, 3f, t); for (int i = 0; i < rayCount; i++) SpawnGodRay(rayScale); } void ApplyDeadState() { if (deadStateApplied) return; deadStateApplied = true; var venusDeath = GameManager.Instance.GetCurrentPlanet() .planetTransform?.GetComponent(); if (venusDeath != null) venusDeath.ApplyDeadColors(); } void Update() { if (!Application.isPlaying) return; if (mesosphereLevel >= 1f && previousLevel < 1f) InitState(); previousLevel = mesosphereLevel; UpdateClouds(); UpdateGodRays(mesosphereLevel); if (mesosphereLevel <= 0f && !deadStateApplied) ApplyDeadState(); } public void CleanUp() { foreach (var obj in allClusters) if (obj != null) Destroy(obj.gameObject); allClusters.Clear(); foreach (var obj in godRays) if (obj != null) Destroy(obj); godRays.Clear(); enabled = false; } }