From 7a3061fc4c1d339f07c94e15acbf8741eceafc03 Mon Sep 17 00:00:00 2001 From: Brollyy Date: Sun, 1 Mar 2026 17:38:30 +0100 Subject: [PATCH 1/7] Camera shake video --- docs/effects/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/effects/README.md b/docs/effects/README.md index e11d8fd..0b3a230 100644 --- a/docs/effects/README.md +++ b/docs/effects/README.md @@ -15,11 +15,9 @@ Adds a temporary camera shake effect for impact, hits, drops, or strong rhythm a - `frequency`: Speed of the shake movement. - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. -**Preview media** +**Preview** -![Camera Shake preview](camera-shake/preview.gif) - -[Watch video preview](camera-shake/demo.mp4) + ## Camera Tilt From 215368a74f5f561f3bdec21b3b9ec5ab11137061 Mon Sep 17 00:00:00 2001 From: Brollyy Date: Sun, 1 Mar 2026 17:40:41 +0100 Subject: [PATCH 2/7] Camera shake video --- docs/effects/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/effects/README.md b/docs/effects/README.md index 0b3a230..f3649ac 100644 --- a/docs/effects/README.md +++ b/docs/effects/README.md @@ -17,7 +17,7 @@ Adds a temporary camera shake effect for impact, hits, drops, or strong rhythm a **Preview** - +[![Watch the video](https://img.youtube.com/vi/9YBuEKdRlTk/maxresdefault.jpg)](https://youtu.be/9YBuEKdRlTk) ## Camera Tilt From 2f1cb2d37aab9172b953f8e2a3cbd1dda1a15e66 Mon Sep 17 00:00:00 2001 From: Brollyy Date: Sun, 1 Mar 2026 18:08:06 +0100 Subject: [PATCH 3/7] Refactor to use built-in ColorField support --- BopVisualEffects/Core/EntityExtensions.cs | 53 +++++++++++++++++++ .../Effects/ColorTint/ColorTintEffect.cs | 44 +++++---------- BopVisualEffects/Effects/Fog/FogEffect.cs | 49 ++++++----------- 3 files changed, 81 insertions(+), 65 deletions(-) create mode 100644 BopVisualEffects/Core/EntityExtensions.cs diff --git a/BopVisualEffects/Core/EntityExtensions.cs b/BopVisualEffects/Core/EntityExtensions.cs new file mode 100644 index 0000000..e885edd --- /dev/null +++ b/BopVisualEffects/Core/EntityExtensions.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace BopVisualEffects.Core; + +/// +/// Extension methods for reading typed values from properties. +/// +internal static class EntityExtensions +{ + /// + /// Reads a from an entity property by . + /// + /// + /// Handles two cases, in order: + /// + /// The value is already a struct (in-memory, before a save/load cycle). + /// + /// The value is a with r/g/b/a sub-keys + /// (produced when the game JSON-deserializes a stored without type info). + /// + /// + /// Returns (default: ) when neither applies. + /// + /// The entity to read from. + /// Property key holding the color value. + /// Value returned when the property is absent or unrecognised. + public static Color GetColor(this Entity entity, string key, Color? fallback = null) + { + if (!entity.dynamicData.TryGetValue(key, out var value) || value is null) return fallback ?? Color.white; + return value switch + { + Color color => color, + Dictionary dict => new Color(GetSubFloat(dict, "r"), GetSubFloat(dict, "g"), + GetSubFloat(dict, "b"), GetSubFloat(dict, "a", 1f)), + _ => fallback ?? Color.white + }; + } + + private static float GetSubFloat(Dictionary dict, string key, float defaultValue = 0f) + { + if (!dict.TryGetValue(key, out object? value) || value is null) + return defaultValue; + + return value switch + { + float f => f, + double d => (float)d, + int i => i, + _ => defaultValue + }; + } +} diff --git a/BopVisualEffects/Effects/ColorTint/ColorTintEffect.cs b/BopVisualEffects/Effects/ColorTint/ColorTintEffect.cs index c3c3216..6e34e13 100644 --- a/BopVisualEffects/Effects/ColorTint/ColorTintEffect.cs +++ b/BopVisualEffects/Effects/ColorTint/ColorTintEffect.cs @@ -32,10 +32,7 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid) resizable = true, properties = new Dictionary { - ["r"] = 1.0f, - ["g"] = 0.0f, - ["b"] = 0.0f, - ["alpha"] = 0.25f + ["color"] = new MixtapeEventTemplates.ColorField(new Color(1.0f, 0.0f, 0.0f, 0.25f)) } }; } @@ -45,10 +42,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader) { var log = ClassLogger.GetForClass(); var durationBeats = Mathf.Max(0.01f, entity.length); - var r = entity.GetFloat("r"); - var g = entity.GetFloat("g"); - var b = entity.GetFloat("b"); - var alpha = entity.GetFloat("alpha"); + var color = entity.GetColor("color"); var startBeat = entity.beat; var endBeat = startBeat + durationBeats; @@ -59,17 +53,14 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader) void SpawnAction() { EffectRuntimeController.Instance.SpawnRunner(runner => - runner.Initialize(loader, loader.jukebox, startBeat, endBeat, r, g, b, alpha)); + runner.Initialize(loader, loader.jukebox, startBeat, endBeat, color)); } } private sealed class ColorTintRunner : MonoBehaviour { private bool _initialized; - private float _r; - private float _g; - private float _b; - private float _maxAlpha; + private Color _color; private float _startBeat; private float _endBeat; private MixtapeLoaderCustom? _loader; @@ -79,16 +70,13 @@ private sealed class ColorTintRunner : MonoBehaviour /// /// Initializes this runner with effect parameters. /// - public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float r, float g, float b, float alpha) + public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, Color color) { _loader = loader; _jukebox = jukebox; _startBeat = startBeat; _endBeat = endBeat; - _r = Mathf.Clamp01(r); - _g = Mathf.Clamp01(g); - _b = Mathf.Clamp01(b); - _maxAlpha = Mathf.Clamp01(alpha); + _color = new Color(Mathf.Clamp01(color.r), Mathf.Clamp01(color.g), Mathf.Clamp01(color.b), Mathf.Clamp01(color.a)); InitializeOverlay(); } @@ -134,7 +122,7 @@ private void LateUpdate() envelope = 1f; if (_overlay != null) - _overlay.SetColor(_r, _g, _b, _maxAlpha * envelope); + _overlay.SetColor(new Color(_color.r, _color.g, _color.b, _color.a * envelope)); } private void OnDisable() @@ -149,7 +137,7 @@ private void InitializeOverlay() return; _overlay = camera.gameObject.AddComponent(); - _overlay.SetColor(_r, _g, _b, _maxAlpha); + _overlay.SetColor(_color); _initialized = true; } @@ -170,20 +158,14 @@ private void RemoveOverlay() private sealed class ColorTintOverlay : MonoBehaviour { private static Material? _material; - private float _r; - private float _g; - private float _b; - private float _alpha; + private Color _color; /// /// Updates the overlay color and opacity. /// - public void SetColor(float r, float g, float b, float alpha) + public void SetColor(Color color) { - _r = r; - _g = g; - _b = b; - _alpha = alpha; + _color = color; } private static Material? GetMaterial() @@ -207,7 +189,7 @@ public void SetColor(float r, float g, float b, float alpha) // Unity calls this on the camera's GameObject after it finishes rendering the scene. private void OnPostRender() { - if (_alpha <= 0f) + if (_color.a <= 0f) return; var mat = GetMaterial(); @@ -219,7 +201,7 @@ private void OnPostRender() GL.PushMatrix(); GL.LoadOrtho(); GL.Begin(GL.QUADS); - GL.Color(new Color(_r, _g, _b, _alpha)); + GL.Color(_color); GL.Vertex3(0f, 0f, 0f); GL.Vertex3(0f, 1f, 0f); GL.Vertex3(1f, 1f, 0f); diff --git a/BopVisualEffects/Effects/Fog/FogEffect.cs b/BopVisualEffects/Effects/Fog/FogEffect.cs index 5e1a8c0..05ff27a 100644 --- a/BopVisualEffects/Effects/Fog/FogEffect.cs +++ b/BopVisualEffects/Effects/Fog/FogEffect.cs @@ -32,10 +32,7 @@ public MixtapeEventTemplate CreateTemplate(string pluginGuid) resizable = true, properties = new Dictionary { - ["r"] = 0.8f, - ["g"] = 0.8f, - ["b"] = 0.9f, - ["alpha"] = 0.6f, + ["color"] = new MixtapeEventTemplates.ColorField(new Color(0.8f, 0.8f, 0.9f, 0.6f)), ["height"] = 0.5f } }; @@ -46,10 +43,7 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader) { var log = ClassLogger.GetForClass(); var durationBeats = Mathf.Max(0.01f, entity.length); - var r = entity.GetFloat("r"); - var g = entity.GetFloat("g"); - var b = entity.GetFloat("b"); - var alpha = entity.GetFloat("alpha"); + var color = entity.GetColor("color"); var height = entity.GetFloat("height"); var startBeat = entity.beat; var endBeat = startBeat + durationBeats; @@ -61,17 +55,14 @@ public bool TrySchedule(Entity entity, MixtapeLoaderCustom loader) void SpawnAction() { EffectRuntimeController.Instance.SpawnRunner(runner => - runner.Initialize(loader, loader.jukebox, startBeat, endBeat, r, g, b, alpha, height)); + runner.Initialize(loader, loader.jukebox, startBeat, endBeat, color, height)); } } private sealed class FogRunner : MonoBehaviour { private bool _initialized; - private float _r; - private float _g; - private float _b; - private float _maxAlpha; + private Color _color; private float _height; private float _startBeat; private float _endBeat; @@ -82,16 +73,13 @@ private sealed class FogRunner : MonoBehaviour /// /// Initializes this runner with effect parameters. /// - public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float r, float g, float b, float alpha, float height) + public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, Color color, float height) { _loader = loader; _jukebox = jukebox; _startBeat = startBeat; _endBeat = endBeat; - _r = Mathf.Clamp01(r); - _g = Mathf.Clamp01(g); - _b = Mathf.Clamp01(b); - _maxAlpha = Mathf.Clamp01(alpha); + _color = new Color(Mathf.Clamp01(color.r), Mathf.Clamp01(color.g), Mathf.Clamp01(color.b), Mathf.Clamp01(color.a)); _height = Mathf.Clamp01(height); InitializeOverlay(); } @@ -138,7 +126,7 @@ private void LateUpdate() envelope = 1f; if (_overlay != null) - _overlay.SetParams(_r, _g, _b, _maxAlpha * envelope, _height); + _overlay.SetParams(new Color(_color.r, _color.g, _color.b, _color.a * envelope), _height); } private void OnDisable() @@ -153,7 +141,7 @@ private void InitializeOverlay() return; _overlay = camera.gameObject.AddComponent(); - _overlay.SetParams(_r, _g, _b, _maxAlpha, _height); + _overlay.SetParams(_color, _height); _initialized = true; } @@ -175,21 +163,15 @@ private void RemoveOverlay() private sealed class FogOverlay : MonoBehaviour { private static Material? _material; - private float _r; - private float _g; - private float _b; - private float _alpha; + private Color _color; private float _height; /// /// Updates the overlay parameters. /// - public void SetParams(float r, float g, float b, float alpha, float height) + public void SetParams(Color color, float height) { - _r = r; - _g = g; - _b = b; - _alpha = alpha; + _color = color; _height = height; } @@ -214,7 +196,7 @@ public void SetParams(float r, float g, float b, float alpha, float height) // Unity calls this on the camera's GameObject after it finishes rendering the scene. private void OnPostRender() { - if (_alpha <= 0f || _height <= 0f) + if (_color.a <= 0f || _height <= 0f) return; var mat = GetMaterial(); @@ -224,18 +206,17 @@ private void OnPostRender() mat.SetPass(0); // Ground fog: opaque at y=0 (bottom), fades to transparent at y=_height. - var fogColor = new Color(_r, _g, _b, _alpha); - var clear = new Color(_r, _g, _b, 0f); + var clear = new Color(_color.r, _color.g, _color.b, 0f); GL.PushMatrix(); GL.LoadOrtho(); GL.Begin(GL.QUADS); // Bottom-left → top-left → top-right → bottom-right - GL.Color(fogColor); GL.Vertex3(0f, 0f, 0f); + GL.Color(_color); GL.Vertex3(0f, 0f, 0f); GL.Color(clear); GL.Vertex3(0f, _height, 0f); GL.Color(clear); GL.Vertex3(1f, _height, 0f); - GL.Color(fogColor); GL.Vertex3(1f, 0f, 0f); + GL.Color(_color); GL.Vertex3(1f, 0f, 0f); GL.End(); GL.PopMatrix(); From 976b2f888b1ac597d2ad1b1439007b6adf881cee Mon Sep 17 00:00:00 2001 From: Brollyy Date: Sun, 1 Mar 2026 18:33:27 +0100 Subject: [PATCH 4/7] Revise README for effects with new previews Updated effect descriptions and added preview links for various effects in the README. --- docs/effects/README.md | 95 ++++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/docs/effects/README.md b/docs/effects/README.md index f3649ac..f9cb92a 100644 --- a/docs/effects/README.md +++ b/docs/effects/README.md @@ -17,7 +17,7 @@ Adds a temporary camera shake effect for impact, hits, drops, or strong rhythm a **Preview** -[![Watch the video](https://img.youtube.com/vi/9YBuEKdRlTk/maxresdefault.jpg)](https://youtu.be/9YBuEKdRlTk) +https://github.com/user-attachments/assets/7c0a6e5e-4b7b-45ae-9be7-bef0b399b7b1 ## Camera Tilt @@ -25,57 +25,71 @@ Adds a temporary camera shake effect for impact, hits, drops, or strong rhythm a **Config Key:** `CameraTilt.Enabled` -Briefly tilts the camera on its Z-axis with a smooth swing arc. Great for expressive rhythm accents and musical phrases in a cartoonish style. +Briefly tilts the camera on its Z-axis with a smooth swing arc. **Properties** - `angle`: Maximum tilt angle in degrees (positive = counter-clockwise). - `length` (event length in editor): How long the tilt lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/79927ec8-175f-4c43-ab05-3cdffa6b753f + ## Color Tint **DisplayName:** `Color Tint` **Config Key:** `ColorTint.Enabled` -Applies a sustained full-screen color tint that fades in, holds, then fades out. Perfect for horror (red), sepia (warm), or supernatural (purple) atmospheres. +Applies a sustained full-screen color tint that fades in, holds, then fades out. **Properties** -- `r`, `g`, `b`: Tint color (0–1 each; default `1, 0, 0` — red). -- `alpha`: Maximum opacity (0–1; default `0.25`). +- `color`: Tint color (default `#ff000040` — red with slight opacity). - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/434d016e-1cc6-4503-98f6-6affb074c270 + ## Fog **DisplayName:** `Fog` **Config Key:** `Fog.Enabled` -Draws a ground fog gradient overlay — opaque at the bottom of the screen, fading to transparent at the specified height. Works in both 2D and 3D scenes. Fades in and out smoothly. +Draws a ground fog gradient overlay - opaque at the bottom of the screen, fading to transparent at the specified height. Fades in and out smoothly. **Properties** -- `r`, `g`, `b`: Fog color (0–1 each; default `0.8, 0.8, 0.9` — pale blue-grey). -- `alpha`: Maximum opacity at the bottom of the screen (0–1; default `0.6`). +- `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`). - `length` (event length in editor): How long the fog lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/d6c128bb-7b45-4654-9265-a0499195218a + ## Horizontal Flip **DisplayName:** `Horizontal Flip` **Config Key:** `HorizontalFlip.Enabled` -Mirrors the screen left-to-right for the duration of the event. Composes correctly with Vertical Flip and zoom effects. +Mirrors the screen left-to-right for the duration of the event. Composes with Vertical Flip and zoom effects. **Properties** - `length` (event length in editor): How long the flip lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/a2a1ca7b-ff89-446f-a28d-326a0a86df50 + ## HSL Filter **DisplayName:** `HSL Filter` **Config Key:** `Hsl.Enabled` -Adjusts hue, saturation and lightness of the whole screen for creative colour grading using a per-pixel shader. Fades in and out smoothly. Requires the shader AssetBundle to be built from `BopVisualEffectsShaders/` — see that directory's `README.md` for build instructions. Falls back to GL blend operations (saturation/lightness only; no hue rotation) when the bundle is absent. +Adjusts hue, saturation and lightness of the whole screen for colour grading using a per-pixel shader. Fades in and out smoothly. **Properties** - `hue_shift`: Degrees to rotate the hue wheel (-180–180; default `0`). Requires shader bundle; ignored in GL fallback. @@ -84,6 +98,10 @@ Adjusts hue, saturation and lightness of the whole screen for creative colour gr - `intensity`: Overall blend strength (0–1; default `1.0`). - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/75390981-e2cb-4c9f-8796-28dbf8b0462e + ## Letterbox **DisplayName:** `Letterbox` @@ -96,6 +114,10 @@ Adds cinematic black bars at the top and bottom of the screen for a dramatic wid - `size`: Height of each bar as a normalized screen fraction (0–0.49; default `0.1`). - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/cdb0a74d-9a72-4efd-9d15-b83b5d19998b + ## Pixel Grid **DisplayName:** `Pixel Grid` @@ -108,13 +130,17 @@ Pixelates the screen by downsampling the camera's rendered output to a low-resol - `pixel_size`: Size of each pixel block in screen pixels (2–64; default `4`). Larger values produce a more pronounced 8-bit look. - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/ea81aebd-35ad-4002-82c5-fab4a258e7d2 + ## Scanlines **DisplayName:** `Scanlines` **Config Key:** `Scanlines.Enabled` -Draws horizontal CRT-style scan lines over the screen for a retro 8-bit aesthetic. Can optionally scroll up or down continuously. Fades in and out smoothly. +Draws horizontal CRT-style scan lines over the screen for a retro aesthetic. Can optionally scroll up or down continuously. Fades in and out smoothly. **Properties** - `alpha`: Darkness of each scan line (0–1; default `0.35`). @@ -122,13 +148,17 @@ Draws horizontal CRT-style scan lines over the screen for a retro 8-bit aestheti - `scroll_speed`: Speed at which lines scroll upward, in cells per second (default `0` = static; negative = scroll downward). - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/9cb4cacc-9497-4f44-94bc-e9b764c68e5e + ## Screen Noise **DisplayName:** `Screen Noise` **Config Key:** `ScreenNoise.Enabled` -Draws animated TV static noise specks over the screen for a glitchy atmosphere. The noise changes every frame. Each speck uses an independent local RNG so global gameplay randomness is unaffected. +Draws animated TV static noise specks over the screen for a glitchy atmosphere. **Properties** - `alpha`: Maximum opacity of the noise specks (0–1; default `0.5`). @@ -136,29 +166,41 @@ Draws animated TV static noise specks over the screen for a glitchy atmosphere. - `size`: Physical size of each speck in normalized screen coordinates (0.005–0.1; default `0.01`). - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/6852cad0-a432-4389-b98e-e251f6e7e212 + ## Sepia **DisplayName:** `Sepia` **Config Key:** `Sepia.Enabled` -Applies a warm vintage sepia-tone filter using a per-pixel shader (standard Adobe/Kodak sepia matrix). Fades in and out smoothly. Requires the shader AssetBundle to be built from `BopVisualEffectsShaders/` — see that directory's `README.md` for build instructions. Falls back to a three-pass GL approximation (desaturate → amber tint → sepia multiply) when the bundle is absent. +Applies a warm vintage sepia-tone filter using a per-pixel shader (standard Adobe/Kodak sepia matrix). Fades in and out smoothly. **Properties** - `intensity`: Strength of the sepia toning (0–1; default `0.8`). - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/ff1025b3-1f4b-44c8-982f-6691fe861277 + ## Vertical Flip **DisplayName:** `Vertical Flip` **Config Key:** `VerticalFlip.Enabled` -Mirrors the screen top-to-bottom for the duration of the event. Composes correctly with Horizontal Flip and zoom effects. +Mirrors the screen top-to-bottom for the duration of the event. Composes with Horizontal Flip and zoom effects. **Properties** - `length` (event length in editor): How long the flip lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/c155c567-04b0-4fa8-9e83-109681003a1d + ## Vignette **DisplayName:** `Vignette` @@ -172,39 +214,38 @@ Darkens the screen edges with a smooth radial/elliptical gradient using a 32-seg - `size`: How far the darkening extends inward as a fraction of screen half-height (0–0.5; default `0.1`). - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/b2031a79-2d93-46ab-b15b-765fe0f1bd79 + ## Zoom In **DisplayName:** `Zoom In` **Config Key:** `ZoomIn.Enabled` -Eases the camera smoothly into a zoomed-in view, holds at the target level, then eases back out. Great for building tension or drawing attention to a musical phrase. Composes correctly with Zoom Out, Zoom Pulse, and other concurrent Zoom In instances. +Eases the camera smoothly into a zoomed-in view, holds at the target level, then eases back out. Composes with other zoom events. **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]`. - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. +**Preview** + +https://github.com/user-attachments/assets/466ba43b-e218-432f-9023-095f3095e138 + ## Zoom Out **DisplayName:** `Zoom Out` **Config Key:** `ZoomOut.Enabled` -Eases the camera smoothly out to a wider view, holds, then eases back in. Great for revealing the scene or creating a sense of space. Composes correctly with Zoom In, Zoom Pulse, and other concurrent Zoom Out instances. +Eases the camera smoothly out to a wider view, holds, then eases back in. Composes with other zoom events. **Properties** - `intensity`: How far to zoom out, as a fraction of the camera's base size (e.g. `0.2` = 20% further out). - `length` (event length in editor): How long the effect lasts, in beats. This event is resizable in the timeline. -## Zoom Pulse - -**DisplayName:** `Zoom Pulse` - -**Config Key:** `ZoomPulse.Enabled` - -Rapidly zooms the camera in and then eases it back out, creating a punchy "push-in" accent. Works with both orthographic and perspective cameras. Composes correctly with Zoom In, Zoom Out, and other concurrent Zoom Pulse instances. - -**Properties** -- `intensity`: How much to zoom in, expressed as a fraction of the camera's base size (e.g. `0.15` = 15% zoom). -- `length` (event length in editor): How long the pulse lasts, in beats. This event is resizable in the timeline. +**Preview** +https://github.com/user-attachments/assets/bf89e240-a1f7-48b7-90f9-46fc471c3c6c From 8e186bbea1ecfa4d01ac244045256505f4ee2742 Mon Sep 17 00:00:00 2001 From: Brollyy Date: Sun, 1 Mar 2026 18:34:23 +0100 Subject: [PATCH 5/7] Removed unnecessary files --- .../Effects/ZoomPulse/ZoomPulseEffect.cs | 144 ------------------ docs/effects/camera-shake/.gitkeep | 0 docs/effects/camera-tilt/.gitkeep | 0 docs/effects/color-tint/.gitkeep | 0 docs/effects/fog/.gitkeep | 0 docs/effects/hsl-filter/.gitkeep | 0 docs/effects/letterbox/.gitkeep | 0 docs/effects/pixel-grid/.gitkeep | 0 docs/effects/scanlines/.gitkeep | 0 docs/effects/screen-noise/.gitkeep | 0 docs/effects/sepia/.gitkeep | 0 docs/effects/vignette/.gitkeep | 0 docs/effects/zoom-in/.gitkeep | 0 docs/effects/zoom-out/.gitkeep | 0 docs/effects/zoom-pulse/.gitkeep | 0 15 files changed, 144 deletions(-) delete mode 100644 BopVisualEffects/Effects/ZoomPulse/ZoomPulseEffect.cs delete mode 100644 docs/effects/camera-shake/.gitkeep delete mode 100644 docs/effects/camera-tilt/.gitkeep delete mode 100644 docs/effects/color-tint/.gitkeep delete mode 100644 docs/effects/fog/.gitkeep delete mode 100644 docs/effects/hsl-filter/.gitkeep delete mode 100644 docs/effects/letterbox/.gitkeep delete mode 100644 docs/effects/pixel-grid/.gitkeep delete mode 100644 docs/effects/scanlines/.gitkeep delete mode 100644 docs/effects/screen-noise/.gitkeep delete mode 100644 docs/effects/sepia/.gitkeep delete mode 100644 docs/effects/vignette/.gitkeep delete mode 100644 docs/effects/zoom-in/.gitkeep delete mode 100644 docs/effects/zoom-out/.gitkeep delete mode 100644 docs/effects/zoom-pulse/.gitkeep diff --git a/BopVisualEffects/Effects/ZoomPulse/ZoomPulseEffect.cs b/BopVisualEffects/Effects/ZoomPulse/ZoomPulseEffect.cs deleted file mode 100644 index 125cd7a..0000000 --- a/BopVisualEffects/Effects/ZoomPulse/ZoomPulseEffect.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System.Collections.Generic; -using BopVisualEffects.Core; -using BopVisualEffects.Effects.Zoom; -using UnityEngine; - -namespace BopVisualEffects.Effects.ZoomPulse; - -/// -/// Effect definition for a quick camera zoom-in/out pulse for beat accents or drops. -/// -public sealed class ZoomPulseEffect : IVisualEffectDefinition -{ - /// - public string Id => "zoom pulse"; - - /// - public string DisplayName => "Zoom Pulse"; - - /// - public string ConfigKey => "ZoomPulse"; - - /// - public string Description => "Briefly zooms the camera in and back out for a punchy, cartoonish accent."; - - /// - public MixtapeEventTemplate CreateTemplate(string pluginGuid) - { - return new MixtapeEventTemplate - { - dataModel = $"{pluginGuid}/{Id}", - length = 0.5f, - resizable = true, - properties = new Dictionary - { - ["intensity"] = 0.15f - } - }; - } - - /// - 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 startBeat = entity.beat; - var endBeat = startBeat + durationBeats; - - loader.scheduler.Schedule(startBeat, (System.Action?)SpawnAction); - log.Debug($"Scheduled '{DisplayName}' from beat {startBeat:0.###} to {endBeat:0.###}."); - return true; - - void SpawnAction() - { - EffectRuntimeController.Instance.SpawnRunner(runner => - runner.Initialize(loader, loader.jukebox, startBeat, endBeat, intensity)); - } - } - - private sealed class ZoomPulseRunner : MonoBehaviour - { - private bool _initialized; - private float _intensity; - private float _startBeat; - private float _endBeat; - private MixtapeLoaderCustom? _loader; - private JukeboxScript? _jukebox; - private Camera? _camera; - - /// - /// Initializes this runner with effect parameters. - /// - public void Initialize(MixtapeLoaderCustom loader, JukeboxScript? jukebox, float startBeat, float endBeat, float intensity) - { - _loader = loader; - _jukebox = jukebox; - _startBeat = startBeat; - _endBeat = endBeat; - _intensity = Mathf.Max(0f, intensity); - InitializeTargetCamera(); - } - - /// - /// Stops this effect instance. - /// - public void Stop() - { - if (_camera is not null) - CameraZoomService.RemoveFactor(_camera, this); - Destroy(this); - } - - private void LateUpdate() - { - if (_jukebox is null) - { - Stop(); - return; - } - - if (!_initialized) - { - InitializeTargetCamera(); - if (!_initialized) - return; - } - - var currentBeat = _jukebox.CurrentBeat; - if (currentBeat >= _endBeat) - { - Stop(); - return; - } - - // Zoom in quickly then ease back out (attack/decay envelope shaped like a triangle peaking at 30%). - var progress = Mathf.InverseLerp(_startBeat, _endBeat, currentBeat); - var envelope = progress < 0.3f - ? Mathf.InverseLerp(0f, 0.3f, progress) - : 1f - Mathf.InverseLerp(0.3f, 1f, progress); - - var zoomDelta = _intensity * envelope; - - // Clamp zoom factor so the camera size/FOV never reaches zero or negative values. - var zoomFactor = Mathf.Clamp(1f - zoomDelta, 0.01f, 1f); - CameraZoomService.SetFactor(_camera!, this, zoomFactor); - } - - private void OnDisable() - { - if (_camera is not null) - CameraZoomService.RemoveFactor(_camera, this); - } - - private void InitializeTargetCamera() - { - Camera? camera = EffectRuntimeController.ResolveEffectCamera(_loader); - if (camera is null) - return; - - _camera = camera; - _initialized = true; - } - } -} diff --git a/docs/effects/camera-shake/.gitkeep b/docs/effects/camera-shake/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/camera-tilt/.gitkeep b/docs/effects/camera-tilt/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/color-tint/.gitkeep b/docs/effects/color-tint/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/fog/.gitkeep b/docs/effects/fog/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/hsl-filter/.gitkeep b/docs/effects/hsl-filter/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/letterbox/.gitkeep b/docs/effects/letterbox/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/pixel-grid/.gitkeep b/docs/effects/pixel-grid/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/scanlines/.gitkeep b/docs/effects/scanlines/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/screen-noise/.gitkeep b/docs/effects/screen-noise/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/sepia/.gitkeep b/docs/effects/sepia/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/vignette/.gitkeep b/docs/effects/vignette/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/zoom-in/.gitkeep b/docs/effects/zoom-in/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/zoom-out/.gitkeep b/docs/effects/zoom-out/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/effects/zoom-pulse/.gitkeep b/docs/effects/zoom-pulse/.gitkeep deleted file mode 100644 index e69de29..0000000 From b56617c578ae6960b14f3c5e6332cdce612beae1 Mon Sep 17 00:00:00 2001 From: Brollyy Date: Sun, 1 Mar 2026 18:38:35 +0100 Subject: [PATCH 6/7] Removed referneces to deleted effect --- BopVisualEffects/Core/EffectDefinitionRegistry.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/BopVisualEffects/Core/EffectDefinitionRegistry.cs b/BopVisualEffects/Core/EffectDefinitionRegistry.cs index 05f60cf..92c05f4 100644 --- a/BopVisualEffects/Core/EffectDefinitionRegistry.cs +++ b/BopVisualEffects/Core/EffectDefinitionRegistry.cs @@ -17,7 +17,6 @@ using BopVisualEffects.Effects.Vignette; using BopVisualEffects.Effects.ZoomIn; using BopVisualEffects.Effects.ZoomOut; -using BopVisualEffects.Effects.ZoomPulse; namespace BopVisualEffects.Core; @@ -70,7 +69,6 @@ public static void Initialize(string pluginGuid, ClassLogger log, ConfigFile con registry.Register(new VignetteEffect()); registry.Register(new ZoomInEffect()); registry.Register(new ZoomOutEffect()); - registry.Register(new ZoomPulseEffect()); _instance = registry; } From c5c05264f1b60e430fda93aa889e6078e9220bf8 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 18:58:18 +0100 Subject: [PATCH 7/7] Update contribution guide: preview media now hosted on GitHub, not in-repo (#24) * Initial plan * Update README to reflect new preview video requirements (upload .mp4 to GitHub, no in-repo storage) Co-authored-by: Brollyy <12004018+Brollyy@users.noreply.github.com> * Add placeholder instruction for preview video in copilot-instructions.md Co-authored-by: Brollyy <12004018+Brollyy@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Brollyy <12004018+Brollyy@users.noreply.github.com> --- .github/copilot-instructions.md | 2 +- README.md | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 168720d..aa12037 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -56,7 +56,7 @@ Each effect follows this structure: 2. A dedicated `MonoBehaviour` runner (nested in the same file or a sibling file) spawned via `EffectRuntimeController.Instance.SpawnRunner(...)` inside `TrySchedule`. 3. The runner handles timing (start/end beat), per-frame updates in `LateUpdate`, and cleanup in `Stop`/`OnDisable`. 4. Registration via `EffectDefinitionRegistry.Initialize(...)` using `Register(new YourEffect())`. -5. Documentation media at `docs/effects//preview.gif` and `docs/effects//demo.mp4`, and an entry in `docs/effects/README.md`. +5. A short `.mp4` preview video (uploaded to GitHub, not stored in the repository) showing the event selected with its properties and the effect being applied in the editor, with the link included in `docs/effects/README.md` under the **Preview** section. Since agents cannot run the game to record this, include the following placeholder instead and leave it for the PR author to replace: ``. **Handling effect conflicts and concurrent instances:** - Before implementing a new effect, consider how it interacts with all existing effects when running simultaneously. diff --git a/README.md b/README.md index f7af0f1..fbe43f6 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,8 @@ If `BepInExPluginsDir` exists, build output is copied there automatically. 4. Add a dedicated runtime runner `MonoBehaviour` for the effect (in the same file or a sibling file). 5. Implement `TrySchedule` to read entity properties and spawn your runner through `EffectRuntimeController.Instance.SpawnRunner(...)`. 6. Register the effect once in `EffectDefinitionRegistry.Initialize(...)` using `Register(new YourEffect())`. -7. Add media for documentation: - - `docs/effects//preview.gif` - - `docs/effects//demo.mp4` -8. Update `docs/effects/README.md`. +7. Record a short `.mp4` preview video showing the event selected with its properties and the effect being applied in the editor. Upload it to GitHub (e.g. by attaching it to a PR comment or issue) and copy the resulting link. +8. Update `docs/effects/README.md` and include the uploaded video link under the **Preview** section for the new effect. ### Pull requests