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(); } 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 /// /// 根据计算出的点数绘制线条 /// 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 对外暴露 /// /// 设置屏幕尺寸作为参数 /// /// 宽 /// 高 public void SetLineSize(float width, float height) { _screenHeight = height; _screenWidth = width; } /// /// 设置波形参数 /// /// public void SetWaveParam(WaveParam waveData) { _waveParam = waveData; config.isClarity = waveData.isClarity; } /// /// 显示波形 /// public void PlayWave() { _isWavePlaying = true; } /// /// 停止显示波形 /// 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 } }