Files
aibis-dream/Assets/Scripts/MiniGame/HuoShan/SalesSystem/GlowTubeRenderer.cs
T

262 lines
8.6 KiB
C#

using UnityEngine;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 辉光管渲染器 - 在 SpriteRenderer 上叠加等离子体噪声发光效果
/// 移植自 Web_EmotionWave glow-tube 分支
/// 核心视觉:底部辉光 → 等离子体噪声层 → 中心高光 → 火花 → 反抗脉冲闪光
/// </summary>
[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<SpriteRenderer>();
_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;
}
/// <summary>
/// 外部触发一次反抗脉冲(旋钮回弹时调用)
/// </summary>
public void TriggerPulse(float strength = 0.5f)
{
_resistancePulse = Mathf.Max(_resistancePulse, strength);
}
#endregion
}
}