using UnityEngine; using System.Collections; using DG.Tweening; [RequireComponent(typeof(LineRenderer))] public class WaveformVisualizer : MonoBehaviour { public int samples = 1024; public float amplitude = 1f; public float spatialFrequency = 1f; public float temporalFrequency = 0.1f; public float noiseAmplitude = 0.5f; public float targetWaveformNoiseAmplitude = 0f; [Range(0f, 1f)] 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; private float[] waveformData; private float[] noiseData; private float[] displayData; public WaveformType waveformType = WaveformType.Sine; public float amplitudeModulationFrequency = 0.5f; public float amplitudeModulationDepth = 0.5f; private bool isShaking = false; private bool isDisordered = false; public float shakeDuration = 0.5f; public float disorderDuration = 1f; public float shakeAmount = 0.1f; private Coroutine shockCoroutine; private Color originalWaveColor; void Start() { lineRenderer = GetComponent(); lineRenderer.positionCount = samples; lineRenderer.useWorldSpace = false; spriteRenderer = GetComponentInParent(); if (spriteRenderer != null) { spriteWidth = spriteRenderer.bounds.size.x; spriteHeight = spriteRenderer.bounds.size.y; lineRenderer.sortingLayerID = spriteRenderer.sortingLayerID; lineRenderer.sortingOrder = spriteRenderer.sortingOrder + 1; } else { Debug.LogError("Could not find SpriteRenderer in parent object."); } waveformData = new float[samples]; noiseData = new float[samples]; displayData = new float[samples]; originalWaveColor = Color.Lerp(noiseColor, clearColor, clarity); } void Update() { if (isDisordered) { GenerateDisorderedData(); } else { GenerateNoiseData(); GenerateWaveformData(); for (int i = 0; i < samples; i++) { displayData[i] = Mathf.Lerp(noiseData[i], waveformData[i], clarity); } SmoothWaveform(displayData, 2); } 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); } } float GetWaveformValue(float t) { float time = Time.time * temporalFrequency; float amplitudeModulation = 1f + amplitudeModulationDepth * Mathf.Sin(2f * Mathf.PI * amplitudeModulationFrequency * Time.time); float angle = t * Mathf.PI * 2f * spatialFrequency + time; 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; } return value * amplitude * amplitudeModulation; } void GenerateDisorderedData() { for (int i = 0; i < samples; i++) { displayData[i] = Random.Range(-amplitude, amplitude); } SmoothWaveform(displayData, 2); } void DrawWaveform() { 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); Vector3 position = new Vector3(x, y, 0); if (isShaking) { float offsetX = Random.Range(-shakeAmount, shakeAmount); float offsetY = Random.Range(-shakeAmount, shakeAmount); position += new Vector3(offsetX, offsetY, 0); } lineRenderer.SetPosition(i, position); } Color waveColor = Color.Lerp(noiseColor, clearColor, clarity); if (isDisordered) { waveColor = Color.red; } lineRenderer.startColor = waveColor; lineRenderer.endColor = waveColor; float startWidth = Mathf.Lerp(0.02f, 0.05f, clarity); lineRenderer.startWidth = startWidth; lineRenderer.endWidth = startWidth; } void SmoothWaveform(float[] data, int iterations) { for (int iter = 0; iter < iterations; iter++) { float prev = data[0]; for (int i = 1; i < data.Length - 1; i++) { float current = data[i]; data[i] = (prev + current + data[i + 1]) / 3f; prev = current; } } } public void TriggerShock(bool isCorrect) { if (shockCoroutine != null) { StopCoroutine(shockCoroutine); } shockCoroutine = StartCoroutine(ShockRoutine(isCorrect)); } IEnumerator ShockRoutine(bool isCorrect) { if (!isCorrect) { isDisordered = true; DOTween.To(() => 0f, x => ApplyDisorder(x), 1f, disorderDuration / 2) .SetEase(Ease.InOutSine); yield return new WaitForSeconds(disorderDuration); DOTween.To(() => 1f, x => ApplyDisorder(x), 0f, disorderDuration / 2) .SetEase(Ease.InOutSine) .OnComplete(() => isDisordered = false); } else { isShaking = true; DOTween.To(() => shakeAmount, x => shakeAmount = x, 0.1f, shakeDuration / 2) .SetEase(Ease.InOutSine) .OnComplete(() => { DOVirtual.DelayedCall(0.5f, () => { DOTween.To(() => shakeAmount, x => shakeAmount = x, 0f, shakeDuration / 2) .SetEase(Ease.InOutSine) .OnComplete(() => isShaking = false); }); }); } } void ApplyDisorder(float progress) { for (int i = 0; i < samples; i++) { float noise = Random.Range(-amplitude * 0.1f, amplitude * 0.1f); displayData[i] = Mathf.Lerp(waveformData[i], waveformData[i] + noise, progress); } SmoothWaveform(displayData, 2); } } public enum WaveformType { Sine, Square, Triangle, Sawtooth, Pulse, Noise, Composite }