Files
aibis-dream/Assets/Scripts/MiniGame/HuoShan/Waveform/WaveformRenderer.cs
T
2026-01-22 16:27:05 +08:00

776 lines
28 KiB
C#

using UnityEngine;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 波形渲染器 - 单个波形的渲染
/// 支持噪波效果和清晰度控制,像录音软件中的声波
/// </summary>
[ExecuteInEditMode]
[RequireComponent(typeof(LineRenderer))]
public class WaveformRenderer : MonoBehaviour
{
public enum PreviewMode
{
Clean, // 清晰波形,无噪波
WithNoise, // 显示噪波效果
Runtime // 模拟运行时(噪波+清晰度)
}
public enum ConfigPreviewType
{
None, // 不使用Config,使用下方的手动设置
Emotion, // 预览情绪波形配置
Logic, // 预览逻辑波形配置
Joke, // 预览笑话波形配置
FilterOutput, // 预览滤波器输出配置
NormalOutput // 预览正常输出配置
}
[Header("═══ 从Config预览(拖入Config后选择类型即可预览)═══")]
[Tooltip("拖入WaveformConfig资源")]
[SerializeField] private WaveformConfig previewConfig;
[Tooltip("选择要预览的波形类型(选择后立即生效)")]
[SerializeField] private ConfigPreviewType previewFromConfig = ConfigPreviewType.None;
[Header("═══ 预览效果 ═══")]
[SerializeField] private PreviewMode editorPreviewMode = PreviewMode.Clean;
[Tooltip("勾选强制刷新波形显示")]
[SerializeField] private bool forceRefresh = false;
[Header("═══ 波形设置(手动调整或从Config应用)═══")]
[SerializeField] private WaveformType waveType = WaveformType.Emotion;
[SerializeField] private float waveHeight = 1f;
[SerializeField] private float waveWidth = 10f;
[SerializeField] private float waveSpeed = 2f;
[SerializeField] private float frequency = 1f;
[Header("噪波设置")]
[SerializeField] private float noiseAmount = 0.8f; // 噪波强度 (0=无噪波, 1=完全噪波)
[SerializeField] private float noiseFrequency = 15f; // 噪波频率
[SerializeField] private float noiseSpeed = 5f; // 噪波变化速度
[Header("清晰度设置")]
[SerializeField] private float clarity = 0f; // 清晰度 (0=模糊, 1=完全清晰)
[SerializeField] private float clarityPosition = 0.5f; // 清晰位置 (0-1)
[SerializeField] private float clarityWidth = 0.1f; // 清晰区域宽度
[Header("绘图设置")]
[SerializeField] private int resolution = 200;
[SerializeField] private float lineWidth = 0.05f;
[Header("颜色设置")]
[SerializeField] private Color primaryColor = Color.cyan;
[SerializeField] private Color secondaryColor = Color.blue;
[SerializeField] private Color noiseColor = new Color(0.5f, 0.5f, 0.5f, 0.5f);
[SerializeField] private Color clearColor = Color.green;
[Header("发光点")]
[SerializeField] private bool showGlowPoint = true;
[SerializeField] private SpriteRenderer glowPointRenderer;
[SerializeField] private float glowSize = 0.15f;
private LineRenderer _lineRenderer;
private Vector3[] _points;
private float _phaseOffset = 0f;
private float _noisePhase = 0f;
private float _transformProgress = 0f;
private WaveformType _transformFromType;
private WaveformType _transformToType;
private bool _isTransforming = false;
private float _currentAlpha = 1f;
// 随机噪波种子
private float[] _noiseSeed;
// 合成模式参数
private float _logicWeight = 0.3f;
private float _jokeWeight = 0.85f;
private float _synthesisProgress = 1f;
public WaveformType WaveType => waveType;
public bool IsTransforming => _isTransforming;
public float Clarity => clarity;
public float ClarityPosition => clarityPosition;
public float ClarityWidth => clarityWidth;
public float NoiseAmount => noiseAmount;
public Color PrimaryColor => primaryColor;
public Color SecondaryColor => secondaryColor;
public float CurrentAlpha => _currentAlpha;
private void Awake()
{
InitializeLineRenderer();
SetupGlowPoint();
InitializeNoise();
}
/// <summary>
/// 响应 Inspector 中的值变化
/// </summary>
private void OnValidate()
{
// 确保在编辑器中能即时预览
if (_lineRenderer == null)
{
_lineRenderer = GetComponent<LineRenderer>();
}
if (_lineRenderer != null)
{
_lineRenderer.startWidth = lineWidth;
_lineRenderer.endWidth = lineWidth;
if (_points == null || _points.Length != resolution)
{
_points = new Vector3[resolution];
_lineRenderer.positionCount = resolution;
}
UpdateColors();
}
// 检查Config预览设置
if (previewFromConfig != ConfigPreviewType.None && previewConfig == null)
{
Debug.LogWarning("WaveformRenderer: 请先拖入 WaveformConfig 资源到 Preview Config 字段!");
}
// 强制刷新按钮
if (forceRefresh)
{
forceRefresh = false;
InitializeNoise();
// 显示实际使用的参数
if (previewFromConfig != ConfigPreviewType.None && previewConfig != null)
{
var settings = GetPreviewSettings();
if (settings != null)
{
Debug.Log($"WaveformRenderer: 预览Config [{previewFromConfig}] - 类型:{settings.waveType}, 高度:{settings.waveHeight}, 速度:{settings.waveSpeed}, 频率:{settings.frequency}");
}
}
else
{
Debug.Log($"WaveformRenderer: 使用本地参数 - 类型:{waveType}, 高度:{waveHeight}, 速度:{waveSpeed}, 频率:{frequency}");
}
}
if (_noiseSeed == null || _noiseSeed.Length != resolution)
{
InitializeNoise();
}
// 在编辑器中立即更新波形
if (!Application.isPlaying)
{
UpdateWaveform();
}
}
private void InitializeLineRenderer()
{
_lineRenderer = GetComponent<LineRenderer>();
if (_lineRenderer == null) return;
_lineRenderer.useWorldSpace = false;
_lineRenderer.positionCount = resolution;
_lineRenderer.startWidth = lineWidth;
_lineRenderer.endWidth = lineWidth;
_points = new Vector3[resolution];
UpdateColors();
}
private void InitializeNoise()
{
_noiseSeed = new float[resolution];
for (int i = 0; i < resolution; i++)
{
_noiseSeed[i] = Random.Range(0f, 100f);
}
}
private void SetupGlowPoint()
{
if (!showGlowPoint) return;
if (glowPointRenderer == null)
{
GameObject glowObj = new GameObject("GlowPoint");
glowObj.transform.SetParent(transform);
glowObj.transform.localPosition = Vector3.zero;
glowPointRenderer = glowObj.AddComponent<SpriteRenderer>();
Texture2D texture = new Texture2D(32, 32);
Color[] colors = new Color[32 * 32];
for (int i = 0; i < colors.Length; i++)
{
float x = (i % 32) / 31f;
float y = (i / 32) / 31f;
float distance = Vector2.Distance(new Vector2(x, y), new Vector2(0.5f, 0.5f));
colors[i] = new Color(1, 1, 1, Mathf.Clamp01(1 - distance * 2));
}
texture.SetPixels(colors);
texture.Apply();
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, 32, 32), new Vector2(0.5f, 0.5f));
glowPointRenderer.sprite = sprite;
}
glowPointRenderer.color = primaryColor;
glowPointRenderer.transform.localScale = Vector3.one * glowSize;
}
private void Update()
{
if (_lineRenderer == null) InitializeLineRenderer();
if (_points == null) _points = new Vector3[resolution];
if (_noiseSeed == null) InitializeNoise();
// 获取实际波形速度(优先使用Config预览)
float actualWaveSpeed = waveSpeed;
bool isEditor = !Application.isPlaying;
if (isEditor && previewFromConfig != ConfigPreviewType.None && previewConfig != null)
{
WaveformSettings settings = GetPreviewSettings();
if (settings != null)
{
actualWaveSpeed = settings.waveSpeed;
}
}
_phaseOffset += Time.deltaTime * actualWaveSpeed;
_noisePhase += Time.deltaTime * noiseSpeed;
UpdateWaveform();
}
private void UpdateWaveform()
{
if (_lineRenderer == null || _points == null) return;
// 确定当前使用的预览模式
bool isEditor = !Application.isPlaying;
PreviewMode currentMode = isEditor ? editorPreviewMode : PreviewMode.Runtime;
// 获取实际使用的参数(优先使用Config预览)
WaveformType actualWaveType = waveType;
float actualFrequency = frequency;
float actualWaveHeight = waveHeight;
float actualWaveSpeed = waveSpeed;
if (isEditor && previewFromConfig != ConfigPreviewType.None && previewConfig != null)
{
WaveformSettings settings = GetPreviewSettings();
if (settings != null)
{
actualWaveType = settings.waveType;
actualFrequency = settings.frequency;
actualWaveHeight = settings.waveHeight;
actualWaveSpeed = settings.waveSpeed;
}
}
for (int i = 0; i < resolution; i++)
{
float x = ((float)i / resolution) * waveWidth - waveWidth * 0.5f;
float normalizedX = (float)i / resolution;
float timeOffset = normalizedX * actualFrequency + _phaseOffset;
// 获取基础波形值
float baseWaveValue;
if (_isTransforming)
{
float fromValue = GetWaveValue(_transformFromType, timeOffset, normalizedX);
float toValue = GetWaveValue(_transformToType, timeOffset, normalizedX);
baseWaveValue = Mathf.Lerp(fromValue, toValue, _transformProgress);
}
else
{
baseWaveValue = GetWaveValue(actualWaveType, timeOffset, normalizedX);
}
float finalValue;
switch (currentMode)
{
case PreviewMode.Clean:
// 清晰模式:直接显示波形
finalValue = baseWaveValue;
break;
case PreviewMode.WithNoise:
// 噪波模式:只显示噪波效果,不考虑清晰度
float noise = GenerateNoise(i, normalizedX);
finalValue = Mathf.Lerp(noise, baseWaveValue, 1f - noiseAmount);
break;
case PreviewMode.Runtime:
default:
// 运行时模式:应用噪波和清晰度
float localClarity = CalculateLocalClarity(normalizedX);
float runtimeNoise = GenerateNoise(i, normalizedX);
finalValue = Mathf.Lerp(runtimeNoise, baseWaveValue, localClarity);
break;
}
_points[i] = new Vector3(x, finalValue * actualWaveHeight, 0f);
}
_lineRenderer.SetPositions(_points);
UpdateLineColors();
if (showGlowPoint && glowPointRenderer != null && resolution > 0)
{
glowPointRenderer.transform.localPosition = _points[resolution - 1];
}
}
/// <summary>
/// 获取当前预览的Config设置
/// </summary>
private WaveformSettings GetPreviewSettings()
{
if (previewConfig == null) return null;
switch (previewFromConfig)
{
case ConfigPreviewType.Emotion:
return previewConfig.emotionWaveform;
case ConfigPreviewType.Logic:
return previewConfig.logicWaveform;
case ConfigPreviewType.Joke:
return previewConfig.jokeWaveform;
case ConfigPreviewType.FilterOutput:
return previewConfig.filterOutputWaveform;
case ConfigPreviewType.NormalOutput:
return previewConfig.normalOutputWaveform;
default:
return null;
}
}
/// <summary>
/// 计算指定位置的局部清晰度
/// </summary>
private float CalculateLocalClarity(float normalizedX)
{
// 基础清晰度
float baseClarity = clarity;
// 如果清晰度大于0,在清晰位置附近更清晰
if (clarity > 0)
{
float distanceFromClear = Mathf.Abs(normalizedX - clarityPosition);
float clearZone = 1f - Mathf.Clamp01(distanceFromClear / clarityWidth);
baseClarity = Mathf.Max(clarity, clearZone);
}
return Mathf.Clamp01(baseClarity);
}
/// <summary>
/// 生成噪波(像录音软件中的声波)
/// </summary>
private float GenerateNoise(int index, float normalizedX)
{
float seed = _noiseSeed[index];
// 多层噪波叠加,产生声波效果
float noise = 0f;
// 低频噪波 - 整体起伏
noise += Mathf.PerlinNoise(seed + _noisePhase * 0.3f, normalizedX * 2f) * 0.6f;
// 中频噪波 - 细节
noise += Mathf.PerlinNoise(seed * 2f + _noisePhase * 0.7f, normalizedX * 5f) * 0.3f;
// 高频噪波 - 毛刺
noise += (Mathf.PerlinNoise(seed * 3f + _noisePhase, normalizedX * noiseFrequency) - 0.5f) * 0.4f;
// 随机尖峰(像声波的峰值)
float spike = Mathf.Sin((normalizedX + _noisePhase * 0.1f) * Mathf.PI * 20f);
spike = Mathf.Pow(Mathf.Abs(spike), 3f) * Mathf.Sign(spike);
noise += spike * 0.2f;
// 归一化到 -1 到 1
noise = (noise - 0.5f) * 2f * noiseAmount;
return Mathf.Clamp(noise, -1f, 1f);
}
/// <summary>
/// 更新线条颜色(清晰区域显示不同颜色)
/// </summary>
private void UpdateLineColors()
{
if (_lineRenderer == null) return;
Gradient gradient = new Gradient();
if (clarity > 0.5f)
{
// 有清晰区域时,显示清晰位置的颜色变化
float clearStart = Mathf.Clamp01(clarityPosition - clarityWidth);
float clearEnd = Mathf.Clamp01(clarityPosition + clarityWidth);
gradient.SetKeys(
new GradientColorKey[] {
new GradientColorKey(primaryColor, 0f),
new GradientColorKey(primaryColor, clearStart),
new GradientColorKey(clearColor, clarityPosition),
new GradientColorKey(primaryColor, clearEnd),
new GradientColorKey(secondaryColor, 1f)
},
new GradientAlphaKey[] {
new GradientAlphaKey(_currentAlpha, 0f),
new GradientAlphaKey(_currentAlpha, 1f)
}
);
}
else
{
gradient.SetKeys(
new GradientColorKey[] {
new GradientColorKey(primaryColor, 0f),
new GradientColorKey(secondaryColor, 1f)
},
new GradientAlphaKey[] {
new GradientAlphaKey(_currentAlpha, 0f),
new GradientAlphaKey(_currentAlpha, 1f)
}
);
}
_lineRenderer.colorGradient = gradient;
}
private float GetWaveValue(WaveformType type, float time, float normalizedX)
{
switch (type)
{
case WaveformType.Emotion:
return GenerateEmotionWave(time, normalizedX);
case WaveformType.Logic:
return GenerateLogicWave(time);
case WaveformType.Joke:
return GenerateJokeWave(time, normalizedX);
case WaveformType.FilterOutput:
return GenerateFilterOutput(time, normalizedX);
case WaveformType.NormalOutput:
return GenerateNormalOutput(time, normalizedX);
default:
return Mathf.Sin(time * Mathf.PI * 2f);
}
}
#region 波形生成
/// <summary>
/// 情绪波形 - 低沉、缓慢、压抑、忧伤
/// 特征:非常低的频率,缓慢的起伏,像深深的叹息
/// </summary>
private float GenerateEmotionWave(float time, float normalizedX)
{
// 使用更慢的时间流逝,让波形看起来更低沉
float slowTime = time * 0.3f; // 慢3倍
float t = slowTime % 1f;
if (t < 0) t += 1f;
// 主波:非常缓慢的正弦波(深沉的悲伤)
float sadnessBase = Mathf.Sin(t * Mathf.PI * 2f) * 0.5f;
// 第二层:更慢的起伏(压抑感)
float depression = Mathf.Sin(t * Mathf.PI * 0.8f) * 0.25f;
// 微弱的焦虑颤抖(但很轻微)
float anxiety = Mathf.Sin(t * Mathf.PI * 4f + normalizedX) * 0.1f;
anxiety *= (0.5f + Mathf.Sin(t * Mathf.PI) * 0.3f);
// 组合,整体偏向负值(下沉感)
float result = sadnessBase + depression + anxiety - 0.15f;
// 偶尔的深深叹息
if (t > 0.6f && t < 0.8f)
{
float sigh = -Mathf.Sin((t - 0.6f) / 0.2f * Mathf.PI) * 0.4f;
result += sigh;
}
return Mathf.Clamp(result, -1f, 1f);
}
/// <summary>
/// 逻辑波形 - 清晰的方波
/// </summary>
private float GenerateLogicWave(float time)
{
float t = time % 1f;
if (t < 0) t += 1f;
float amplitude = 0.7f;
float squareWave = t < 0.5f ? amplitude : -amplitude;
// 非常短的边缘过渡
float edgeSmooth = 0.015f;
if (t < edgeSmooth)
{
squareWave = Mathf.Lerp(-amplitude, amplitude, t / edgeSmooth);
}
else if (t > 0.5f - edgeSmooth && t < 0.5f + edgeSmooth)
{
float localT = (t - (0.5f - edgeSmooth)) / (edgeSmooth * 2f);
squareWave = Mathf.Lerp(amplitude, -amplitude, localT);
}
else if (t > 1f - edgeSmooth)
{
squareWave = Mathf.Lerp(-amplitude, amplitude, (t - (1f - edgeSmooth)) / edgeSmooth);
}
return squareWave;
}
/// <summary>
/// 笑话波形 - 高频、尖锐、跳动
/// 特征:快速的脉冲、突然的尖峰、亢奋的跳动
/// </summary>
private float GenerateJokeWave(float time, float normalizedX)
{
// 使用更快的时间,让波形看起来更尖锐跳动
float fastTime = time * 2.5f; // 快2.5倍
float t = fastTime % 1f;
if (t < 0) t += 1f;
// 快速三角脉冲(尖锐的笑声节奏)
float pulsePhase = (t * 4f) % 1f; // 每周期4个脉冲
float pulse = 0f;
if (pulsePhase < 0.1f)
{
// 非常快速的上升
pulse = pulsePhase / 0.1f;
}
else if (pulsePhase < 0.2f)
{
// 快速下降
pulse = 1f - (pulsePhase - 0.1f) / 0.1f;
}
pulse *= 0.9f;
// 高频振荡(尖锐感)
float highFreq = Mathf.Sin(t * Mathf.PI * 12f) * 0.35f;
// 随机尖峰(突然的"笑点"爆发)
float spike = 0f;
float spikeT = (t * 7f) % 1f;
if (spikeT < 0.08f)
{
spike = Mathf.Sin(spikeT / 0.08f * Mathf.PI) * 0.5f;
}
// 额外的高频颤动(亢奋感)
float tremor = Mathf.Sin(t * Mathf.PI * 25f + normalizedX * 5f) * 0.12f;
float result = pulse + highFreq + spike + tremor;
return Mathf.Clamp(result, -1f, 1f);
}
private float GenerateFilterOutput(float time, float normalizedX)
{
float logic = GenerateLogicWave(time);
float joke = GenerateJokeWave(time, normalizedX);
float effectiveLogicWeight = _logicWeight;
float effectiveJokeWeight = _jokeWeight * _synthesisProgress;
float result = logic * effectiveLogicWeight + joke * effectiveJokeWeight;
return Mathf.Clamp(result, -1f, 1f);
}
private float GenerateNormalOutput(float time, float normalizedX)
{
float logic = GenerateLogicWave(time) * 0.4f;
float emotion = GenerateEmotionWave(time, normalizedX) * 0.4f;
float result = logic + emotion;
result += Mathf.Sin(time * Mathf.PI * 3f) * 0.1f;
return Mathf.Clamp(result, -1f, 1f);
}
#endregion
#region 公共方法
/// <summary>
/// 设置清晰度(0=完全噪波, 1=完全清晰)
/// </summary>
public void SetClarity(float value)
{
clarity = Mathf.Clamp01(value);
}
/// <summary>
/// 设置清晰位置(进度条位置,0-1)
/// </summary>
public void SetClarityPosition(float position)
{
clarityPosition = Mathf.Clamp01(position);
}
/// <summary>
/// 设置清晰区域宽度
/// </summary>
public void SetClarityWidth(float width)
{
clarityWidth = Mathf.Clamp(width, 0.01f, 0.5f);
}
/// <summary>
/// 设置噪波强度
/// </summary>
public void SetNoiseAmount(float amount)
{
noiseAmount = Mathf.Clamp01(amount);
}
/// <summary>
/// 检查当前位置是否清晰(用于锁定检测)
/// </summary>
public bool IsPositionClear(float position, float threshold = 0.8f)
{
float localClarity = CalculateLocalClarity(position);
return localClarity >= threshold;
}
public void ApplySettings(WaveformSettings settings)
{
waveType = settings.waveType;
waveHeight = settings.waveHeight;
waveSpeed = settings.waveSpeed;
frequency = settings.frequency;
primaryColor = settings.primaryColor;
secondaryColor = settings.secondaryColor;
UpdateColors();
}
public void SetWaveType(WaveformType type)
{
waveType = type;
_isTransforming = false;
}
public void SetColors(Color primary, Color secondary)
{
primaryColor = primary;
secondaryColor = secondary;
UpdateColors();
}
public void StartTransform(WaveformType fromType, WaveformType toType)
{
_transformFromType = fromType;
_transformToType = toType;
_transformProgress = 0f;
_isTransforming = true;
}
public void UpdateTransformProgress(float progress)
{
_transformProgress = Mathf.Clamp01(progress);
if (_transformProgress >= 1f)
{
waveType = _transformToType;
_isTransforming = false;
}
}
public void FinishTransform()
{
waveType = _transformToType;
_transformProgress = 1f;
_isTransforming = false;
}
public void SetSynthesisWeights(float logicWeight, float jokeWeight)
{
_logicWeight = Mathf.Clamp01(logicWeight);
_jokeWeight = Mathf.Clamp01(jokeWeight);
}
public void SetSynthesisProgress(float progress)
{
_synthesisProgress = Mathf.Clamp01(progress);
}
public void SetVisible(bool visible)
{
if (_lineRenderer != null)
_lineRenderer.enabled = visible;
if (glowPointRenderer != null)
glowPointRenderer.enabled = visible;
}
public void SetAlpha(float alpha)
{
_currentAlpha = Mathf.Clamp01(alpha);
if (_lineRenderer == null) return;
Color startColor = primaryColor;
Color endColor = secondaryColor;
startColor.a = _currentAlpha;
endColor.a = _currentAlpha;
Gradient gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[] { new GradientColorKey(startColor, 0f), new GradientColorKey(endColor, 1f) },
new GradientAlphaKey[] { new GradientAlphaKey(_currentAlpha, 0f), new GradientAlphaKey(_currentAlpha, 1f) }
);
_lineRenderer.colorGradient = gradient;
if (glowPointRenderer != null)
{
Color glowColor = primaryColor;
glowColor.a = _currentAlpha;
glowPointRenderer.color = glowColor;
}
}
private void UpdateColors()
{
if (_lineRenderer == null) return;
// 获取实际颜色(优先使用Config预览)
Color actualPrimaryColor = primaryColor;
Color actualSecondaryColor = secondaryColor;
bool isEditor = !Application.isPlaying;
if (isEditor && previewFromConfig != ConfigPreviewType.None && previewConfig != null)
{
WaveformSettings settings = GetPreviewSettings();
if (settings != null)
{
actualPrimaryColor = settings.primaryColor;
actualSecondaryColor = settings.secondaryColor;
}
}
Gradient gradient = new Gradient();
gradient.SetKeys(
new GradientColorKey[] { new GradientColorKey(actualPrimaryColor, 0f), new GradientColorKey(actualSecondaryColor, 1f) },
new GradientAlphaKey[] { new GradientAlphaKey(_currentAlpha, 0f), new GradientAlphaKey(_currentAlpha, 1f) }
);
_lineRenderer.colorGradient = gradient;
if (glowPointRenderer != null)
{
glowPointRenderer.color = actualPrimaryColor;
}
}
#endregion
}
}