276 lines
8.2 KiB
C#
276 lines
8.2 KiB
C#
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
|
|
public enum WaveformType
|
|
{
|
|
Sine,
|
|
Square,
|
|
Triangle,
|
|
Sawtooth,
|
|
Noise,
|
|
Composite,
|
|
Pulse,
|
|
}
|
|
|
|
[RequireComponent(typeof(LineRenderer))]
|
|
public class WaveformVisualizer : MonoBehaviour
|
|
{
|
|
private int samples = 1024;
|
|
[HideInInspector]
|
|
public float amplitude = 0.4f;
|
|
|
|
public float spatialFrequency = 1f;
|
|
public float temporalFrequency = 0.1f;
|
|
private float targetWaveformNoiseAmplitude = 0.1f;
|
|
public float noiseAmplitude = 0.5f;
|
|
[Range(0f, 1f)]
|
|
[HideInInspector]
|
|
public float clarity = 0f;
|
|
|
|
public Color noiseColor = Color.gray;
|
|
public Color clearColor = Color.green;
|
|
|
|
private LineRenderer lineRenderer;
|
|
private SpriteRenderer spriteRenderer;
|
|
private float spriteWidth;
|
|
private float spriteHeight;
|
|
[HideInInspector]
|
|
public float[] waveformData;
|
|
private float[] noiseData;
|
|
[HideInInspector]
|
|
public float[] displayData;
|
|
private float[] antiWaveformData;
|
|
|
|
[HideInInspector]
|
|
public WaveformType waveformType = WaveformType.Sine;
|
|
|
|
public float amplitudeModulationFrequency = 0.5f;
|
|
public float amplitudeModulationDepth = 0.5f;
|
|
|
|
public float phaseShift = 0f; // 相位偏移
|
|
|
|
private bool isCaptured = false; // 是否已拍照(冻结波形)
|
|
|
|
private int spikeIndex; // 单个突刺位置
|
|
private bool isCorrect=true;
|
|
|
|
private bool isSpikeIndexSet = false; // 标志位,判断 spikeIndex 是否已设置
|
|
|
|
void Start()
|
|
{
|
|
lineRenderer = GetComponent<LineRenderer>();
|
|
lineRenderer.positionCount = samples;
|
|
lineRenderer.useWorldSpace = false;
|
|
|
|
spriteRenderer = GetComponentInParent<SpriteRenderer>();
|
|
if (spriteRenderer != null)
|
|
{
|
|
spriteWidth = spriteRenderer.bounds.size.x;
|
|
spriteHeight = spriteRenderer.bounds.size.y;
|
|
lineRenderer.sortingLayerID = spriteRenderer.sortingLayerID;
|
|
lineRenderer.sortingOrder = spriteRenderer.sortingOrder + 2;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("Could not find SpriteRenderer in parent object.");
|
|
}
|
|
|
|
waveformData = new float[samples];
|
|
noiseData = new float[samples];
|
|
displayData = new float[samples];
|
|
antiWaveformData = new float[samples];
|
|
|
|
|
|
DrawWaveform(); // 初始化波形
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
|
|
GenerateNoiseData();
|
|
GenerateWaveformData();
|
|
|
|
if (phaseShift > 0)
|
|
{
|
|
GenerateAntiWaveformData();
|
|
}
|
|
|
|
for (int i = 0; i < samples; i++)
|
|
{
|
|
displayData[i] = Mathf.Lerp(noiseData[i], waveformData[i], clarity);
|
|
if (phaseShift > 0)
|
|
{
|
|
displayData[i] += antiWaveformData[i];
|
|
}
|
|
}
|
|
DrawWaveform();
|
|
|
|
}
|
|
|
|
void GenerateNoiseData()
|
|
{
|
|
float adjustedNoiseAmplitude = Mathf.Lerp(noiseAmplitude, targetWaveformNoiseAmplitude, clarity);
|
|
for (int i = 0; i < samples; i++)
|
|
{
|
|
noiseData[i] = Random.Range(-adjustedNoiseAmplitude, adjustedNoiseAmplitude);
|
|
}
|
|
}
|
|
|
|
void GenerateWaveformData()
|
|
{
|
|
for (int i = 0; i < samples; i++)
|
|
{
|
|
float t = (float)i / samples;
|
|
waveformData[i] = GetWaveformValue(t, 0) * clarity;
|
|
}
|
|
if(!isCorrect)
|
|
{
|
|
GenerateErrorData();
|
|
}
|
|
|
|
}
|
|
private void GenerateErrorData()
|
|
{
|
|
if (isSpikeIndexSet) return; // 如果已设置,则不再随机
|
|
|
|
float threshold = 0.9f; // 80% 的阈值
|
|
|
|
// 找到波形数据中的最大绝对值
|
|
float maxAmplitude = 0f;
|
|
for (int i = 0; i < samples; i++)
|
|
{
|
|
maxAmplitude = Mathf.Max(maxAmplitude, Mathf.Abs(waveformData[i]));
|
|
}
|
|
|
|
// 计算超过 80% 最大值的阈值
|
|
float amplitudeThreshold = maxAmplitude * threshold;
|
|
|
|
// 找到所有超过阈值的点
|
|
List<int> candidateIndices = new List<int>();
|
|
for (int i = 0; i < samples; i++)
|
|
{
|
|
if (Mathf.Abs(waveformData[i]) > amplitudeThreshold)
|
|
{
|
|
candidateIndices.Add(i);
|
|
}
|
|
}
|
|
|
|
// 随机选择一个满足条件的点作为 spikeIndex
|
|
if (candidateIndices.Count > 0)
|
|
{
|
|
int randomIndex = Random.Range(0, candidateIndices.Count);
|
|
spikeIndex = candidateIndices[randomIndex];
|
|
isSpikeIndexSet = true; // 标志位设为 true,表示已设置过 spikeIndex
|
|
}
|
|
else
|
|
{
|
|
spikeIndex = 0; // 如果没有找到满足条件的点,则默认为 0
|
|
isSpikeIndexSet = true; // 即使没有找到满足条件的点,也将标志位设为 true
|
|
}
|
|
}
|
|
public void SetError()
|
|
{
|
|
isCorrect=false;
|
|
}
|
|
float GetWaveformValue(float t, float phaseOffset)
|
|
{
|
|
float time = Time.time * temporalFrequency;
|
|
float amplitudeModulation = 1f + amplitudeModulationDepth * Mathf.Sin(2f * Mathf.PI * amplitudeModulationFrequency * Time.time);
|
|
float angle = t * Mathf.PI * 2f * spatialFrequency + phaseOffset;
|
|
|
|
float value = 0f;
|
|
|
|
switch (waveformType)
|
|
{
|
|
case WaveformType.Sine:
|
|
value = Mathf.Sin(angle);
|
|
break;
|
|
case WaveformType.Square:
|
|
value = Mathf.Sign(Mathf.Sin(angle));
|
|
break;
|
|
case WaveformType.Triangle:
|
|
value = Mathf.PingPong(angle / Mathf.PI, 2f) - 1f;
|
|
break;
|
|
case WaveformType.Sawtooth:
|
|
value = ((angle / (Mathf.PI * 2f)) % 1f) * 2f - 1f;
|
|
break;
|
|
case WaveformType.Pulse:
|
|
value = Mathf.PingPong(angle / Mathf.PI, 2f) > 1f ? 1f : -1f;
|
|
break;
|
|
case WaveformType.Noise:
|
|
value = Mathf.PerlinNoise(angle / (Mathf.PI * 2f), Time.time * temporalFrequency) * 2f - 1f;
|
|
break;
|
|
case WaveformType.Composite:
|
|
value = Mathf.Sin(angle) + Mathf.Sin(angle * 2f) * 0.5f;
|
|
break;
|
|
default:
|
|
value = 0f;
|
|
break;
|
|
}
|
|
|
|
return value * amplitude * amplitudeModulation;
|
|
}
|
|
|
|
void GenerateAntiWaveformData()
|
|
{
|
|
for (int i = 0; i < samples; i++)
|
|
{
|
|
float t = (float)i / samples;
|
|
antiWaveformData[i] = -GetWaveformValue(t, phaseShift) * clarity;
|
|
}
|
|
}
|
|
void DrawWaveform()
|
|
{
|
|
int spikeRadius = 20; // 扩大扰动的半径
|
|
for (int i = 0; i < samples; i++)
|
|
{
|
|
float x = ((float)i / (samples - 1)) * spriteWidth - (spriteWidth / 2f);
|
|
float amplitudeScale = Mathf.Lerp(0.2f, 1f, clarity);
|
|
float y = displayData[i] * amplitudeScale * (spriteHeight / 2f);
|
|
|
|
if(!isCorrect)
|
|
{
|
|
int distanceToSpike = Mathf.Abs(i - spikeIndex);
|
|
if (distanceToSpike <= spikeRadius)
|
|
{
|
|
float attenuation = 1f - (float)distanceToSpike / spikeRadius;
|
|
attenuation *= 0.9f; // 降低扰动强度
|
|
|
|
float baseAmplitude = displayData[i] * amplitudeScale * (spriteHeight / 2f);
|
|
float randomRange = (Mathf.Abs(baseAmplitude) - amplitude / 2f) * attenuation;
|
|
if (Mathf.Abs(baseAmplitude) < amplitude / 2)
|
|
{
|
|
float randomOffset = Random.Range(-randomRange, randomRange);
|
|
y += randomOffset; // 对 Y 值进行随机扰动
|
|
}
|
|
}
|
|
|
|
}
|
|
Vector3 position = new Vector3(x, y, 0);
|
|
lineRenderer.SetPosition(i, position);
|
|
}
|
|
|
|
Color waveColor = Color.Lerp(noiseColor, clearColor, clarity);
|
|
lineRenderer.startColor = waveColor;
|
|
lineRenderer.endColor = waveColor;
|
|
|
|
float startWidth = Mathf.Lerp(0.02f, 0.05f, clarity);
|
|
lineRenderer.startWidth = startWidth;
|
|
lineRenderer.endWidth = startWidth;
|
|
}
|
|
// 应用相位偏移
|
|
public void SetPhaseShift(float phaseShiftValue)
|
|
{
|
|
phaseShift = phaseShiftValue;
|
|
}
|
|
// 重置波形
|
|
public void ResetWaveform()
|
|
{
|
|
isCaptured = false;
|
|
phaseShift = 0f;
|
|
spikeIndex=0;
|
|
isCorrect=true;
|
|
isSpikeIndexSet = false;
|
|
}
|
|
}
|