Files
ScienceLab.AtmosphericPressure/Assets/Scripts/TroposphereEffects.cs
2026-05-29 18:21:53 +03:00

358 lines
12 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TroposphereEffects : MonoBehaviour
{
[Header("Ìàòåð³àëè õìàð")]
public Material[] cloudMaterials;
[Header("Äîù")]
public GameObject rainPrefab;
[Header("Õìàðè")]
public int cloudGroupCount = 20;
public int particlesPerCloud = 4;
public float cloudGroupSpread = 0.4f;
public float cloudMinSize = 0.8f;
public float cloudMaxSize = 1.8f;
public float cloudOrbitSpeed = 0.5f;
[Header("Äîùîâ³ õìàðè")]
public int rainCloudGroupCount = 5;
public float rainDuration = 15f;
public float rainPause = 10f;
private float cloudRadius;
private Vector3 planetCenter;
[Header("Òóìàí")]
public Material fogMaterial;
private class RainCloud
{
public List<CloudPuff> puffs = new List<CloudPuff>();
public GameObject rainInstance;
public GameObject fogInstance;
public float timer;
public bool isRaining;
public bool isFogging;
public Vector3 position;
}
private class CloudPuff
{
public Transform transform;
public float orbitSpeed;
public Vector3 orbitAxis;
}
private List<CloudPuff> cloudPuffs = new List<CloudPuff>();
private List<RainCloud> rainClouds = new List<RainCloud>();
private Transform cloudRoot;
void Start()
{
planetCenter = this.transform.position;
cloudRadius = this.transform.lossyScale.x * 0.5f * 0.95f;
SpawnCloudGroups();
SpawnRainClouds();
}
void SpawnCloudGroups()
{
cloudRoot = new GameObject("CloudParticles").transform;
cloudRoot.parent = this.transform;
cloudRoot.localPosition = Vector3.zero;
for (int i = 0; i < cloudGroupCount; i++)
{
Vector3 groupCenter = planetCenter + Random.onUnitSphere * cloudRadius;
Vector3 toCenter = (planetCenter - groupCenter).normalized;
Vector3 axis = Quaternion.AngleAxis(Random.Range(-20f, 20f), Random.onUnitSphere) * Vector3.up;
float speed = cloudOrbitSpeed * Random.Range(0.7f, 1.3f);
for (int j = 0; j < particlesPerCloud; j++)
{
Vector3 pos = groupCenter + (Vector3)Random.insideUnitSphere * cloudGroupSpread;
CloudPuff puff = CreateCloudPuff(cloudRoot, pos, toCenter, axis, speed, false, i);
cloudPuffs.Add(puff);
}
}
}
GameObject CreateFog(Vector3 position)
{
GameObject fogObj = new GameObject("Fog");
fogObj.transform.parent = cloudRoot;
fogObj.transform.position = position;
ParticleSystem ps = fogObj.AddComponent<ParticleSystem>();
var main = ps.main;
main.loop = true;
main.playOnAwake = true;
main.maxParticles = 80;
main.startLifetime = new ParticleSystem.MinMaxCurve(10f, 16f);
main.startSpeed = new ParticleSystem.MinMaxCurve(0.03f, 0.08f);
main.startSize = new ParticleSystem.MinMaxCurve(0.2f, 0.6f);
main.startColor = new ParticleSystem.MinMaxGradient(
new Color(0.88f, 0.92f, 0.95f, 0.18f),
new Color(0.95f, 0.97f, 1f, 0.09f)
);
main.startRotation = new ParticleSystem.MinMaxCurve(0f, 360f * Mathf.Deg2Rad);
main.simulationSpace = ParticleSystemSimulationSpace.World;
var emission = ps.emission;
emission.rateOverTime = 8f;
var shape = ps.shape;
shape.enabled = true;
shape.shapeType = ParticleSystemShapeType.Sphere;
shape.radius = cloudGroupSpread * 2.5f;
shape.radiusThickness = 1f;
var velocityOverLifetime = ps.velocityOverLifetime;
velocityOverLifetime.enabled = true;
velocityOverLifetime.space = ParticleSystemSimulationSpace.World;
Vector3 tangent = Vector3.Cross((planetCenter - position).normalized, Vector3.up).normalized;
velocityOverLifetime.x = new ParticleSystem.MinMaxCurve(tangent.x * 0.08f - 0.04f, tangent.x * 0.08f + 0.04f);
velocityOverLifetime.y = new ParticleSystem.MinMaxCurve(-0.01f, 0.01f);
velocityOverLifetime.z = new ParticleSystem.MinMaxCurve(tangent.z * 0.08f - 0.04f, tangent.z * 0.08f + 0.04f);
var sizeOverLifetime = ps.sizeOverLifetime;
sizeOverLifetime.enabled = true;
AnimationCurve curve = new AnimationCurve();
curve.AddKey(0f, 0f);
curve.AddKey(0.15f, 1f);
curve.AddKey(0.75f, 0.85f);
curve.AddKey(1f, 0f);
sizeOverLifetime.size = new ParticleSystem.MinMaxCurve(1f, curve);
var colorOverLifetime = ps.colorOverLifetime;
colorOverLifetime.enabled = true;
Gradient gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[] {
new GradientColorKey(new Color(0.88f, 0.92f, 0.95f), 0f),
new GradientColorKey(new Color(0.95f, 0.97f, 1f), 0.5f),
new GradientColorKey(new Color(0.88f, 0.92f, 0.95f), 1f)
},
new GradientAlphaKey[] {
new GradientAlphaKey(0f, 0f),
new GradientAlphaKey(0.18f, 0.2f),
new GradientAlphaKey(0.12f, 0.7f),
new GradientAlphaKey(0f, 1f)
}
);
colorOverLifetime.color = new ParticleSystem.MinMaxGradient(gradient);
var rotationOverLifetime = ps.rotationOverLifetime;
rotationOverLifetime.enabled = true;
rotationOverLifetime.z = new ParticleSystem.MinMaxCurve(-0.2f, 0.2f);
var renderer = ps.GetComponent<ParticleSystemRenderer>();
renderer.renderMode = ParticleSystemRenderMode.Billboard;
renderer.sortingFudge = -3f;
if (fogMaterial != null)
renderer.material = fogMaterial;
ps.Play();
return fogObj;
}
void SpawnRainClouds()
{
for (int i = 0; i < rainCloudGroupCount; i++)
{
RainCloud rc = CreateRainCloud();
rainClouds.Add(rc);
}
}
RainCloud CreateRainCloud()
{
RainCloud rc = new RainCloud();
rc.position = planetCenter + Random.onUnitSphere * cloudRadius;
rc.timer = Random.Range(0f, rainDuration);
rc.isRaining = true;
Vector3 toCenter = (planetCenter - rc.position).normalized;
Vector3 axis = Quaternion.AngleAxis(Random.Range(-20f, 20f), Random.onUnitSphere) * Vector3.up;
float speed = cloudOrbitSpeed * Random.Range(0.6f, 1.0f);
for (int j = 0; j < particlesPerCloud; j++)
{
Vector3 pos = rc.position + (Vector3)Random.insideUnitSphere * cloudGroupSpread;
CloudPuff puff = CreateCloudPuff(cloudRoot, pos, toCenter, axis, speed, true, j);
rc.puffs.Add(puff);
cloudPuffs.Add(puff);
}
if (rainPrefab != null)
{
Vector3 dirToCenter = (planetCenter - rc.position).normalized;
Vector3 rainPos = rc.position + dirToCenter * (cloudGroupSpread + 0.9f);
rc.rainInstance = Instantiate(rainPrefab, rainPos, Quaternion.identity);
rc.rainInstance.transform.parent = cloudRoot;
rc.rainInstance.transform.localScale = Vector3.one * 0.5f;
RainDirector director = rc.rainInstance.AddComponent<RainDirector>();
director.planetCenter = planetCenter;
}
return rc;
}
CloudPuff CreateCloudPuff(Transform parent, Vector3 pos, Vector3 toCenter, Vector3 axis, float speed, bool dark, int texIndex)
{
ParticleSystem ps = new GameObject("CloudPuff").AddComponent<ParticleSystem>();
ps.transform.parent = parent;
ps.transform.position = pos;
var main = ps.main;
main.loop = false;
main.playOnAwake = true;
main.maxParticles = 1;
main.startLifetime = 999f;
main.startSpeed = 0f;
main.startSize = Random.Range(cloudMinSize, cloudMaxSize);
main.startColor = dark
? new Color(Random.Range(0.1f, 0.3f), Random.Range(0.1f, 0.3f), Random.Range(0.1f, 0.3f), Random.Range(0.7f, 0.9f))
: new Color(Random.Range(0.88f, 1f), Random.Range(0.88f, 1f), 1f, Random.Range(0.55f, 0.8f));
main.simulationSpace = ParticleSystemSimulationSpace.Local;
var emission = ps.emission;
emission.rateOverTime = 0f;
emission.SetBursts(new ParticleSystem.Burst[] { new ParticleSystem.Burst(0f, 1) });
var shape = ps.shape;
shape.enabled = false;
var renderer = ps.GetComponent<ParticleSystemRenderer>();
renderer.renderMode = ParticleSystemRenderMode.Billboard;
renderer.sortingFudge = -10f;
if (cloudMaterials != null && cloudMaterials.Length > 0)
{
renderer.material = cloudMaterials[texIndex % cloudMaterials.Length];
}
else
{
Material mat = new Material(Shader.Find("Universal Render Pipeline/Particles/Unlit"));
mat.SetFloat("_Surface", 1f);
mat.SetFloat("_Blend", 0f);
mat.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
mat.renderQueue = 3001;
mat.SetColor("_BaseColor", main.startColor.color);
renderer.material = mat;
}
ps.Play();
return new CloudPuff
{
transform = ps.transform,
orbitSpeed = speed,
orbitAxis = axis
};
}
void Update()
{
if (!Application.isPlaying) return;
Quaternion globalRotation = Quaternion.AngleAxis(cloudOrbitSpeed * Time.deltaTime, Vector3.up);
foreach (CloudPuff puff in cloudPuffs)
{
if (puff.transform == null) continue;
puff.transform.position = planetCenter + globalRotation * (puff.transform.position - planetCenter);
}
for (int i = 0; i < rainClouds.Count; i++)
{
RainCloud rc = rainClouds[i];
rc.timer -= Time.deltaTime;
if (rc.isRaining && rc.timer <= 0f)
{
if (rc.rainInstance != null)
Destroy(rc.rainInstance);
rc.isRaining = false;
rc.isFogging = true;
rc.timer = 20f;
Vector3 fogPos = rc.position + (planetCenter - rc.position).normalized * cloudGroupSpread * 1.5f;
rc.fogInstance = CreateFog(fogPos);
}
else if (rc.isFogging && rc.timer <= 0f)
{
if (rc.fogInstance != null)
Destroy(rc.fogInstance);
rc.isFogging = false;
rc.timer = rainPause;
foreach (CloudPuff puff in rc.puffs)
{
if (puff.transform != null)
Destroy(puff.transform.gameObject);
cloudPuffs.Remove(puff);
}
}
else if (!rc.isRaining && !rc.isFogging && rc.timer <= 0f)
{
foreach (CloudPuff puff in rc.puffs)
cloudPuffs.Remove(puff);
rainClouds[i] = CreateRainCloud();
}
if (rc.rainInstance != null)
rc.rainInstance.transform.position = planetCenter +
globalRotation * (rc.rainInstance.transform.position - planetCenter);
if (rc.fogInstance != null)
rc.fogInstance.transform.position = planetCenter +
globalRotation * (rc.fogInstance.transform.position - planetCenter);
}
}
void DestroyRainCloud(RainCloud rc)
{
if (rc.rainInstance != null)
Destroy(rc.rainInstance);
foreach (CloudPuff puff in rc.puffs)
{
if (puff.transform != null)
Destroy(puff.transform.gameObject);
}
}
public void SetRainEnabled(bool enabled)
{
foreach (RainCloud rc in rainClouds)
{
if (rc.rainInstance != null)
rc.rainInstance.SetActive(enabled);
}
}
public void SetCloudsEnabled(bool enabled)
{
if (cloudRoot != null)
cloudRoot.gameObject.SetActive(enabled);
}
void Awake()
{
enabled = false;
}
}