262 lines
8.6 KiB
C#
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
|
|
}
|
|
}
|