303 lines
8.9 KiB
C#
303 lines
8.9 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using Random = UnityEngine.Random;
|
|
|
|
namespace AibisDream.FixSystem
|
|
{
|
|
public class WaveformLine : MonoBehaviour
|
|
{
|
|
#region 组件索引
|
|
|
|
private LineRenderer _waveformLine;
|
|
|
|
#endregion
|
|
|
|
private float _screenWidth;
|
|
private float _screenHeight;
|
|
|
|
#region 状态
|
|
|
|
private bool _isWavePlaying;
|
|
|
|
#endregion
|
|
|
|
private WaveParam _waveParam;
|
|
|
|
#region Line数据
|
|
|
|
private float[] _waveformData;
|
|
private float[] _noiseData;
|
|
private float[] _spikeData;
|
|
|
|
private float[] _displayData; // 最后展示数据
|
|
|
|
#endregion
|
|
|
|
[SerializeField] private WaveformConfig config;
|
|
|
|
private void Awake()
|
|
{
|
|
InitComponentRefs();
|
|
InitValues();
|
|
}
|
|
|
|
private void InitValues()
|
|
{
|
|
_waveformData = new float[config.samples];
|
|
_noiseData = new float[config.samples];
|
|
_spikeData = new float[config.samples];
|
|
|
|
_displayData = new float[config.samples];
|
|
}
|
|
|
|
private void InitComponentRefs()
|
|
{
|
|
_waveformLine = GetComponent<LineRenderer>();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (_isWavePlaying)
|
|
{
|
|
// 绘制线条
|
|
DrawWaveform();
|
|
}
|
|
}
|
|
|
|
private void DrawWaveform()
|
|
{
|
|
// 计算当前线条的点数据
|
|
CalcLinePointsData();
|
|
// 计算结束,绘制线条
|
|
DrawLine();
|
|
}
|
|
|
|
#region 波形数据计算
|
|
|
|
private void CalcLinePointsData()
|
|
{
|
|
// 分组计算:
|
|
var noiseData = GenerateNoiseData(); // 生成噪声
|
|
var waveformData = GenerateWaveformData(); // 波形数据
|
|
var spikeData = GenerateSpikeData(); // 错误尖峰数据
|
|
|
|
// 合成最终结果
|
|
for (int i = 0; i < config.samples; i++)
|
|
{
|
|
// 正常线条加错误尖峰
|
|
var lineValue = (_waveParam.isClarity ? waveformData[i] : noiseData[i]) + spikeData[i];
|
|
_displayData[i] = Mathf.Clamp(lineValue, -_screenHeight / 1.8f, _screenHeight / 1.8f);
|
|
}
|
|
}
|
|
|
|
private float[] GenerateNoiseData()
|
|
{
|
|
for (var i = 0; i < config.samples; i++)
|
|
{
|
|
_noiseData[i] = Random.Range(-config.AdjustedNoiseAmplitude, config.AdjustedNoiseAmplitude);
|
|
}
|
|
|
|
return _noiseData;
|
|
}
|
|
|
|
private float[] GenerateWaveformData()
|
|
{
|
|
if (_waveParam.isClarity)
|
|
{
|
|
// 计算波形
|
|
for (int i = 0; i < config.samples; i++)
|
|
{
|
|
float basicAngle = TransIndexToAngle(i);
|
|
// 正波形
|
|
float wavePoint = CalcLineValueByAngle(basicAngle);
|
|
// 如果有相位偏移
|
|
if (_waveParam.phaseShift > 0)
|
|
{
|
|
wavePoint -= CalcLineValueByAngle(basicAngle + _waveParam.phaseShift);
|
|
}
|
|
|
|
_waveformData[i] = wavePoint;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 此时没有波形,数组置为0
|
|
Array.Clear(_waveformData, 0, _waveformData.Length);
|
|
}
|
|
|
|
return _waveformData;
|
|
}
|
|
|
|
private float TransIndexToAngle(int idx)
|
|
{
|
|
return (float)idx / config.samples * Mathf.PI * 2f * config.spatialFrequency;
|
|
}
|
|
|
|
private float CalcLineValueByAngle(float angle)
|
|
{
|
|
// 计算波形值
|
|
var value = _waveParam.waveformType switch
|
|
{
|
|
WaveformType.Sine => Mathf.Sin(angle),
|
|
WaveformType.Square => Mathf.Sign(Mathf.Sin(angle)),
|
|
WaveformType.Triangle => Mathf.PingPong(angle / Mathf.PI, 1f) * 2f - 1f,
|
|
WaveformType.Sawtooth => angle / (Mathf.PI * 2f) % 1f * 2f - 1f, // 统一周期为 2π
|
|
WaveformType.Pulse => Mathf.PingPong(angle / (Mathf.PI * 2f), 1f) > 0.5f ? 1f : -1f, // 统一周期为 2π
|
|
WaveformType.Noise => Mathf.PerlinNoise(angle / (Mathf.PI * 2f), Time.time * config.temporalFrequency) *
|
|
2f - 1f, // 统一周期为 2π
|
|
WaveformType.Composite => Mathf.Sin(angle) + Mathf.Sin(angle * 2f) * 0.5f, // 组合波形也统一周期
|
|
_ => 0f
|
|
};
|
|
|
|
return value * config.amplitude; // 最终返回角度对应波形
|
|
}
|
|
|
|
private float[] GenerateSpikeData()
|
|
{
|
|
if (_waveParam.isSpikeShowed)
|
|
{
|
|
// 错误点可以显示
|
|
for (var i = 0; i < config.samples; i++)
|
|
{
|
|
_spikeData[i] = CalcSpikeValueByIdx(i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 错误点尚不可显示,全部置为0
|
|
Array.Clear(_spikeData, 0, _spikeData.Length);
|
|
}
|
|
|
|
return _spikeData;
|
|
}
|
|
|
|
private float CalcSpikeValueByIdx(int idx)
|
|
{
|
|
var distanceToSpike = Mathf.Abs(_waveParam.spikeIdx - idx);
|
|
// 尖峰范围外直接数据为0
|
|
if (distanceToSpike > config.spikeRange) return 0f;
|
|
|
|
// 尖峰范围内计算
|
|
// 计算随机范围
|
|
var attenuation = (1f - distanceToSpike / 20f) * 0.9f;
|
|
var baseAmplitude = _displayData[idx] * config.AmplitudeScale * (_screenHeight / 2f);
|
|
var randomRange = (Mathf.Abs(baseAmplitude) - config.amplitude / 3f) * attenuation;
|
|
// 得出结果
|
|
if (Mathf.Abs(baseAmplitude) < config.amplitude / 3)
|
|
{
|
|
return Random.Range(-randomRange, randomRange); // 对 Y 值进行随机扰动
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// 根据计算出的点数绘制线条
|
|
/// </summary>
|
|
private void DrawLine()
|
|
{
|
|
for (int i = 0; i < config.samples; i++)
|
|
{
|
|
// 计算线上每个点的位置
|
|
float x = (float)i / (config.samples - 1) * _screenWidth - _screenWidth / 2f; // x轴数据
|
|
float y = _displayData[i] * config.AmplitudeScale * (_screenHeight / 2f);
|
|
Vector3 position = new Vector3(x, y, 0);
|
|
// 设置
|
|
_waveformLine.SetPosition(i, position);
|
|
}
|
|
|
|
// 设置颜色和宽度
|
|
_waveformLine.startColor = config.LineColor;
|
|
_waveformLine.endColor = config.LineColor;
|
|
_waveformLine.startWidth = config.LineWidth;
|
|
_waveformLine.endWidth = config.LineWidth;
|
|
}
|
|
|
|
#region 对外暴露
|
|
|
|
/// <summary>
|
|
/// 设置屏幕尺寸作为参数
|
|
/// </summary>
|
|
/// <param name="width">宽</param>
|
|
/// <param name="height">高</param>
|
|
public void SetLineSize(float width, float height)
|
|
{
|
|
_screenHeight = height;
|
|
_screenWidth = width;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置波形参数
|
|
/// </summary>
|
|
/// <param name="waveData"></param>
|
|
public void SetWaveParam(WaveParam waveData)
|
|
{
|
|
_waveParam = waveData;
|
|
config.isClarity = waveData.isClarity;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 显示波形
|
|
/// </summary>
|
|
public void PlayWave()
|
|
{
|
|
_isWavePlaying = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 停止显示波形
|
|
/// </summary>
|
|
public void StopWave()
|
|
{
|
|
_isWavePlaying = false;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
[Serializable]
|
|
public class WaveformConfig
|
|
{
|
|
public int samples = 1024;
|
|
public float amplitude = 0.6f;
|
|
public int spikeRange = 20;
|
|
|
|
public float spatialFrequency = 1f;
|
|
public float temporalFrequency = 0.1f;
|
|
public float targetWaveformNoiseAmplitude = 0.1f;
|
|
public float noiseAmplitude = 0.5f;
|
|
|
|
public Color noiseColor = Color.gray;
|
|
public Color clearColor = Color.green;
|
|
|
|
public bool isClarity;
|
|
|
|
public Color LineColor => isClarity ? clearColor : noiseColor;
|
|
public float LineWidth => isClarity ? 0.05f : 0.02f;
|
|
public float AmplitudeScale => isClarity ? 1f : 0.2f;
|
|
public float AdjustedNoiseAmplitude => isClarity ? targetWaveformNoiseAmplitude : noiseAmplitude;
|
|
}
|
|
|
|
public struct WaveParam
|
|
{
|
|
public WaveformType waveformType;
|
|
public float phaseShift;
|
|
public bool isClarity;
|
|
public bool isSpikeShowed;
|
|
public int spikeIdx;
|
|
}
|
|
|
|
public enum WaveformType
|
|
{
|
|
Default,
|
|
Sine,
|
|
Square,
|
|
Triangle,
|
|
Sawtooth,
|
|
Noise,
|
|
Composite,
|
|
Pulse
|
|
}
|
|
} |