using UnityEngine; using UnityEngine.UI; /// /// Процедурный рендерер объемных, мягких облачных волн эхолокации для Главного Меню. /// Формирует многослойный шлейф с плавным гауссовым рассеиванием и органическими пульсациями, /// в точности повторяя эффект стелс-эхолокации из геймплея. /// [RequireComponent(typeof(CanvasRenderer))] public class MenuEchoWaveRenderer : MaskableGraphic { [Range(36, 120)] public int segments = 72; [Tooltip("Текущий радиус волны в пикселях")] public float radius = 0f; [Tooltip("Широкая полоса рассеивания тумана")] public float thickness = 180f; [Tooltip("Общая прозрачность волны (0 - 1)")] [Range(0f, 1f)] public float waveAlpha = 1f; [Header("Цветовая палитра тумана")] public Color waveColor = new Color(1.00f, 0.82f, 0.20f, 0.40f); // Рассеянный золотой туман public Color coreColor = new Color(1.00f, 0.94f, 0.50f, 0.70f); // Мягкое ядро свечения public void SetWave(float currentRadius, float currentThickness, float currentAlpha, Color baseColor, Color highlightColor) { radius = currentRadius; thickness = currentThickness; waveAlpha = Mathf.Clamp01(currentAlpha); waveColor = baseColor; coreColor = highlightColor; SetVerticesDirty(); } protected override void OnPopulateMesh(VertexHelper vh) { vh.Clear(); if (radius <= 1f || thickness <= 1f || waveAlpha <= 0.002f) return; // Коэффициенты распределения слоев для мягкого профиля "облака" // 6 колец = 5 полос плавного спада (хвост тумана -> ядро -> мягкий передний фронт) float[] ringOffsets = new float[] { -0.65f, -0.32f, -0.05f, 0.15f, 0.38f, 0.60f }; float[] ringAlphas = new float[] { 0.00f, 0.30f, 0.65f, 0.75f, 0.25f, 0.00f }; int ringCount = ringOffsets.Length; Color[] ringColors = new Color[ringCount]; for (int r = 0; r < ringCount; r++) { float t = (float)r / (ringCount - 1); // Плавное смешивание базового цвета дымки и ядра Color col = Color.Lerp(waveColor, coreColor, Mathf.Sin(t * Mathf.PI)); col.a = ringAlphas[r] * waveAlpha; ringColors[r] = col; } float angleStep = 360f / segments; // Предварительный буфер для двух шагов угла (текущий и следующий) Vector2[] pts1 = new Vector2[ringCount]; Vector2[] pts2 = new Vector2[ringCount]; CalculateRingPoints(0f, ringOffsets, pts1); for (int i = 0; i < segments; i++) { float a2 = (i + 1) * angleStep * Mathf.Deg2Rad; CalculateRingPoints(a2, ringOffsets, pts2); // Соединяем соседние кольца квадами (2 треугольника на слой) for (int r = 0; r < ringCount - 1; r++) { int vIdx = vh.currentVertCount; vh.AddVert(pts1[r], ringColors[r], Vector2.zero); vh.AddVert(pts1[r + 1], ringColors[r + 1], Vector2.zero); vh.AddVert(pts2[r + 1], ringColors[r + 1], Vector2.zero); vh.AddVert(pts2[r], ringColors[r], Vector2.zero); vh.AddTriangle(vIdx, vIdx + 1, vIdx + 2); vh.AddTriangle(vIdx, vIdx + 2, vIdx + 3); } // Переносим точки для следующего шага for (int r = 0; r < ringCount; r++) { pts1[r] = pts2[r]; } } } private void CalculateRingPoints(float radAngle, float[] offsets, Vector2[] outPts) { Vector2 dir = new Vector2(Mathf.Cos(radAngle), Mathf.Sin(radAngle)); // Органическая деформация облака: легкие акустические неровности и клубы float billow1 = Mathf.Sin(radAngle * 4f + radius * 0.015f) * (thickness * 0.07f); float billow2 = Mathf.Cos(radAngle * 7f - radius * 0.010f) * (thickness * 0.04f); float organicRadius = radius + billow1 + billow2; for (int r = 0; r < offsets.Length; r++) { float ringR = Mathf.Max(0f, organicRadius + offsets[r] * thickness); outPts[r] = dir * ringR; } } }