empty 改为极弱噪波待机态;增加预设切换 crossfade 与 flash 效果; noiseOnlyBlend 支持纯噪波渲染;两段式归一化使 fillAmount=1 正确填满;switch_emotion_wave_config 支持 progress 参数。 Made-with: Cursor
192 lines
7.1 KiB
C#
192 lines
7.1 KiB
C#
using UnityEngine;
|
|
|
|
namespace AibisDream.MiniGame.HuoShan.EmotionWave
|
|
{
|
|
/// <summary>
|
|
/// Controls the EmotionWave visualization.
|
|
/// Drag the progress slider in the Inspector to drive the 3-act narrative.
|
|
/// Ported from Web_EmotionWave/js/app.js.
|
|
/// </summary>
|
|
[ExecuteAlways]
|
|
public class EmotionWaveController : MonoBehaviour
|
|
{
|
|
[Header("References")]
|
|
[SerializeField] private EmotionWaveConfig config;
|
|
[SerializeField] private EmotionWaveRenderer waveRenderer;
|
|
|
|
[Header("Preset")]
|
|
[Tooltip("当前波形预设:normal / predicted / modified_joke / empty,可由 switch_emotion_wave_config 切换")]
|
|
[SerializeField] private string presetId = "normal";
|
|
|
|
[Header("Preset Transition")]
|
|
[SerializeField] private float presetTransitionDuration = 0.22f;
|
|
[SerializeField] private float presetFlashDuration = 0.12f;
|
|
[SerializeField] private float presetFlashGlowBoost = 0.45f;
|
|
[SerializeField] private float presetFlashLightnessBoost = 18f;
|
|
[SerializeField] private float presetFlashGlitchBoost = 0.28f;
|
|
|
|
[Header("Progress Control")]
|
|
[Range(0f, 1f)]
|
|
[SerializeField] private float progress = 0f;
|
|
|
|
[Header("Smooth Tracking")]
|
|
[SerializeField] private float lerpSpeed = 4f;
|
|
|
|
[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;
|
|
private string _previousPresetId = "normal";
|
|
private float _presetTransitionElapsed = 999f;
|
|
private bool _isPresetTransitioning;
|
|
|
|
public float Progress
|
|
{
|
|
get => progress;
|
|
set => progress = Mathf.Clamp01(value);
|
|
}
|
|
|
|
public string PresetId => presetId;
|
|
|
|
public void SetPreset(string id)
|
|
{
|
|
if (!string.IsNullOrEmpty(id))
|
|
{
|
|
_previousPresetId = string.IsNullOrEmpty(presetId) ? id : presetId;
|
|
presetId = id;
|
|
_presetTransitionElapsed = 0f;
|
|
_isPresetTransitioning = true;
|
|
}
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
_previousPresetId = presetId;
|
|
_presetTransitionElapsed = presetTransitionDuration;
|
|
_isPresetTransitioning = false;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (config == null || waveRenderer == null) return;
|
|
|
|
float dt = Time.deltaTime;
|
|
if (dt <= 0f)
|
|
dt = 1f / 60f;
|
|
_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(按当前预设;切换时做一次短暂 crossfade)
|
|
var targetParams = config.InterpolatePreset(presetId, _currentValue);
|
|
var waveParams = targetParams;
|
|
|
|
if (_isPresetTransitioning)
|
|
{
|
|
float duration = Mathf.Max(0.01f, presetTransitionDuration);
|
|
_presetTransitionElapsed += dt;
|
|
|
|
float blend = Smoothstep(Mathf.Clamp01(_presetTransitionElapsed / duration));
|
|
var previousParams = config.InterpolatePreset(_previousPresetId, _currentValue);
|
|
waveParams = EmotionWaveConfig.WaveParams.Lerp(previousParams, targetParams, blend);
|
|
waveParams.Clamp();
|
|
|
|
if (_presetTransitionElapsed >= duration)
|
|
{
|
|
_isPresetTransitioning = false;
|
|
_previousPresetId = presetId;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// CRT startup fade
|
|
waveParams.glowIntensity *= startupEase;
|
|
waveParams.lightness *= startupEase;
|
|
|
|
ApplyPresetSwitchFlash(ref waveParams);
|
|
|
|
// Push to renderer
|
|
waveRenderer.currentParams = waveParams;
|
|
waveRenderer.time = _elapsedTime;
|
|
}
|
|
|
|
private void ApplyPresetSwitchFlash(ref EmotionWaveConfig.WaveParams waveParams)
|
|
{
|
|
float duration = Mathf.Max(0.01f, presetFlashDuration);
|
|
if (_presetTransitionElapsed >= duration)
|
|
return;
|
|
|
|
float flash = 1f - Mathf.Clamp01(_presetTransitionElapsed / duration);
|
|
flash *= flash;
|
|
|
|
waveParams.glowIntensity = Mathf.Min(1.2f, waveParams.glowIntensity + presetFlashGlowBoost * flash);
|
|
waveParams.lightness = Mathf.Min(80f, waveParams.lightness + presetFlashLightnessBoost * flash);
|
|
waveParams.noiseAmount += presetFlashGlitchBoost * flash;
|
|
waveParams.jitter += presetFlashGlitchBoost * 0.45f * flash;
|
|
waveParams.fragmentation = Mathf.Clamp01(waveParams.fragmentation + 0.2f * flash);
|
|
waveParams.noiseSpeed += 6f * flash;
|
|
waveParams.lineWidth += 0.35f * flash;
|
|
waveParams.Clamp();
|
|
}
|
|
|
|
static float Smoothstep(float t)
|
|
{
|
|
t = Mathf.Clamp01(t);
|
|
return t * t * (3f - 2f * t);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Makes a field read-only in the Inspector.
|
|
/// </summary>
|
|
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
|
|
}
|