using UnityEngine; namespace AibisDream.MiniGame.HuoShan.EmotionWave { /// /// Controls the EmotionWave visualization. /// Drag the progress slider in the Inspector to drive the 3-act narrative. /// Ported from Web_EmotionWave/js/app.js. /// [ExecuteAlways] public class EmotionWaveController : MonoBehaviour { [Header("References")] [SerializeField] private EmotionWaveConfig config; [SerializeField] private EmotionWaveRenderer waveRenderer; [Header("Progress Control")] [Range(0f, 1f)] [SerializeField] private float progress = 0f; [Header("Smooth Tracking")] [SerializeField] private float lerpSpeed = 4f; [Header("Tweak (active in 0.75~1.0 range)")] [Range(0f, 1f)] [SerializeField] private float tweakFuzz = 0f; [Range(0f, 1f)] [SerializeField] private float tweakSpin = 0.5f; [Range(0f, 1f)] [SerializeField] private float tweakHue = 0f; [Header("CRT Startup")] [SerializeField] private float startupDuration = 1.8f; [Header("Debug")] [SerializeField] [ReadOnlyField] private string currentStage = ""; [SerializeField] [ReadOnlyField] private string currentStageName = ""; [SerializeField] [ReadOnlyField] private float smoothedProgress; private float _currentValue; private float _elapsedTime; public float Progress { get => progress; set => progress = Mathf.Clamp01(value); } void Update() { if (config == null || waveRenderer == null) return; float dt = Time.deltaTime; _elapsedTime += dt; // CRT startup ease float startupFactor = Mathf.Clamp01(_elapsedTime / startupDuration); float startupEase = startupFactor * startupFactor; // Smooth tracking _currentValue += (progress - _currentValue) * Mathf.Min(1f, dt * lerpSpeed); smoothedProgress = _currentValue; // Interpolate wave params var waveParams = config.Interpolate(_currentValue); // Inject review parameters based on stage phase string phase = config.GetStagePhase(_currentValue); currentStage = phase; currentStageName = config.GetStageName(_currentValue); if (phase == "REVIEW") { float r0 = 0.35f, r1 = 0.5f, r2 = 0.65f; if (_currentValue <= r1) waveParams.reviewIntensity = Smoothstep((_currentValue - r0) / (r1 - r0)); else waveParams.reviewIntensity = 0.15f + 0.85f * Smoothstep((r2 - _currentValue) / (r2 - r1)); waveParams.featureBoxRate = 3f; waveParams.thresholdLevel = 0.75f; waveParams.cursorSpeed = 0.5f; } else { waveParams.reviewIntensity = 0f; waveParams.featureBoxRate = 0f; waveParams.thresholdLevel = 0.8f; waveParams.cursorSpeed = 0f; } // End-stage tweaks (smoothstep fade-in from 0.75) float tweakLinear = Mathf.Clamp01((_currentValue - 0.75f) / 0.1f); float tweakWeight = tweakLinear * tweakLinear * (3f - 2f * tweakLinear); if (tweakWeight > 0.001f) { float fuzz = tweakFuzz * 0.8f; float spin = 0.2f + tweakSpin * 1f; float hueVal = tweakHue * 128f; waveParams.weierstrassAmount = Mathf.Lerp(waveParams.weierstrassAmount, fuzz, tweakWeight); waveParams.phaseSpeed = Mathf.Lerp(waveParams.phaseSpeed, spin, tweakWeight); waveParams.hue = Mathf.Lerp(waveParams.hue, hueVal, tweakWeight); } // CRT startup fade waveParams.glowIntensity *= startupEase; waveParams.lightness *= startupEase; // Push to renderer waveRenderer.currentParams = waveParams; waveRenderer.time = _elapsedTime; } static float Smoothstep(float t) { t = Mathf.Clamp01(t); return t * t * (3f - 2f * t); } } /// /// Makes a field read-only in the Inspector. /// public class ReadOnlyFieldAttribute : PropertyAttribute { } #if UNITY_EDITOR [UnityEditor.CustomPropertyDrawer(typeof(ReadOnlyFieldAttribute))] public class ReadOnlyFieldDrawer : UnityEditor.PropertyDrawer { public override void OnGUI(Rect position, UnityEditor.SerializedProperty property, GUIContent label) { UnityEditor.EditorGUI.BeginDisabledGroup(true); UnityEditor.EditorGUI.PropertyField(position, property, label); UnityEditor.EditorGUI.EndDisabledGroup(); } } #endif }