using UnityEngine; namespace AibisDream.MiniGame.HuoShan { /// /// 辉光管渲染器 - 在 SpriteRenderer 上叠加等离子体噪声发光效果 /// 移植自 Web_EmotionWave glow-tube 分支 /// 核心视觉:底部辉光 → 等离子体噪声层 → 中心高光 → 火花 → 反抗脉冲闪光 /// [RequireComponent(typeof(SpriteRenderer))] public class GlowTubeRenderer : MonoBehaviour { [Header("Intensity")] [Tooltip("基础强度 (0=熄灭, 1=最亮)")] [Range(0f, 1.5f)] [SerializeField] private float intensity = 1f; [Header("Noise")] [Tooltip("噪声纹理尺寸")] [SerializeField] private int textureSize = 128; [Tooltip("噪声缩放")] [SerializeField] private float noiseScale = 0.06f; [Tooltip("噪声流动速度")] [SerializeField] private float noiseSpeed = 1.5f; [Header("Flicker")] [Tooltip("闪烁速率")] [SerializeField] private float flickerRate = 8f; [Tooltip("闪烁幅度")] [SerializeField] private float flickerAmplitude = 0.15f; [Header("Resistance Pulse")] [Tooltip("反抗脉冲衰减速率")] [SerializeField] private float pulseDecayRate = 9f; [Header("Colors")] [SerializeField] private Color lowColor = new Color(0.31f, 0.08f, 0.16f, 1f); [SerializeField] private Color midColor = new Color(0.78f, 0.15f, 0.27f, 1f); [SerializeField] private Color highColor = new Color(1f, 0.46f, 0.39f, 1f); [SerializeField] private Color coreColor = new Color(1f, 0.78f, 0.67f, 1f); [Header("Glow")] [Tooltip("外部辉光颜色")] [SerializeField] private Color outerGlowColor = new Color(1f, 0.47f, 0.31f, 0.4f); public float Intensity { get => intensity; set => intensity = Mathf.Clamp(value, 0f, 1.5f); } private SpriteRenderer _sr; private Texture2D _tex; private Sprite _sprite; private Color32[] _pixels; private float _time; private float _resistancePulse; private float _nextPulseTime; // Perlin noise offset seed private float _seedX; private float _seedY; private void Awake() { _sr = GetComponent(); _seedX = Random.Range(0f, 1000f); _seedY = Random.Range(0f, 1000f); CreateTexture(); } private void OnEnable() { if (_tex == null) CreateTexture(); _sr.sprite = _sprite; } private void OnDisable() { _sr.sprite = null; } private void OnDestroy() { if (_tex != null) Destroy(_tex); if (_sprite != null) Destroy(_sprite); } private void Update() { float dt = Time.deltaTime; _time += dt; UpdateResistancePulse(dt); RenderToTexture(); } private void CreateTexture() { int w = textureSize; int h = Mathf.Max(1, textureSize / 3); _tex = new Texture2D(w, h, TextureFormat.RGBA32, false) { filterMode = FilterMode.Bilinear, wrapMode = TextureWrapMode.Clamp }; _pixels = new Color32[w * h]; float ppu = w / (_sr.bounds.size.x > 0.01f ? _sr.bounds.size.x : 1f); ppu = Mathf.Max(ppu, 50f); _sprite = Sprite.Create(_tex, new Rect(0, 0, w, h), new Vector2(0.5f, 0.5f), ppu); _sr.sprite = _sprite; } #region Rendering private void RenderToTexture() { if (_tex == null) return; int w = _tex.width; int h = _tex.height; float di = Mathf.Clamp(intensity + _resistancePulse, 0f, 1.5f); if (di < 0.01f) { ClearPixels(); _tex.SetPixels32(_pixels); _tex.Apply(); return; } float baseFlicker = 1f + Mathf.Sin(_time * flickerRate) * flickerAmplitude * di; float burstFlicker = 1f; if (di > 0.7f) { float bc = Mathf.PerlinNoise(_time * 2f + _seedX, _seedY) - 0.5f; if (bc > 0.35f) burstFlicker = 1.3f + bc * 0.4f; } float totalFlicker = baseFlicker * burstFlicker; for (int py = 0; py < h; py++) { for (int px = 0; px < w; px++) { int idx = py * w + px; float cx = (px / (float)w - 0.5f) * 2f; float cy = (py / (float)h - 0.5f) * 2f; float ellipse = cx * cx * 1.1f + cy * cy * 2.5f; if (ellipse > 1f) { _pixels[idx] = new Color32(0, 0, 0, 0); continue; } float edgeFade = 1f - Mathf.Pow(ellipse, 1.5f); float nx = px * noiseScale + _seedX; float ny = py * noiseScale + _seedY; float nt = _time * noiseSpeed; float n1 = Mathf.PerlinNoise(nx + nt, ny + nt * 0.7f) * 2f - 1f; float n2 = (Mathf.PerlinNoise(nx * 2f + nt * 1.3f, ny * 2f - nt * 0.5f) * 2f - 1f) * 0.5f; float n3 = (Mathf.PerlinNoise(nx * 0.5f - nt * 0.4f, ny * 0.5f + nt * 0.9f) * 2f - 1f) * 0.3f; float val = (n1 + n2 + n3) * 0.55f + 0.5f; val = Mathf.Clamp01(val); val *= edgeFade * di * totalFlicker; if (val < 0.02f) { _pixels[idx] = new Color32(5, 4, 6, (byte)(255 * di * edgeFade * 0.3f)); continue; } Color c; if (val < 0.3f) { float t = val / 0.3f; c = Color.Lerp(Color.black, lowColor, t); c.a = val * 1.5f; } else if (val < 0.6f) { float t = (val - 0.3f) / 0.3f; c = Color.Lerp(midColor, highColor, t); c.a = 0.45f + t * 0.25f; } else { float t = (val - 0.6f) / 0.4f; c = Color.Lerp(highColor, coreColor, t); c.a = 0.7f + t * 0.3f; } c.a *= di; c.a = Mathf.Min(1f, c.a); // Add center highlight if (di > 0.1f) { float hlDist = Mathf.Sqrt(cx * cx + cy * cy); float hl = Mathf.Max(0f, 1f - hlDist / 0.7f) * 0.25f * di; c = Color.Lerp(c, coreColor, hl); } // Resistance pulse flash overlay if (_resistancePulse > 0.05f) { float flash = _resistancePulse * 0.4f * edgeFade; c = Color.Lerp(c, new Color(1f, 0.7f, 0.55f, 1f), Mathf.Min(0.5f, flash)); } _pixels[idx] = c; } } _tex.SetPixels32(_pixels); _tex.Apply(); } private void ClearPixels() { var black = new Color32(0, 0, 0, 0); for (int i = 0; i < _pixels.Length; i++) _pixels[i] = black; } #endregion #region Resistance Pulse private void UpdateResistancePulse(float dt) { if (intensity < 0.5f && intensity > 0.03f) { float suppression = 1f - intensity; if (_time > _nextPulseTime) { _resistancePulse = Mathf.Max(_resistancePulse, suppression * (0.25f + Random.value * 0.5f)); _nextPulseTime = _time + 0.2f + Random.value * (2.5f - suppression * 2f); } } _resistancePulse *= Mathf.Exp(-dt * pulseDecayRate); if (_resistancePulse < 0.008f) _resistancePulse = 0f; } /// /// 外部触发一次反抗脉冲(旋钮回弹时调用) /// public void TriggerPulse(float strength = 0.5f) { _resistancePulse = Mathf.Max(_resistancePulse, strength); } #endregion } }