210 lines
8.4 KiB
C#
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";
|
|
}
|
|
}
|
|
}
|