diff --git a/BopVisualEffects/Core/EffectEnvelope.cs b/BopVisualEffects/Core/EffectEnvelope.cs
new file mode 100644
index 0000000..52d7928
--- /dev/null
+++ b/BopVisualEffects/Core/EffectEnvelope.cs
@@ -0,0 +1,30 @@
+using UnityEngine;
+
+namespace BopVisualEffects.Core;
+
+///
+/// Shared envelope math for effects whose strength fades at event boundaries.
+///
+internal static class EffectEnvelope
+{
+ ///
+ /// Evaluates a fade envelope while allowing either boundary fade to be disabled.
+ ///
+ /// Normalized event progress in the range 0..1.
+ /// Whether to fade in from zero at the start.
+ /// Whether to fade out to zero at the end.
+ /// End of the fade-in segment.
+ /// Start of the fade-out segment.
+ public static float Evaluate(float progress, bool easeIn, bool easeOut, float fadeInEnd = 0.2f, float fadeOutStart = 0.8f)
+ {
+ progress = Mathf.Clamp01(progress);
+
+ if (easeIn && progress < fadeInEnd)
+ return Mathf.InverseLerp(0f, fadeInEnd, progress);
+
+ if (easeOut && progress > fadeOutStart)
+ return 1f - Mathf.InverseLerp(fadeOutStart, 1f, progress);
+
+ return 1f;
+ }
+}
diff --git a/BopVisualEffects/Effects/ColorTint/ColorTintEffect.cs b/BopVisualEffects/Effects/ColorTint/ColorTintEffect.cs
index 6e34e13..89fd3c7 100644
--- a/BopVisualEffects/Effects/ColorTint/ColorTintEffect.cs
+++ b/BopVisualEffects/Effects/ColorTint/ColorTintEffect.cs
@@ -32,7 +32,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
resizable = true,
properties = new Dictionary
{
- ["color"] = new MixtapeEventTemplates.ColorField(new Color(1.0f, 0.0f, 0.0f, 0.25f))
+ ["color"] = new MixtapeEventTemplates.ColorField(new Color(1.0f, 0.0f, 0.0f, 0.25f)),
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -43,6 +45,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var log = ClassLogger.GetForClass();
var durationBeats = Mathf.Max(0.01f, entity.length);
var color = entity.GetColor("color");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -53,7 +57,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, color));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, color, easeIn, easeOut));
}
}
@@ -66,17 +70,21 @@ private sealed class ColorTintRunner : MonoBehaviour
private MixtapeLoaderCustom? _loader;
private JukeboxScript? _jukebox;
private ColorTintOverlay? _overlay;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
- public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, Color color)
+ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, Color color, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
_startBeat = startBeat;
_endBeat = endBeat;
_color = new Color(Mathf.Clamp01(color.r), Mathf.Clamp01(color.g), Mathf.Clamp01(color.b), Mathf.Clamp01(color.a));
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeOverlay();
}
@@ -113,13 +121,7 @@ private void LateUpdate()
// Fade in over first 20%, hold, fade out over last 20%.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.2f)
- envelope = Mathf.InverseLerp(0f, 0.2f, progress);
- else if (progress > 0.8f)
- envelope = 1f - Mathf.InverseLerp(0.8f, 1f, progress);
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut);
if (_overlay != null)
_overlay.SetColor(new Color(_color.r, _color.g, _color.b, _color.a * envelope));
diff --git a/BopVisualEffects/Effects/Fog/FogEffect.cs b/BopVisualEffects/Effects/Fog/FogEffect.cs
index 05ff27a..548a9fd 100644
--- a/BopVisualEffects/Effects/Fog/FogEffect.cs
+++ b/BopVisualEffects/Effects/Fog/FogEffect.cs
@@ -33,7 +33,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
properties = new Dictionary
{
["color"] = new MixtapeEventTemplates.ColorField(new Color(0.8f, 0.8f, 0.9f, 0.6f)),
- ["height"] = 0.5f
+ ["height"] = 0.5f,
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -45,6 +47,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var durationBeats = Mathf.Max(0.01f, entity.length);
var color = entity.GetColor("color");
var height = entity.GetFloat("height");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -55,7 +59,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, color, height));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, color, height, easeIn, easeOut));
}
}
@@ -69,11 +73,13 @@ private sealed class FogRunner : MonoBehaviour
private MixtapeLoaderCustom? _loader;
private JukeboxScript? _jukebox;
private FogOverlay? _overlay;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
- public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, Color color, float height)
+ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, Color color, float height, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
@@ -81,6 +87,8 @@ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float
_endBeat = endBeat;
_color = new Color(Mathf.Clamp01(color.r), Mathf.Clamp01(color.g), Mathf.Clamp01(color.b), Mathf.Clamp01(color.a));
_height = Mathf.Clamp01(height);
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeOverlay();
}
@@ -117,13 +125,7 @@ private void LateUpdate()
// Fade in over first 20%, hold, fade out over last 20%.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.2f)
- envelope = Mathf.InverseLerp(0f, 0.2f, progress);
- else if (progress > 0.8f)
- envelope = 1f - Mathf.InverseLerp(0.8f, 1f, progress);
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut);
if (_overlay != null)
_overlay.SetParams(new Color(_color.r, _color.g, _color.b, _color.a * envelope), _height);
diff --git a/BopVisualEffects/Effects/Hsl/HslEffect.cs b/BopVisualEffects/Effects/Hsl/HslEffect.cs
index d10126e..cfe0b9d 100644
--- a/BopVisualEffects/Effects/Hsl/HslEffect.cs
+++ b/BopVisualEffects/Effects/Hsl/HslEffect.cs
@@ -37,7 +37,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
["hue_shift"] = 0.0f,
["saturation"] = 1.0f,
["lightness"] = 0.0f,
- ["intensity"] = 1.0f
+ ["intensity"] = 1.0f,
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -51,6 +53,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var saturation = entity.GetFloat("saturation");
var lightness = entity.GetFloat("lightness");
var intensity = entity.GetFloat("intensity");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -61,7 +65,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, hueShift, saturation, lightness, intensity));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, hueShift, saturation, lightness, intensity, easeIn, easeOut));
}
}
@@ -78,12 +82,14 @@ private sealed class HslRunner : MonoBehaviour
private JukeboxScript? _jukebox;
private Camera? _camera;
private HslRequest? _request;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat,
- float hueShift, float saturation, float lightness, float intensity)
+ float hueShift, float saturation, float lightness, float intensity, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
@@ -93,6 +99,8 @@ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float
_saturation = Mathf.Max(0f, saturation);
_lightness = Mathf.Clamp(lightness, -0.5f, 0.5f);
_maxIntensity = Mathf.Clamp01(intensity);
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeRequest();
}
@@ -129,13 +137,7 @@ private void LateUpdate()
// Fade in over first 20%, hold, fade out over last 20%.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.2f)
- envelope = Mathf.InverseLerp(0f, 0.2f, progress);
- else if (progress > 0.8f)
- envelope = 1f - Mathf.InverseLerp(0.8f, 1f, progress);
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut);
if (_request != null)
{
@@ -178,4 +180,3 @@ private void RemoveRequest()
}
}
}
-
diff --git a/BopVisualEffects/Effects/Letterbox/LetterboxEffect.cs b/BopVisualEffects/Effects/Letterbox/LetterboxEffect.cs
index 0cf0057..06dcee6 100644
--- a/BopVisualEffects/Effects/Letterbox/LetterboxEffect.cs
+++ b/BopVisualEffects/Effects/Letterbox/LetterboxEffect.cs
@@ -32,7 +32,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
resizable = true,
properties = new Dictionary
{
- ["size"] = 0.1f
+ ["size"] = 0.1f,
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -43,6 +45,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var log = ClassLogger.GetForClass();
var durationBeats = Mathf.Max(0.01f, entity.length);
var size = entity.GetFloat("size");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -53,7 +57,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, size));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, size, easeIn, easeOut));
}
}
@@ -66,17 +70,21 @@ private sealed class LetterboxRunner : MonoBehaviour
private MixtapeLoaderCustom? _loader;
private JukeboxScript? _jukebox;
private LetterboxOverlay? _overlay;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
- public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float size)
+ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float size, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
_startBeat = startBeat;
_endBeat = endBeat;
_size = Mathf.Clamp(size, 0f, 0.49f);
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeOverlay();
}
@@ -113,13 +121,7 @@ private void LateUpdate()
// Slide bars in over first 15%, hold, slide out over last 15%.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.15f)
- envelope = Mathf.InverseLerp(0f, 0.15f, progress);
- else if (progress > 0.85f)
- envelope = 1f - Mathf.InverseLerp(0.85f, 1f, progress);
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut, 0.15f, 0.85f);
if (_overlay != null)
_overlay.SetParams(_size * envelope);
diff --git a/BopVisualEffects/Effects/PixelGrid/PixelGridEffect.cs b/BopVisualEffects/Effects/PixelGrid/PixelGridEffect.cs
index d1e5f08..d1c52b4 100644
--- a/BopVisualEffects/Effects/PixelGrid/PixelGridEffect.cs
+++ b/BopVisualEffects/Effects/PixelGrid/PixelGridEffect.cs
@@ -33,7 +33,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
resizable = true,
properties = new Dictionary
{
- ["pixel_size"] = 4.0f
+ ["pixel_size"] = 4.0f,
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -44,6 +46,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var log = ClassLogger.GetForClass();
var durationBeats = Mathf.Max(0.01f, entity.length);
var pixelSize = entity.GetFloat("pixel_size");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -54,7 +58,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, pixelSize));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, pixelSize, easeIn, easeOut));
}
}
@@ -67,17 +71,21 @@ private sealed class PixelGridRunner : MonoBehaviour
private MixtapeLoaderCustom? _loader;
private JukeboxScript? _jukebox;
private PixelGridOverlay? _overlay;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
- public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float pixelSize)
+ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float pixelSize, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
_startBeat = startBeat;
_endBeat = endBeat;
_pixelSize = Mathf.Clamp(Mathf.RoundToInt(pixelSize), 2, 64);
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeOverlay();
}
@@ -115,13 +123,7 @@ private void LateUpdate()
// Ramp block size up over first 15%, hold, ramp down over last 15%.
// block_size=1 means no pixelation; ramping from 1 → _pixelSize gives a "zooming into pixels" look.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.15f)
- envelope = Mathf.InverseLerp(0f, 0.15f, progress);
- else if (progress > 0.85f)
- envelope = 1f - Mathf.InverseLerp(0.85f, 1f, progress);
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut, 0.15f, 0.85f);
var currentBlockSize = Mathf.Max(1, Mathf.RoundToInt(Mathf.Lerp(1f, _pixelSize, envelope)));
if (_overlay != null)
diff --git a/BopVisualEffects/Effects/Scanlines/ScanlinesEffect.cs b/BopVisualEffects/Effects/Scanlines/ScanlinesEffect.cs
index af16a71..d2e4902 100644
--- a/BopVisualEffects/Effects/Scanlines/ScanlinesEffect.cs
+++ b/BopVisualEffects/Effects/Scanlines/ScanlinesEffect.cs
@@ -34,7 +34,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
{
["alpha"] = 0.35f,
["count"] = 60.0f,
- ["scroll_speed"] = 0.0f
+ ["scroll_speed"] = 0.0f,
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -47,6 +49,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var alpha = entity.GetFloat("alpha");
var count = entity.GetFloat("count");
var scrollSpeed = entity.GetFloat("scroll_speed");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -57,7 +61,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, alpha, count, scrollSpeed));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, alpha, count, scrollSpeed, easeIn, easeOut));
}
}
@@ -72,11 +76,13 @@ private sealed class ScanlinesRunner : MonoBehaviour
private MixtapeLoaderCustom? _loader;
private JukeboxScript? _jukebox;
private ScanlinesOverlay? _overlay;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
- public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float alpha, float count, float scrollSpeed)
+ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float alpha, float count, float scrollSpeed, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
@@ -85,6 +91,8 @@ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float
_alpha = Mathf.Clamp01(alpha);
_count = Mathf.Clamp(Mathf.RoundToInt(count), 4, 2000);
_scrollSpeed = scrollSpeed;
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeOverlay();
}
@@ -121,13 +129,7 @@ private void LateUpdate()
// Fade in over first 15%, hold, fade out over last 15%.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.15f)
- envelope = Mathf.InverseLerp(0f, 0.15f, progress);
- else if (progress > 0.85f)
- envelope = 1f - Mathf.InverseLerp(0.85f, 1f, progress);
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut, 0.15f, 0.85f);
if (_overlay != null)
_overlay.SetParams(_alpha * envelope, _count, _scrollSpeed);
diff --git a/BopVisualEffects/Effects/ScreenNoise/ScreenNoiseEffect.cs b/BopVisualEffects/Effects/ScreenNoise/ScreenNoiseEffect.cs
index 4b0c240..19e856b 100644
--- a/BopVisualEffects/Effects/ScreenNoise/ScreenNoiseEffect.cs
+++ b/BopVisualEffects/Effects/ScreenNoise/ScreenNoiseEffect.cs
@@ -34,7 +34,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
{
["alpha"] = 0.5f,
["count"] = 400.0f,
- ["size"] = 0.01f
+ ["size"] = 0.01f,
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -47,6 +49,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var alpha = entity.GetFloat("alpha");
var count = entity.GetFloat("count");
var size = entity.GetFloat("size");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -57,7 +61,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, alpha, count, size));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, alpha, count, size, easeIn, easeOut));
}
}
@@ -72,11 +76,13 @@ private sealed class ScreenNoiseRunner : MonoBehaviour
private MixtapeLoaderCustom? _loader;
private JukeboxScript? _jukebox;
private ScreenNoiseOverlay? _overlay;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
- public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float alpha, float count, float size)
+ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float alpha, float count, float size, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
@@ -85,6 +91,8 @@ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float
_alpha = Mathf.Clamp01(alpha);
_count = Mathf.Clamp(Mathf.RoundToInt(count), 10, 2000);
_size = Mathf.Clamp(size, 0.005f, 0.1f);
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeOverlay();
}
@@ -121,13 +129,7 @@ private void LateUpdate()
// Fade in over first 15%, hold, fade out over last 15%.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.15f)
- envelope = Mathf.InverseLerp(0f, 0.15f, progress);
- else if (progress > 0.85f)
- envelope = 1f - Mathf.InverseLerp(0.85f, 1f, progress);
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut, 0.15f, 0.85f);
if (_overlay != null)
_overlay.SetParams(_alpha * envelope, _count, _size);
diff --git a/BopVisualEffects/Effects/Sepia/SepiaEffect.cs b/BopVisualEffects/Effects/Sepia/SepiaEffect.cs
index f2a8592..6a81cf9 100644
--- a/BopVisualEffects/Effects/Sepia/SepiaEffect.cs
+++ b/BopVisualEffects/Effects/Sepia/SepiaEffect.cs
@@ -34,7 +34,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
resizable = true,
properties = new Dictionary
{
- ["intensity"] = 0.8f
+ ["intensity"] = 0.8f,
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -45,6 +47,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var log = ClassLogger.GetForClass();
var durationBeats = Mathf.Max(0.01f, entity.length);
var intensity = entity.GetFloat("intensity");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -55,7 +59,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, intensity));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, intensity, easeIn, easeOut));
}
}
@@ -69,17 +73,21 @@ private sealed class SepiaRunner : MonoBehaviour
private JukeboxScript? _jukebox;
private Camera? _camera;
private SepiaRequest? _request;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
- public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float intensity)
+ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float intensity, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
_startBeat = startBeat;
_endBeat = endBeat;
_maxIntensity = Mathf.Clamp01(intensity);
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeRequest();
}
@@ -116,13 +124,7 @@ private void LateUpdate()
// Fade in over first 20%, hold, fade out over last 20%.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.2f)
- envelope = Mathf.InverseLerp(0f, 0.2f, progress);
- else if (progress > 0.8f)
- envelope = 1f - Mathf.InverseLerp(0.8f, 1f, progress);
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut);
if (_request != null)
_request.Intensity = _maxIntensity * envelope;
@@ -155,4 +157,3 @@ private void RemoveRequest()
}
}
}
-
diff --git a/BopVisualEffects/Effects/Vignette/VignetteEffect.cs b/BopVisualEffects/Effects/Vignette/VignetteEffect.cs
index d872743..146ac9f 100644
--- a/BopVisualEffects/Effects/Vignette/VignetteEffect.cs
+++ b/BopVisualEffects/Effects/Vignette/VignetteEffect.cs
@@ -33,7 +33,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
properties = new Dictionary
{
["alpha"] = 0.7f,
- ["size"] = 0.1f
+ ["size"] = 0.1f,
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -45,6 +47,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var durationBeats = Mathf.Max(0.01f, entity.length);
var alpha = entity.GetFloat("alpha");
var size = entity.GetFloat("size");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -55,7 +59,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, alpha, size));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, alpha, size, easeIn, easeOut));
}
}
@@ -69,11 +73,13 @@ private sealed class VignetteRunner : MonoBehaviour
private MixtapeLoaderCustom? _loader;
private JukeboxScript? _jukebox;
private VignetteOverlay? _overlay;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
- public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float alpha, float size)
+ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float alpha, float size, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
@@ -81,6 +87,8 @@ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float
_endBeat = endBeat;
_alpha = Mathf.Clamp01(alpha);
_size = Mathf.Clamp(size, 0f, 0.5f);
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeOverlay();
}
@@ -117,13 +125,7 @@ private void LateUpdate()
// Fade in over first 20%, hold, fade out over last 20%.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.2f)
- envelope = Mathf.InverseLerp(0f, 0.2f, progress);
- else if (progress > 0.8f)
- envelope = 1f - Mathf.InverseLerp(0.8f, 1f, progress);
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut);
if (_overlay != null)
_overlay.SetParams(_alpha * envelope, _size);
diff --git a/BopVisualEffects/Effects/ZoomIn/ZoomInEffect.cs b/BopVisualEffects/Effects/ZoomIn/ZoomInEffect.cs
index 403b489..38abf10 100644
--- a/BopVisualEffects/Effects/ZoomIn/ZoomInEffect.cs
+++ b/BopVisualEffects/Effects/ZoomIn/ZoomInEffect.cs
@@ -32,7 +32,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
resizable = true,
properties = new Dictionary
{
- ["intensity"] = 0.2f
+ ["intensity"] = 0.2f,
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -43,6 +45,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var log = ClassLogger.GetForClass();
var durationBeats = Mathf.Max(0.01f, entity.length);
var intensity = entity.GetFloat("intensity");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -53,7 +57,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, intensity));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, intensity, easeIn, easeOut));
}
}
@@ -66,17 +70,21 @@ private sealed class ZoomInRunner : MonoBehaviour
private MixtapeLoaderCustom? _loader;
private JukeboxScript? _jukebox;
private Camera? _camera;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
- public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float intensity)
+ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float intensity, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
_startBeat = startBeat;
_endBeat = endBeat;
_intensity = Mathf.Clamp(intensity, 0f, 0.99f);
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeTargetCamera();
}
@@ -114,13 +122,7 @@ private void LateUpdate()
// Ease in over first 20%, hold zoomed in from 20-80%, ease back out over last 20%.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.2f)
- envelope = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(0f, 0.2f, progress));
- else if (progress > 0.8f)
- envelope = Mathf.SmoothStep(0f, 1f, 1f - Mathf.InverseLerp(0.8f, 1f, progress));
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut);
// Zoom in: zoomFactor < 1 means smaller camera size = more zoomed in.
var zoomFactor = Mathf.Clamp(1f - _intensity * envelope, 0.01f, 1f);
diff --git a/BopVisualEffects/Effects/ZoomOut/ZoomOutEffect.cs b/BopVisualEffects/Effects/ZoomOut/ZoomOutEffect.cs
index ff25b25..0014015 100644
--- a/BopVisualEffects/Effects/ZoomOut/ZoomOutEffect.cs
+++ b/BopVisualEffects/Effects/ZoomOut/ZoomOutEffect.cs
@@ -32,7 +32,9 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid)
resizable = true,
properties = new Dictionary
{
- ["intensity"] = 0.2f
+ ["intensity"] = 0.2f,
+ ["ease_in"] = true,
+ ["ease_out"] = true
}
};
}
@@ -43,6 +45,8 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
var log = ClassLogger.GetForClass();
var durationBeats = Mathf.Max(0.01f, entity.length);
var intensity = entity.GetFloat("intensity");
+ var easeIn = entity.GetBool("ease_in", true);
+ var easeOut = entity.GetBool("ease_out", true);
var startBeat = entity.beat;
var endBeat = startBeat + durationBeats;
@@ -53,7 +57,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader)
void SpawnAction()
{
EffectRuntimeController.Instance.SpawnRunner(runner =>
- runner.Initialize(loader, loader.jukebox, startBeat, endBeat, intensity));
+ runner.Initialize(loader, loader.jukebox, startBeat, endBeat, intensity, easeIn, easeOut));
}
}
@@ -66,17 +70,21 @@ private sealed class ZoomOutRunner : MonoBehaviour
private MixtapeLoaderCustom? _loader;
private JukeboxScript? _jukebox;
private Camera? _camera;
+ private bool _easeIn;
+ private bool _easeOut;
///
/// Initializes this runner with effect parameters.
///
- public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float intensity)
+ public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float intensity, bool easeIn, bool easeOut)
{
_loader = loader;
_jukebox = jukebox;
_startBeat = startBeat;
_endBeat = endBeat;
_intensity = Mathf.Max(0f, intensity);
+ _easeIn = easeIn;
+ _easeOut = easeOut;
InitializeTargetCamera();
}
@@ -114,13 +122,7 @@ private void LateUpdate()
// Ease out over first 20%, hold zoomed out from 20-80%, ease back in over last 20%.
var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat);
- float envelope;
- if (progress < 0.2f)
- envelope = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(0f, 0.2f, progress));
- else if (progress > 0.8f)
- envelope = Mathf.SmoothStep(0f, 1f, 1f - Mathf.InverseLerp(0.8f, 1f, progress));
- else
- envelope = 1f;
+ var envelope = EffectEnvelope.Evaluate(progress, _easeIn, _easeOut);
// Zoom out: zoomFactor > 1 means larger camera size = more zoomed out.
var zoomFactor = 1f + _intensity * envelope;
diff --git a/docs/effects/README.md b/docs/effects/README.md
index f9cb92a..cb96714 100644
--- a/docs/effects/README.md
+++ b/docs/effects/README.md
@@ -45,6 +45,8 @@ Applies a sustained full-screen color tint that fades in, holds, then fades out.
**Properties**
- `color`: Tint color (default `#ff000040` — red with slight opacity).
+- `ease_in`: Fade in from zero at the event start (default `true`).
+- `ease_out`: Fade out to zero at the event end (default `true`).
- `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline.
**Preview**
@@ -62,6 +64,8 @@ Draws a ground fog gradient overlay - opaque at the bottom of the screen, fading
**Properties**
- `color`: Fog color (default `#CCCCE599` - pale blue-gray).
- `height`: Normalized screen height at which the fog fully fades to transparent (0–1; default `0.5`).
+- `ease_in`: Fade in from zero at the event start (default `true`).
+- `ease_out`: Fade out to zero at the event end (default `true`).
- `length` (event length in editor): How long the fog lasts, in beats. This event is resizable in the timeline.
**Preview**
@@ -96,6 +100,8 @@ Adjusts hue, saturation and lightness of the whole screen for colour grading usi
- `saturation`: Saturation multiplier (0 = fully greyscale, 1 = unchanged, >1 = boosted; default `1.0`). Boost above 1 requires shader bundle.
- `lightness`: Additive lightness offset (-0.5–0.5; default `0`). Positive values brighten, negative values darken.
- `intensity`: Overall blend strength (0–1; default `1.0`).
+- `ease_in`: Fade in from zero at the event start (default `true`).
+- `ease_out`: Fade out to zero at the event end (default `true`).
- `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline.
**Preview**
@@ -112,6 +118,8 @@ Adds cinematic black bars at the top and bottom of the screen for a dramatic wid
**Properties**
- `size`: Height of each bar as a normalized screen fraction (0–0.49; default `0.1`).
+- `ease_in`: Slide bars in at the event start (default `true`).
+- `ease_out`: Slide bars out at the event end (default `true`).
- `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline.
**Preview**
@@ -128,6 +136,8 @@ Pixelates the screen by downsampling the camera's rendered output to a low-resol
**Properties**
- `pixel_size`: Size of each pixel block in screen pixels (2–64; default `4`). Larger values produce a more pronounced 8-bit look.
+- `ease_in`: Ramp pixelation in at the event start (default `true`).
+- `ease_out`: Ramp pixelation out at the event end (default `true`).
- `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline.
**Preview**
@@ -146,6 +156,8 @@ Draws horizontal CRT-style scan lines over the screen for a retro aesthetic. Can
- `alpha`: Darkness of each scan line (0–1; default `0.35`).
- `count`: Number of scan lines (default `60`, clamped 4–2000).
- `scroll_speed`: Speed at which lines scroll upward, in cells per second (default `0` = static; negative = scroll downward).
+- `ease_in`: Fade scanlines in at the event start (default `true`).
+- `ease_out`: Fade scanlines out at the event end (default `true`).
- `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline.
**Preview**
@@ -164,6 +176,8 @@ Draws animated TV static noise specks over the screen for a glitchy atmosphere.
- `alpha`: Maximum opacity of the noise specks (0–1; default `0.5`).
- `count`: Number of noise specks drawn per frame (10–2000; default `400`).
- `size`: Physical size of each speck in normalized screen coordinates (0.005–0.1; default `0.01`).
+- `ease_in`: Fade noise in at the event start (default `true`).
+- `ease_out`: Fade noise out at the event end (default `true`).
- `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline.
**Preview**
@@ -180,6 +194,8 @@ Applies a warm vintage sepia-tone filter using a per-pixel shader (standard Adob
**Properties**
- `intensity`: Strength of the sepia toning (0–1; default `0.8`).
+- `ease_in`: Fade sepia in at the event start (default `true`).
+- `ease_out`: Fade sepia out at the event end (default `true`).
- `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline.
**Preview**
@@ -212,6 +228,8 @@ Darkens the screen edges with a smooth radial/elliptical gradient using a 32-seg
**Properties**
- `alpha`: Darkness of the edge (0–1; default `0.7`).
- `size`: How far the darkening extends inward as a fraction of screen half-height (0–0.5; default `0.1`).
+- `ease_in`: Fade vignette in at the event start (default `true`).
+- `ease_out`: Fade vignette out at the event end (default `true`).
- `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline.
**Preview**
@@ -228,6 +246,8 @@ Eases the camera smoothly into a zoomed-in view, holds at the target level, then
**Properties**
- `intensity`: How far to zoom in, as a fraction of the camera's base size (e.g. `0.2` = 20% closer). Clamped to `[0, 0.99]`.
+- `ease_in`: Ease into the zoom at the event start (default `true`).
+- `ease_out`: Ease out of the zoom at the event end (default `true`).
- `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline.
**Preview**
@@ -244,6 +264,8 @@ Eases the camera smoothly out to a wider view, holds, then eases back in. Compos
**Properties**
- `intensity`: How far to zoom out, as a fraction of the camera's base size (e.g. `0.2` = 20% further out).
+- `ease_in`: Ease into the zoom at the event start (default `true`).
+- `ease_out`: Ease out of the zoom at the event end (default `true`).
- `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline.
**Preview**