Skip to content

Commit 171df3a

Browse files
harsizclaude
andcommitted
Add next-note indicator, beat-flash panels, and fix ruleset settings
Settings (Settings > Rulesets > osu!vis): - Fix: Ruleset.CreateSettings() is the correct override name — was incorrectly named CreateSettingsSubsection() before (no effect) - Two new toggles: "Next-note colour indicator" and "Beat-flash side panels" - All six settings now appear and persist correctly Next-note colour indicator (SoundVisPlayfield): - Coloured ring around the logo border that tracks the soonest upcoming unjudged note; colour changes smoothly (120 ms cross-fade) on action change - Fades in from 800 ms out and pulses brighter in the last 150 ms - Ring thickness grows as the note gets close (4 px → 9 px) - Controllable via ShowNextNoteIndicator setting; respects off state Beat-flash side panels (BeatFlashDisplay, new file): - BeatSyncedContainer with two 180 px additive panels (left + right edges) and a very subtle full-screen pulse layer - Flash intensity = kiai boost (0.35) + loudness boost (amplitude vs. 24-beat rolling average); clamped to 0.80 max - Hue drifts +0.055 per beat so each flash has a slightly different colour; saturation raised to 0.90 during kiai - Screen-pulse only fires during kiai or ratio > 1.35 (avoids distracting the player during calm sections) - Controllable via ShowBeatFlashes setting; skips if strength < 0.06 Misc: - SoundVisPlayfield now layered: BeatFlashDisplay (depth 2) → MusicVisualizerDisplay (depth 1) → nextNoteRing (depth 0.5) → logo → HO - All config bindings resolved via [Resolved(CanBeNull=true)] pattern Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent 34597fe commit 171df3a

6 files changed

Lines changed: 341 additions & 64 deletions

File tree

‎osu.Game.Rulesets.SoundVis/Configuration/SoundVisRulesetConfigManager.cs‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ public SoundVisRulesetConfigManager(SettingsStore? store, RulesetInfo ruleset, i
1313

1414
protected override void InitialiseDefaults()
1515
{
16-
SetDefault(SoundVisRulesetSetting.ShowColors, true);
17-
SetDefault(SoundVisRulesetSetting.ShowLightUp, true);
18-
SetDefault(SoundVisRulesetSetting.SpinSpeedMultiplier, 1.0, 0.5, 3.0, 0.05);
19-
SetDefault(SoundVisRulesetSetting.BarGlowIntensity, 0.5, 0.0, 1.0, 0.05);
16+
SetDefault(SoundVisRulesetSetting.ShowColors, true);
17+
SetDefault(SoundVisRulesetSetting.ShowLightUp, true);
18+
SetDefault(SoundVisRulesetSetting.SpinSpeedMultiplier, 1.0, 0.5, 3.0, 0.05);
19+
SetDefault(SoundVisRulesetSetting.BarGlowIntensity, 0.5, 0.0, 1.0, 0.05);
20+
SetDefault(SoundVisRulesetSetting.ShowNextNoteIndicator, true);
21+
SetDefault(SoundVisRulesetSetting.ShowBeatFlashes, true);
2022
}
2123
}
2224
}

‎osu.Game.Rulesets.SoundVis/SoundVisRuleset.cs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using osu.Game.Rulesets.Edit;
1313
using osu.Game.Rulesets.SoundVis.Edit;
1414
using osu.Game.Rulesets.SoundVis.Mods;
15+
using osu.Game.Overlays.Settings;
1516
using osu.Game.Rulesets.SoundVis.UI;
1617
using osu.Game.Rulesets.UI;
1718
using osu.Game.Skinning;
@@ -59,6 +60,10 @@ public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) =
5960
public override IRulesetConfigManager CreateConfig(SettingsStore? settings)
6061
=> new SoundVisRulesetConfigManager(settings, RulesetInfo);
6162

63+
// Wires up the settings panel under Settings > Rulesets > osu!vis
64+
public override RulesetSettingsSubsection CreateSettings()
65+
=> new SoundVisSettingsSubsection(this);
66+
6267
public override HitObjectComposer CreateHitObjectComposer() => new SoundVisHitObjectComposer(this);
6368

6469
public override Drawable CreateIcon() => new SoundVisIcon();

‎osu.Game.Rulesets.SoundVis/SoundVisRulesetSetting.cs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,11 @@ public enum SoundVisRulesetSetting
1313

1414
/// <summary>Opacity of the soft additive glow around each approach bar (0 – 1).</summary>
1515
BarGlowIntensity,
16+
17+
/// <summary>Show a coloured ring around the logo indicating which key to press next.</summary>
18+
ShowNextNoteIndicator,
19+
20+
/// <summary>Flash the screen edges on loud / kiai beats.</summary>
21+
ShowBeatFlashes,
1622
}
1723
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
using System;
2+
using osu.Framework.Allocation;
3+
using osu.Framework.Audio.Track;
4+
using osu.Framework.Bindables;
5+
using osu.Framework.Graphics;
6+
using osu.Framework.Graphics.Containers;
7+
using osu.Framework.Graphics.Shapes;
8+
using osu.Game.Beatmaps.ControlPoints;
9+
using osu.Game.Graphics.Containers;
10+
using osu.Game.Rulesets.SoundVis.Configuration;
11+
using osuTK;
12+
using osuTK.Graphics;
13+
14+
namespace osu.Game.Rulesets.SoundVis.UI
15+
{
16+
/// <summary>
17+
/// Glowing side panels and a subtle screen pulse that react to the beat.
18+
/// Flashes are brighter during kiai sections and when the music is louder than the recent average.
19+
/// </summary>
20+
public partial class BeatFlashDisplay : BeatSyncedContainer
21+
{
22+
// Rolling amplitude history for "louder-than-average" detection
23+
private const int HISTORY_LEN = 24;
24+
private readonly float[] ampHistory = new float[HISTORY_LEN];
25+
private int historyIdx;
26+
27+
// Slow hue drift — gives each beat a slightly different colour
28+
private float hue;
29+
30+
// Panel references
31+
private Box leftPanel = null!;
32+
private Box rightPanel = null!;
33+
private Box screenPulse = null!;
34+
35+
private readonly BindableBool showBeatFlashes = new BindableBool(true);
36+
37+
[Resolved(CanBeNull = true)]
38+
private SoundVisRulesetConfigManager? rulesetConfig { get; set; }
39+
40+
[BackgroundDependencyLoader]
41+
private void load()
42+
{
43+
RelativeSizeAxes = Axes.Both;
44+
45+
// ── Full-screen pulse (very subtle, sits behind everything) ───────────
46+
AddInternal(screenPulse = new Box
47+
{
48+
RelativeSizeAxes = Axes.Both,
49+
Colour = Color4.White,
50+
Blending = BlendingParameters.Additive,
51+
Alpha = 0,
52+
});
53+
54+
// ── Left side panel ───────────────────────────────────────────────────
55+
AddInternal(new Container
56+
{
57+
RelativeSizeAxes = Axes.Y,
58+
Width = 180,
59+
Anchor = Anchor.CentreLeft,
60+
Origin = Anchor.CentreLeft,
61+
Children = new Drawable[]
62+
{
63+
leftPanel = new Box
64+
{
65+
RelativeSizeAxes = Axes.Both,
66+
Blending = BlendingParameters.Additive,
67+
Alpha = 0,
68+
},
69+
},
70+
});
71+
72+
// ── Right side panel ──────────────────────────────────────────────────
73+
AddInternal(new Container
74+
{
75+
RelativeSizeAxes = Axes.Y,
76+
Width = 180,
77+
Anchor = Anchor.CentreRight,
78+
Origin = Anchor.CentreRight,
79+
Children = new Drawable[]
80+
{
81+
rightPanel = new Box
82+
{
83+
RelativeSizeAxes = Axes.Both,
84+
Blending = BlendingParameters.Additive,
85+
Alpha = 0,
86+
},
87+
},
88+
});
89+
}
90+
91+
protected override void LoadComplete()
92+
{
93+
base.LoadComplete();
94+
rulesetConfig?.BindWith(SoundVisRulesetSetting.ShowBeatFlashes, showBeatFlashes);
95+
}
96+
97+
protected override void OnNewBeat(
98+
int beatIndex,
99+
TimingControlPoint timingPoint,
100+
EffectControlPoint effectPoint,
101+
ChannelAmplitudes amplitudes)
102+
{
103+
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
104+
105+
if (!showBeatFlashes.Value) return;
106+
107+
// ── Amplitude history & ratio ─────────────────────────────────────────
108+
float amp = amplitudes.Maximum;
109+
ampHistory[historyIdx % HISTORY_LEN] = amp;
110+
historyIdx++;
111+
112+
float avg = 0;
113+
for (int i = 0; i < HISTORY_LEN; i++) avg += ampHistory[i];
114+
avg /= HISTORY_LEN;
115+
116+
// ratio > 1 → louder than recent average → stronger flash
117+
float ratio = avg > 0.005f ? amp / avg : 1f;
118+
119+
// ── Base strength: kiai + above-average loudness ───────────────────────
120+
// kiai alone → 0.35 base; additionally ramp up if ratio > 0.9
121+
float kiaiBoost = effectPoint.KiaiMode ? 0.35f : 0f;
122+
float loudnessBoost = Math.Clamp((ratio - 0.9f) * 1.1f, 0f, 0.55f);
123+
float strength = Math.Clamp(kiaiBoost + loudnessBoost, 0f, 0.80f);
124+
125+
if (strength < 0.06f) return; // skip very quiet non-kiai beats
126+
127+
// ── Colour: slow hue drift with higher saturation during kiai ─────────
128+
hue = (hue + 0.055f) % 1f;
129+
float saturation = effectPoint.KiaiMode ? 0.90f : 0.70f;
130+
var colour = Color4.FromHsv(new Vector4(hue, saturation, 1f, 1f));
131+
132+
double beatLen = timingPoint.BeatLength;
133+
134+
// Side panels: sharp hit, long tail
135+
leftPanel.Colour = colour;
136+
rightPanel.Colour = colour;
137+
138+
leftPanel
139+
.FadeTo(strength * 0.75f, 35, Easing.OutQuint)
140+
.Then()
141+
.FadeOut(beatLen * 0.85, Easing.OutQuint);
142+
143+
rightPanel
144+
.FadeTo(strength * 0.75f, 35, Easing.OutQuint)
145+
.Then()
146+
.FadeOut(beatLen * 0.85, Easing.OutQuint);
147+
148+
// Screen pulse: much softer, kiai-only or very loud beats
149+
if (effectPoint.KiaiMode || ratio > 1.35f)
150+
{
151+
float pulseStrength = effectPoint.KiaiMode ? strength * 0.10f : strength * 0.06f;
152+
screenPulse.Colour = colour;
153+
screenPulse
154+
.FadeTo(pulseStrength, 30, Easing.OutQuint)
155+
.Then()
156+
.FadeOut(beatLen * 0.6, Easing.OutQuint);
157+
}
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)