Files
aibis-dream/Assets/Scripts/MiniGame/HuoShan/EmotionWave/EmotionWaveConfig.cs
T
2026-02-25 16:19:28 +08:00

210 lines
8.4 KiB
C#

using UnityEngine;
namespace AibisDream.MiniGame.HuoShan.EmotionWave
{
[CreateAssetMenu(fileName = "EmotionWaveConfig", menuName = "AibisDream/EmotionWave Config")]
public class EmotionWaveConfig : ScriptableObject
{
[System.Serializable]
public struct WaveParams
{
[Header("Trajectory Shape")]
[Range(0f, 1f)] public float waveMode;
[Range(0.1f, 1f)] public float timeSpread;
public float omegaX;
public float omegaY;
public float phaseSpeed;
[Min(1)] public int numCycles;
[Header("Amplitude & Waveform")]
public float amplitude;
public float amplitudeY;
[Range(0f, 2f)] public float waveformType;
[Range(0f, 1f)] public float spikiness;
[Range(1f, 12f)] public float waveshapeGain;
public float harmonicStrength;
public float harmonicFreq;
[Header("Noise & Glitch")]
public float noiseAmount;
public float noiseScale;
public float noiseSpeed;
public float jitter;
[Range(0f, 1f)] public float weierstrassAmount;
[Header("Line")]
public float lineWidth;
[Range(0.5f, 0.98f)] public float persistence;
[Range(0f, 1f)] public float fragmentation;
[Header("Color (HSL)")]
public float hue;
[Range(0f, 100f)] public float saturation;
[Range(5f, 80f)] public float lightness;
[Range(0f, 1.2f)] public float glowIntensity;
[Header("Dynamics")]
public float breathRate;
public float breathDepth;
[Header("Review (injected at runtime)")]
[HideInInspector] public float reviewIntensity;
[HideInInspector] public float featureBoxRate;
[HideInInspector] public float thresholdLevel;
[HideInInspector] public float cursorSpeed;
[Header("Spike")]
[HideInInspector] public float spikeIntensity;
[HideInInspector] public float spikeFreq;
[HideInInspector] public float spikeSharpness;
[HideInInspector] public float envelopeDecay;
[HideInInspector] public float tipLayer;
public static WaveParams Lerp(WaveParams a, WaveParams b, float t)
{
return new WaveParams
{
waveMode = Mathf.Lerp(a.waveMode, b.waveMode, t),
timeSpread = Mathf.Lerp(a.timeSpread, b.timeSpread, t),
omegaX = Mathf.Lerp(a.omegaX, b.omegaX, t),
omegaY = Mathf.Lerp(a.omegaY, b.omegaY, t),
phaseSpeed = Mathf.Lerp(a.phaseSpeed, b.phaseSpeed, t),
numCycles = Mathf.RoundToInt(Mathf.Lerp(a.numCycles, b.numCycles, t)),
amplitude = Mathf.Lerp(a.amplitude, b.amplitude, t),
amplitudeY = Mathf.Lerp(a.amplitudeY, b.amplitudeY, t),
waveformType = Mathf.Lerp(a.waveformType, b.waveformType, t),
spikiness = Mathf.Lerp(a.spikiness, b.spikiness, t),
waveshapeGain = Mathf.Lerp(a.waveshapeGain, b.waveshapeGain, t),
harmonicStrength = Mathf.Lerp(a.harmonicStrength, b.harmonicStrength, t),
harmonicFreq = Mathf.Lerp(a.harmonicFreq, b.harmonicFreq, t),
noiseAmount = Mathf.Lerp(a.noiseAmount, b.noiseAmount, t),
noiseScale = Mathf.Lerp(a.noiseScale, b.noiseScale, t),
noiseSpeed = Mathf.Lerp(a.noiseSpeed, b.noiseSpeed, t),
jitter = Mathf.Lerp(a.jitter, b.jitter, t),
weierstrassAmount = Mathf.Lerp(a.weierstrassAmount, b.weierstrassAmount, t),
lineWidth = Mathf.Lerp(a.lineWidth, b.lineWidth, t),
persistence = Mathf.Lerp(a.persistence, b.persistence, t),
fragmentation = Mathf.Lerp(a.fragmentation, b.fragmentation, t),
hue = Mathf.Lerp(a.hue, b.hue, t),
saturation = Mathf.Lerp(a.saturation, b.saturation, t),
lightness = Mathf.Lerp(a.lightness, b.lightness, t),
glowIntensity = Mathf.Lerp(a.glowIntensity, b.glowIntensity, t),
breathRate = Mathf.Lerp(a.breathRate, b.breathRate, t),
breathDepth = Mathf.Lerp(a.breathDepth, b.breathDepth, t),
};
}
public void Clamp()
{
numCycles = Mathf.Max(2, numCycles);
amplitude = Mathf.Max(0.05f, amplitude);
amplitudeY = Mathf.Max(0.05f, amplitudeY);
lineWidth = Mathf.Max(0.3f, lineWidth);
persistence = Mathf.Clamp(persistence, 0.5f, 0.98f);
glowIntensity = Mathf.Clamp(glowIntensity, 0f, 1.2f);
noiseAmount = Mathf.Max(0f, noiseAmount);
fragmentation = Mathf.Clamp01(fragmentation);
hue = ((hue % 360f) + 360f) % 360f;
saturation = Mathf.Clamp(saturation, 0f, 100f);
lightness = Mathf.Clamp(lightness, 5f, 80f);
waveMode = Mathf.Clamp01(waveMode);
spikiness = Mathf.Clamp01(spikiness);
waveformType = Mathf.Clamp(waveformType, 0f, 2f);
timeSpread = Mathf.Clamp(timeSpread, 0.1f, 1f);
waveshapeGain = Mathf.Clamp(waveshapeGain, 1f, 12f);
weierstrassAmount = Mathf.Clamp01(weierstrassAmount);
}
}
[System.Serializable]
public struct StageAnchor
{
public float position;
public string name;
public string stage;
public WaveParams waveParams;
}
public StageAnchor[] anchors = new StageAnchor[9];
static float Smoothstep(float t)
{
t = Mathf.Clamp01(t);
return t * t * (3f - 2f * t);
}
public WaveParams Interpolate(float value)
{
value = Mathf.Clamp01(value);
int n = anchors.Length;
if (n == 0) return default;
if (n == 1) return anchors[0].waveParams;
int idx = 0;
for (int i = 0; i < n - 1; i++)
{
if (value <= anchors[i + 1].position)
{
idx = i;
break;
}
if (i == n - 2) idx = n - 2;
}
int i1 = idx;
int i2 = Mathf.Min(n - 1, idx + 1);
float segStart = anchors[i1].position;
float segEnd = anchors[i2].position;
float segLen = segEnd - segStart;
float tLinear = segLen > 0f ? (value - segStart) / segLen : 0f;
float t = Smoothstep(tLinear);
var result = WaveParams.Lerp(anchors[i1].waveParams, anchors[i2].waveParams, t);
result.Clamp();
return result;
}
public string GetStageName(float value)
{
value = Mathf.Clamp01(value);
if (anchors == null || anchors.Length == 0) return "";
StageAnchor closest = anchors[0];
float minDist = float.MaxValue;
foreach (var a in anchors)
{
float d = Mathf.Abs(value - a.position);
if (d < minDist) { minDist = d; closest = a; }
}
if (minDist < 0.03f) return closest.name;
StageAnchor lower = anchors[0];
StageAnchor upper = anchors[anchors.Length - 1];
for (int i = 0; i < anchors.Length - 1; i++)
{
if (value >= anchors[i].position && value <= anchors[i + 1].position)
{
lower = anchors[i];
upper = anchors[i + 1];
break;
}
}
float blend = (upper.position - lower.position) > 0.001f
? (value - lower.position) / (upper.position - lower.position)
: 0f;
if (blend < 0.35f) return lower.name;
if (blend > 0.65f) return upper.name;
return $"{lower.name} -> {upper.name}";
}
public string GetStagePhase(float value)
{
if (value < 0.35f) return "BUILD";
if (value < 0.65f) return "REVIEW";
return "ADJUST";
}
}
}