diff --git a/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs b/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs index 0d981014b8..396d2e9027 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs @@ -50,30 +50,17 @@ namespace osu.Game.Tests.Visual.Menus [Test] public void TestGameplay() { + KiaiGameplayFountains fountains = null!; + AddStep("make fountains", () => { Children = new[] { - new KiaiGameplayFountains.GameplayStarFountain - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - X = 75, - }, - new KiaiGameplayFountains.GameplayStarFountain - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - X = -75, - }, + fountains = new KiaiGameplayFountains(), }; }); - AddStep("activate fountains", () => - { - ((StarFountain)Children[0]).Shoot(1); - ((StarFountain)Children[1]).Shoot(-1); - }); + AddStep("activate fountains", () => fountains.Shoot()); } [Test] diff --git a/osu.Game/Screens/Menu/KiaiMenuFountains.cs b/osu.Game/Screens/Menu/KiaiMenuFountains.cs index b57012eaf7..6e0351f922 100644 --- a/osu.Game/Screens/Menu/KiaiMenuFountains.cs +++ b/osu.Game/Screens/Menu/KiaiMenuFountains.cs @@ -6,9 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Utils; -using osu.Game.Audio; using osu.Game.Graphics.Containers; -using osu.Game.Skinning; namespace osu.Game.Screens.Menu { @@ -20,7 +18,7 @@ namespace osu.Game.Screens.Menu [Resolved] private GameHost host { get; set; } = null!; - private SkinnableSound? sample; + private StarFountainSounds sounds = null!; [BackgroundDependencyLoader] private void load() @@ -41,7 +39,7 @@ namespace osu.Game.Screens.Menu Origin = Anchor.BottomRight, X = -250, }, - sample = new SkinnableSound(new SampleInfo("Gameplay/fountain-shoot")) + sounds = new StarFountainSounds() }; } @@ -83,9 +81,9 @@ namespace osu.Game.Screens.Menu break; } - // Don't play SFX when game is in background as it can be a bit noisy. + // Don't play SFX when game is in background, as it can be a bit noisy. if (host.IsActive.Value) - sample?.Play(); + sounds.Play(); } } } diff --git a/osu.Game/Screens/Menu/StarFountainSounds.cs b/osu.Game/Screens/Menu/StarFountainSounds.cs new file mode 100644 index 0000000000..842e718c48 --- /dev/null +++ b/osu.Game/Screens/Menu/StarFountainSounds.cs @@ -0,0 +1,71 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Threading; +using osu.Game.Audio; +using osu.Game.Skinning; + +namespace osu.Game.Screens.Menu +{ + public partial class StarFountainSounds : CompositeComponent + { + private const int shoot_retrigger_delay = 500; + private const int loop_fade_duration = 500; + + private double? lastPlayback; + + private SkinnableSound shootSample = null!; + private PausableSkinnableSound loopSample = null!; + + private ScheduledDelegate? loopFadeDelegate; + private ScheduledDelegate? loopStopDelegate; + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + shootSample = new SkinnableSound(new SampleInfo("Gameplay/fountain-shoot")), + loopSample = new PausableSkinnableSound(new SampleInfo("Gameplay/fountain-loop")) { Looping = true }, + }; + } + + public void Play() + { + loopFadeDelegate?.Cancel(); + loopStopDelegate?.Cancel(); + + try + { + // Only play 'shootSample' if enough time has passed since last `Play()` call. + if (lastPlayback == null || Time.Current - lastPlayback > shoot_retrigger_delay) + { + loopSample.Stop(); + shootSample.Play(); + return; + } + + // Only call `Play()` if `loopSample` is not already playing, to prevent restarting the sample each time. + if (!loopSample.RequestedPlaying) + { + this.TransformBindableTo(loopSample.Volume, 1); + loopSample.Play(); + } + + // Schedule a volume fadeout, followed by a `Stop()`. + loopFadeDelegate = Scheduler.AddDelayed(() => + { + this.TransformBindableTo(loopSample.Volume, 0, loop_fade_duration); + loopStopDelegate = Scheduler.AddDelayed(() => loopSample.Stop(), loop_fade_duration); + }, shoot_retrigger_delay); + } + finally + { + lastPlayback = Time.Current; + } + } + } +} diff --git a/osu.Game/Screens/Play/KiaiGameplayFountains.cs b/osu.Game/Screens/Play/KiaiGameplayFountains.cs index 017e66253f..826c60c6cf 100644 --- a/osu.Game/Screens/Play/KiaiGameplayFountains.cs +++ b/osu.Game/Screens/Play/KiaiGameplayFountains.cs @@ -6,11 +6,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Utils; -using osu.Game.Audio; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Screens.Menu; -using osu.Game.Skinning; namespace osu.Game.Screens.Play { @@ -21,7 +19,7 @@ namespace osu.Game.Screens.Play private Bindable kiaiStarFountains = null!; - private SkinnableSound? sample; + private StarFountainSounds sounds = null!; [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -44,7 +42,7 @@ namespace osu.Game.Screens.Play Origin = Anchor.BottomRight, X = -75, }, - sample = new SkinnableSound(new SampleInfo("Gameplay/fountain-shoot")) + sounds = new StarFountainSounds(), }; } @@ -72,7 +70,7 @@ namespace osu.Game.Screens.Play leftFountain.Shoot(1); rightFountain.Shoot(-1); - sample?.Play(); + sounds.Play(); } public partial class GameplayStarFountain : StarFountain diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5b5482b3c7..438864f873 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - +