1117 lines
44 KiB
C#
1117 lines
44 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);
|
|
[Tooltip("清晰区域使用主色调的高亮版本")]
|
|
[SerializeField] private bool useBrightenedClearColor = true;
|
|
[SerializeField] private float clearColorBrighten = 0.3f;
|
|
|
|
[Header("发光点")]
|
|
[SerializeField] private bool showGlowPoint = true;
|
|
[SerializeField] private SpriteRenderer glowPointRenderer;
|
|
[SerializeField] private float glowSize = 0.15f;
|
|
|
|
[Header("扫描线设置")]
|
|
[SerializeField] private Color scanLineColor = new Color(0.2f, 0.9f, 1f, 1f);
|
|
[SerializeField] private float scanLineWidthMultiplier = 0.8f; // 相对于波形线宽的倍数(更细)
|
|
[SerializeField] private Color limitLineColor = new Color(1f, 0.8f, 0.2f, 1f);
|
|
[SerializeField] private float limitLineWidthMultiplier = 0.6f; // 相对于波形线宽的倍数(更细)
|
|
[SerializeField] private string scanLineSortingLayer = "Tools";
|
|
[SerializeField] private int scanLineSortingOrderOffset = 1; // 在Tools层级上+1
|
|
|
|
[Header("复古示波器质感(可选)")]
|
|
[Tooltip("开启后会添加轻微的CRT抖动/亮度呼吸,让观感更像老式示波器/仪表屏")]
|
|
[SerializeField] private bool enableOscilloscopeArtifacts = true;
|
|
[Tooltip("亮度/透明度轻微闪烁强度(0-0.2建议)")]
|
|
[Range(0f, 0.2f)]
|
|
[SerializeField] private float crtFlickerAmount = 0.04f;
|
|
[Tooltip("垂直微抖动强度(以波形高度为基准的比例)")]
|
|
[Range(0f, 0.05f)]
|
|
[SerializeField] private float crtVerticalJitter = 0.008f;
|
|
[Tooltip("50/60Hz嗡鸣感(用于模拟电源纹波)")]
|
|
[Range(10f, 90f)]
|
|
[SerializeField] private float crtHumHz = 50f;
|
|
|
|
private LineRenderer _lineRenderer;
|
|
private LineRenderer _scanLineRenderer;
|
|
private LineRenderer _upperLimitLineRenderer;
|
|
private LineRenderer _lowerLimitLineRenderer;
|
|
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 _crtAlphaMul = 1f;
|
|
|
|
// 变换效果参数
|
|
private float _transformDistortion = 0f; // 变换时的扭曲程度
|
|
private float _transformCompression = 1f; // 变换时的压缩程度
|
|
|
|
// 随机噪波种子
|
|
private float[] _noiseSeed;
|
|
|
|
// 合成模式参数
|
|
private float _logicWeight = 0.3f;
|
|
private float _jokeWeight = 0.85f;
|
|
private float _synthesisProgress = 1f;
|
|
|
|
// 情绪波形谐波参数(可通过配置调整)
|
|
private float _harmonicA1 = 0.55f; // 基波振幅
|
|
private float _harmonicA2 = 0.28f; // 二次谐波振幅
|
|
private float _harmonicA3 = 0.12f; // 三次谐波振幅
|
|
private float _harmonicPhase2 = 0.3f; // 二次谐波相位
|
|
private float _harmonicPhase3 = 0.5f; // 三次谐波相位
|
|
private float _verticalOffset = -0.08f; // 整体偏移
|
|
|
|
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();
|
|
InitializeScanLines();
|
|
}
|
|
|
|
/// <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;
|
|
|
|
// 轻微“电源纹波/亮度呼吸”,用于提升拟真仪表屏质感
|
|
if (enableOscilloscopeArtifacts && Application.isPlaying)
|
|
{
|
|
// 使用低成本噪声 + 50Hz正弦混合,避免过于规律
|
|
float t = Time.time;
|
|
float hum = Mathf.Sin(t * Mathf.PI * 2f * crtHumHz) * 0.5f + 0.5f; // 0..1
|
|
float rand = Mathf.PerlinNoise(t * 4.2f, 0.13f); // 0..1
|
|
float mix = Mathf.Lerp(hum, rand, 0.6f); // 0..1
|
|
_crtAlphaMul = Mathf.Clamp(1f - crtFlickerAmount + mix * crtFlickerAmount * 2f, 0.7f, 1.15f);
|
|
}
|
|
else
|
|
{
|
|
_crtAlphaMul = 1f;
|
|
}
|
|
|
|
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)
|
|
{
|
|
baseWaveValue = GetTransformingWaveValue(timeOffset, normalizedX);
|
|
}
|
|
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;
|
|
}
|
|
|
|
// 复古示波器:给“非清晰区域”加一点点垂直抖动(更像硬件采样/显示抖动)
|
|
if (enableOscilloscopeArtifacts && Application.isPlaying && currentMode == PreviewMode.Runtime)
|
|
{
|
|
float localClarity = CalculateLocalClarity(normalizedX);
|
|
float jitterMask = Mathf.Clamp01(1f - localClarity);
|
|
float n = (Mathf.PerlinNoise(_noisePhase * 1.7f, normalizedX * 8.0f) - 0.5f) * 2f; // -1..1
|
|
float hum = Mathf.Sin((Time.time * crtHumHz + normalizedX * 3.5f) * Mathf.PI * 2f); // -1..1
|
|
float jitter = (n * 0.7f + hum * 0.3f) * crtVerticalJitter * jitterMask;
|
|
finalValue += jitter;
|
|
}
|
|
|
|
_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();
|
|
float effectiveAlpha = Mathf.Clamp01(_currentAlpha * _crtAlphaMul);
|
|
|
|
if (clarity > 0.5f)
|
|
{
|
|
// 有清晰区域时,显示清晰位置的颜色变化
|
|
float clearStart = Mathf.Clamp01(clarityPosition - clarityWidth);
|
|
float clearEnd = Mathf.Clamp01(clarityPosition + clarityWidth);
|
|
|
|
// 使用高亮的主色调作为清晰区域颜色(不再使用绿色)
|
|
Color brightenedColor = GetBrightenedClearColor();
|
|
|
|
gradient.SetKeys(
|
|
new GradientColorKey[] {
|
|
new GradientColorKey(primaryColor, 0f),
|
|
new GradientColorKey(primaryColor, clearStart),
|
|
new GradientColorKey(brightenedColor, clarityPosition),
|
|
new GradientColorKey(primaryColor, clearEnd),
|
|
new GradientColorKey(secondaryColor, 1f)
|
|
},
|
|
new GradientAlphaKey[] {
|
|
new GradientAlphaKey(effectiveAlpha, 0f),
|
|
new GradientAlphaKey(effectiveAlpha, 1f)
|
|
}
|
|
);
|
|
}
|
|
else
|
|
{
|
|
gradient.SetKeys(
|
|
new GradientColorKey[] {
|
|
new GradientColorKey(primaryColor, 0f),
|
|
new GradientColorKey(secondaryColor, 1f)
|
|
},
|
|
new GradientAlphaKey[] {
|
|
new GradientAlphaKey(effectiveAlpha, 0f),
|
|
new GradientAlphaKey(effectiveAlpha, 1f)
|
|
}
|
|
);
|
|
}
|
|
|
|
_lineRenderer.colorGradient = gradient;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取高亮的清晰区域颜色(基于主色调)
|
|
/// </summary>
|
|
private Color GetBrightenedClearColor()
|
|
{
|
|
if (!useBrightenedClearColor)
|
|
{
|
|
return primaryColor;
|
|
}
|
|
|
|
// 将主色调提亮
|
|
Color.RGBToHSV(primaryColor, out float h, out float s, out float v);
|
|
v = Mathf.Clamp01(v + clearColorBrighten);
|
|
s = Mathf.Clamp01(s * 0.8f); // 稍微降低饱和度使其更亮
|
|
Color brightened = Color.HSVToRGB(h, s, v);
|
|
brightened.a = primaryColor.a;
|
|
return brightened;
|
|
}
|
|
|
|
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>
|
|
/// 情绪波形 - 基于谐波叠加的简洁实现
|
|
/// 采用傅里叶合成:基波 + 二次谐波 + 三次谐波
|
|
/// 特征:低频、缓慢、通过谐波叠加表达悲伤/压抑情绪
|
|
/// 谐波参数可通过 WaveformSettings 配置
|
|
/// </summary>
|
|
private float GenerateEmotionWave(float time, float normalizedX)
|
|
{
|
|
// 慢速时间,体现低沉感
|
|
float slowTime = time * 0.4f;
|
|
float t = slowTime * Mathf.PI * 2f; // 基频角度
|
|
|
|
// 傅里叶合成:基波 + 二次谐波 + 三次谐波(使用可配置参数)
|
|
float fundamental = Mathf.Sin(t) * _harmonicA1; // 基波
|
|
float harmonic2 = Mathf.Sin(2f * t + _harmonicPhase2) * _harmonicA2; // 二次谐波
|
|
float harmonic3 = Mathf.Sin(3f * t + _harmonicPhase3) * _harmonicA3; // 三次谐波
|
|
|
|
// 叠加得到最终波形
|
|
float result = fundamental + harmonic2 + harmonic3;
|
|
|
|
// 整体偏移(负值表示下沉,体现压抑感)
|
|
result += _verticalOffset;
|
|
|
|
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;
|
|
|
|
// 应用谐波配置(用于情绪波形的傅里叶合成)
|
|
_harmonicA1 = settings.harmonicA1;
|
|
_harmonicA2 = settings.harmonicA2;
|
|
_harmonicA3 = settings.harmonicA3;
|
|
_harmonicPhase2 = settings.harmonicPhase2;
|
|
_harmonicPhase3 = settings.harmonicPhase3;
|
|
_verticalOffset = settings.verticalOffset;
|
|
|
|
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;
|
|
_transformDistortion = 0f;
|
|
_transformCompression = 1f;
|
|
_isTransforming = true;
|
|
}
|
|
|
|
public void UpdateTransformProgress(float progress)
|
|
{
|
|
_transformProgress = Mathf.Clamp01(progress);
|
|
|
|
// 计算变换过程中的效果参数
|
|
// 0-0.3: 开始扭曲,波形开始不稳定
|
|
// 0.3-0.7: 最大扭曲+压缩,波形被"挤压"
|
|
// 0.7-1.0: 扭曲消退,新波形稳定
|
|
if (_transformProgress < 0.3f)
|
|
{
|
|
float t = _transformProgress / 0.3f;
|
|
_transformDistortion = Mathf.Sin(t * Mathf.PI) * 0.5f;
|
|
_transformCompression = 1f;
|
|
}
|
|
else if (_transformProgress < 0.7f)
|
|
{
|
|
float t = (_transformProgress - 0.3f) / 0.4f;
|
|
_transformDistortion = 0.5f + Mathf.Sin(t * Mathf.PI * 3f) * 0.3f;
|
|
// 压缩效果:低频被压缩成高频的感觉
|
|
_transformCompression = Mathf.Lerp(1f, 2.5f, t);
|
|
}
|
|
else
|
|
{
|
|
float t = (_transformProgress - 0.7f) / 0.3f;
|
|
_transformDistortion = Mathf.Lerp(0.5f, 0f, t);
|
|
_transformCompression = Mathf.Lerp(2.5f, 1f, t);
|
|
}
|
|
|
|
if (_transformProgress >= 1f)
|
|
{
|
|
waveType = _transformToType;
|
|
_isTransforming = false;
|
|
_transformDistortion = 0f;
|
|
_transformCompression = 1f;
|
|
}
|
|
}
|
|
|
|
public void FinishTransform()
|
|
{
|
|
waveType = _transformToType;
|
|
_transformProgress = 1f;
|
|
_isTransforming = false;
|
|
_transformDistortion = 0f;
|
|
_transformCompression = 1f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取变换过程中的波形值(带有戏剧性的过渡效果)
|
|
/// </summary>
|
|
private float GetTransformingWaveValue(float timeOffset, float normalizedX)
|
|
{
|
|
float fromValue = GetWaveValue(_transformFromType, timeOffset, normalizedX);
|
|
float toValue = GetWaveValue(_transformToType, timeOffset * _transformCompression, normalizedX);
|
|
|
|
// 基础混合
|
|
float blendedValue = Mathf.Lerp(fromValue, toValue, _transformProgress);
|
|
|
|
// 添加扭曲效果(波形被挤压、扭曲的感觉)
|
|
if (_transformDistortion > 0)
|
|
{
|
|
// 添加不规则的高频扰动
|
|
float distortion = Mathf.Sin(timeOffset * 15f + normalizedX * 10f) * _transformDistortion;
|
|
distortion += Mathf.Sin(timeOffset * 23f) * _transformDistortion * 0.5f;
|
|
|
|
// 压缩效果:让波形看起来像是被挤压
|
|
float compressionEffect = Mathf.Sin(normalizedX * Mathf.PI * _transformCompression * 2f) * _transformDistortion * 0.3f;
|
|
|
|
blendedValue += distortion + compressionEffect;
|
|
}
|
|
|
|
return Mathf.Clamp(blendedValue, -1.2f, 1.2f); // 允许稍微超出范围增加戏剧感
|
|
}
|
|
|
|
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
|
|
|
|
#region 扫描线系统
|
|
|
|
/// <summary>
|
|
/// 初始化扫描线
|
|
/// </summary>
|
|
private void InitializeScanLines()
|
|
{
|
|
// 确保主LineRenderer已初始化
|
|
if (_lineRenderer == null)
|
|
{
|
|
_lineRenderer = GetComponent<LineRenderer>();
|
|
}
|
|
|
|
// 计算扫描线宽度(相对于波形线宽,更细一点)
|
|
float scanWidth = lineWidth * scanLineWidthMultiplier;
|
|
float limitWidth = lineWidth * limitLineWidthMultiplier;
|
|
|
|
// 创建垂直扫描线(用于屏蔽词检测)
|
|
_scanLineRenderer = CreateScanLineRenderer("ScanLine", scanLineColor, scanWidth);
|
|
if (_scanLineRenderer != null) _scanLineRenderer.gameObject.SetActive(false);
|
|
|
|
// 创建上限线(用于限幅检测)
|
|
_upperLimitLineRenderer = CreateScanLineRenderer("UpperLimitLine", limitLineColor, limitWidth);
|
|
if (_upperLimitLineRenderer != null) _upperLimitLineRenderer.gameObject.SetActive(false);
|
|
|
|
// 创建下限线(用于限幅检测)
|
|
_lowerLimitLineRenderer = CreateScanLineRenderer("LowerLimitLine", limitLineColor, limitWidth);
|
|
if (_lowerLimitLineRenderer != null) _lowerLimitLineRenderer.gameObject.SetActive(false);
|
|
|
|
Debug.Log($"WaveformRenderer: 扫描线初始化完成 - ScanLine:{_scanLineRenderer != null}, UpperLimit:{_upperLimitLineRenderer != null}, LowerLimit:{_lowerLimitLineRenderer != null}, ScanWidth:{scanWidth:F4}, LimitWidth:{limitWidth:F4}");
|
|
}
|
|
|
|
private LineRenderer CreateScanLineRenderer(string name, Color color, float width)
|
|
{
|
|
GameObject lineObj = new GameObject(name);
|
|
lineObj.transform.SetParent(transform);
|
|
lineObj.transform.localPosition = Vector3.zero;
|
|
lineObj.transform.localScale = Vector3.one;
|
|
|
|
LineRenderer lr = lineObj.AddComponent<LineRenderer>();
|
|
|
|
// 使用主LineRenderer的材质,或者创建一个简单的材质
|
|
if (_lineRenderer != null && _lineRenderer.material != null)
|
|
{
|
|
lr.material = new Material(_lineRenderer.material);
|
|
}
|
|
else
|
|
{
|
|
// 回退方案:使用内置的UI shader
|
|
Shader shader = Shader.Find("UI/Default");
|
|
if (shader == null) shader = Shader.Find("Sprites/Default");
|
|
if (shader == null) shader = Shader.Find("Unlit/Color");
|
|
lr.material = new Material(shader);
|
|
}
|
|
|
|
lr.startColor = color;
|
|
lr.endColor = color;
|
|
lr.startWidth = width;
|
|
lr.endWidth = width;
|
|
lr.positionCount = 2;
|
|
lr.useWorldSpace = false;
|
|
|
|
// 设置层级:Tools层 + offset
|
|
lr.sortingLayerName = scanLineSortingLayer;
|
|
lr.sortingOrder = scanLineSortingOrderOffset;
|
|
|
|
return lr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 显示垂直扫描线在指定位置
|
|
/// </summary>
|
|
public void ShowScanLine(float normalizedX, float height = -1f)
|
|
{
|
|
if (_scanLineRenderer == null)
|
|
{
|
|
Debug.LogWarning("WaveformRenderer: 扫描线未初始化!尝试重新初始化...");
|
|
InitializeScanLines();
|
|
if (_scanLineRenderer == null) return;
|
|
}
|
|
|
|
float actualHeight = height > 0 ? height : waveHeight * 1.5f;
|
|
float xPos = (normalizedX - 0.5f) * waveWidth;
|
|
|
|
_scanLineRenderer.SetPosition(0, new Vector3(xPos, -actualHeight, 0));
|
|
_scanLineRenderer.SetPosition(1, new Vector3(xPos, actualHeight, 0));
|
|
_scanLineRenderer.gameObject.SetActive(true);
|
|
|
|
Debug.Log($"WaveformRenderer: 显示扫描线 - X:{xPos:F2}, Height:{actualHeight:F2}, WaveWidth:{waveWidth}, WaveHeight:{waveHeight}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 隐藏垂直扫描线
|
|
/// </summary>
|
|
public void HideScanLine()
|
|
{
|
|
if (_scanLineRenderer != null)
|
|
{
|
|
_scanLineRenderer.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置扫描线位置(用于动画)
|
|
/// </summary>
|
|
public void SetScanLinePosition(float normalizedX)
|
|
{
|
|
if (_scanLineRenderer == null || !_scanLineRenderer.gameObject.activeSelf) return;
|
|
|
|
float xPos = (normalizedX - 0.5f) * waveWidth;
|
|
Vector3 pos0 = _scanLineRenderer.GetPosition(0);
|
|
Vector3 pos1 = _scanLineRenderer.GetPosition(1);
|
|
_scanLineRenderer.SetPosition(0, new Vector3(xPos, pos0.y, 0));
|
|
_scanLineRenderer.SetPosition(1, new Vector3(xPos, pos1.y, 0));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 显示限幅线(上下限)
|
|
/// </summary>
|
|
public void ShowLimitLines(float upperLimit = -1f, float lowerLimit = -1f)
|
|
{
|
|
if (_upperLimitLineRenderer == null || _lowerLimitLineRenderer == null)
|
|
{
|
|
Debug.LogWarning("WaveformRenderer: 限幅线未初始化!尝试重新初始化...");
|
|
InitializeScanLines();
|
|
if (_upperLimitLineRenderer == null || _lowerLimitLineRenderer == null) return;
|
|
}
|
|
|
|
float actualUpper = upperLimit > 0 ? upperLimit : waveHeight * 0.8f;
|
|
float actualLower = lowerLimit > 0 ? lowerLimit : -waveHeight * 0.8f;
|
|
float halfWidth = waveWidth * 0.5f;
|
|
|
|
// 上限线
|
|
_upperLimitLineRenderer.SetPosition(0, new Vector3(-halfWidth, actualUpper, 0));
|
|
_upperLimitLineRenderer.SetPosition(1, new Vector3(halfWidth, actualUpper, 0));
|
|
_upperLimitLineRenderer.gameObject.SetActive(true);
|
|
|
|
// 下限线
|
|
_lowerLimitLineRenderer.SetPosition(0, new Vector3(-halfWidth, actualLower, 0));
|
|
_lowerLimitLineRenderer.SetPosition(1, new Vector3(halfWidth, actualLower, 0));
|
|
_lowerLimitLineRenderer.gameObject.SetActive(true);
|
|
|
|
Debug.Log($"WaveformRenderer: 显示限幅线 - Upper:{actualUpper:F2}, Lower:{actualLower:F2}, Width:{halfWidth * 2:F2}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 隐藏限幅线
|
|
/// </summary>
|
|
public void HideLimitLines()
|
|
{
|
|
if (_upperLimitLineRenderer != null)
|
|
{
|
|
_upperLimitLineRenderer.gameObject.SetActive(false);
|
|
}
|
|
if (_lowerLimitLineRenderer != null)
|
|
{
|
|
_lowerLimitLineRenderer.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置限幅线透明度(用于闪烁动画)
|
|
/// </summary>
|
|
public void SetLimitLinesAlpha(float alpha)
|
|
{
|
|
Color color = limitLineColor;
|
|
color.a = alpha;
|
|
|
|
if (_upperLimitLineRenderer != null)
|
|
{
|
|
_upperLimitLineRenderer.startColor = color;
|
|
_upperLimitLineRenderer.endColor = color;
|
|
}
|
|
if (_lowerLimitLineRenderer != null)
|
|
{
|
|
_lowerLimitLineRenderer.startColor = color;
|
|
_lowerLimitLineRenderer.endColor = color;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置扫描线颜色
|
|
/// </summary>
|
|
public void SetScanLineColor(Color color)
|
|
{
|
|
if (_scanLineRenderer != null)
|
|
{
|
|
_scanLineRenderer.startColor = color;
|
|
_scanLineRenderer.endColor = color;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|