osu icon indicating copy to clipboard operation
osu copied to clipboard

Add delayed resume for taiko/catch/mania

Open smoogipoo opened this issue 1 year ago • 1 comments

Resolves https://github.com/ppy/osu/issues/10202 Supersedes / closes https://github.com/ppy/osu/pull/26992

smoogipoo avatar Feb 07 '24 17:02 smoogipoo

The lack of any visual cue here is bothering me. It's almost like a second pause, I find myself struggling to anticipate the moment where gameplay resumes.

Maybe we could do something super simple like this even at least? (very rough)

diff --git a/osu.Game/Screens/Play/DelayedResumeOverlay.cs b/osu.Game/Screens/Play/DelayedResumeOverlay.cs
index ef39c8eb76..5dfbb681c9 100644
--- a/osu.Game/Screens/Play/DelayedResumeOverlay.cs
+++ b/osu.Game/Screens/Play/DelayedResumeOverlay.cs
@@ -1,6 +1,9 @@
 // Copyright (c) ppy Pty Ltd <[email protected]>. 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.Shapes;
 using osu.Framework.Localisation;
 using osu.Framework.Threading;
 
@@ -14,10 +17,24 @@ public partial class DelayedResumeOverlay : ResumeOverlay
         protected override LocalisableString Message => string.Empty;
 
         private ScheduledDelegate? scheduledResume;
+        private Box background = null!;
+
+        [BackgroundDependencyLoader]
+        private void load()
+        {
+            Add(background = new Box
+            {
+                RelativeSizeAxes = Axes.Both,
+                Colour = Colour4.Black,
+                Alpha = 0.75f,
+            });
+        }
 
         protected override void PopIn()
         {
-            base.PopIn();
+            this.FadeIn();
+            background.FadeIn()
+                      .Then().FadeOut(800, Easing.OutSine);
 
             scheduledResume?.Cancel();
             scheduledResume = Scheduler.AddDelayed(Resume, 800);
@@ -25,7 +42,8 @@ protected override void PopIn()
 
         protected override void PopOut()
         {
-            base.PopOut();
+            this.FadeOut();
+
             scheduledResume?.Cancel();
         }
     }

bdach avatar Feb 10 '24 09:02 bdach

I didn't really like the fading overlay because it doesn't work well with the classic taiko skin. I tried to add a countdown instead:

https://drive.google.com/file/d/1wAnOjFAirLGd7cn2jULz0A7sl0jD2q0q/view?usp=sharing

smoogipoo avatar Mar 04 '24 07:03 smoogipoo

This needs sound effects added of any kind.

Also I dunno, it still feels super out of place and awkward.

peppy avatar Mar 14 '24 14:03 peppy

Also I dunno, it still feels super out of place and awkward.

Is that in relation to the design or what? This originally didn't have any design which matches stable. I imagine a world in which every ruleset has a resume overlay that matches gameplay, but that increases the scope of this PR a thousand-fold.

It was intended as an early thing that could be agreed to without much discussion...

smoogipoo avatar Mar 15 '24 13:03 smoogipoo

I've added what I felt would make it good and got this:

https://github.com/ppy/osu/assets/22781491/416aa1ca-8883-4b54-bd99-79e02f79c943

Basically made the countdown appear specifically on the center of the screen, and added a dim to indicate that the game is still paused and there's a countdown.

patch
diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
index adc7bd97ff..2401b044c4 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
@@ -28,6 +28,22 @@ public partial class OsuResumeOverlay : ResumeOverlay
 
         protected override LocalisableString Message => "Click the orange cursor to resume";
 
+        private readonly Container content;
+
+        protected override Container<Drawable> Content => content;
+
+        public OsuResumeOverlay()
+        {
+            InternalChild = new OsuPlayfieldAdjustmentContainer
+            {
+                AlignWithStoryboard = true,
+                Child = content = new Container
+                {
+                    RelativeSizeAxes = Axes.Both,
+                },
+            };
+        }
+
         [BackgroundDependencyLoader]
         private void load()
         {
@@ -40,7 +56,7 @@ private void load()
         protected override void PopIn()
         {
             // Can't display if the cursor is outside the window.
-            if (GameplayCursor.LastFrameState == Visibility.Hidden || !Contains(GameplayCursor.ActiveCursor.ScreenSpaceDrawQuad.Centre))
+            if (GameplayCursor.LastFrameState == Visibility.Hidden || !Content.Contains(GameplayCursor.ActiveCursor.ScreenSpaceDrawQuad.Centre))
             {
                 Resume();
                 return;
@@ -49,7 +65,7 @@ protected override void PopIn()
             base.PopIn();
 
             GameplayCursor.ActiveCursor.Hide();
-            cursorScaleContainer.Position = ToLocalSpace(GameplayCursor.ActiveCursor.ScreenSpaceDrawQuad.Centre);
+            cursorScaleContainer.Position = GameplayCursor.ActiveCursor.Position;
             clickToResumeCursor.Appear();
 
             if (localCursorContainer == null)
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index a422761800..1f474af10f 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -207,8 +207,7 @@ private void load(CancellationToken? cancellationToken)
             if ((ResumeOverlay = CreateResumeOverlay()) != null)
             {
                 AddInternal(CreateInputManager()
-                    .WithChild(CreatePlayfieldAdjustmentContainer()
-                        .WithChild(ResumeOverlay)));
+                    .WithChild(ResumeOverlay));
             }
 
             applyRulesetMods(Mods, config);
diff --git a/osu.Game/Screens/Play/DelayedResumeOverlay.cs b/osu.Game/Screens/Play/DelayedResumeOverlay.cs
index fd1ce5d829..392c636cc2 100644
--- a/osu.Game/Screens/Play/DelayedResumeOverlay.cs
+++ b/osu.Game/Screens/Play/DelayedResumeOverlay.cs
@@ -3,6 +3,7 @@
 
 using System;
 using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Containers;
 using osu.Framework.Graphics.Shapes;
@@ -14,6 +15,8 @@
 using osu.Game.Graphics.Sprites;
 using osu.Game.Overlays;
 using osuTK;
+using osuTK.Graphics;
+using Box = osu.Framework.Graphics.Shapes.Box;
 
 namespace osu.Game.Screens.Play
 {
@@ -39,6 +42,7 @@ public partial class DelayedResumeOverlay : ResumeOverlay
         private double countdownStartTime;
         private bool countdownComplete;
 
+        private Box dim = null!;
         private Drawable outerContent = null!;
         private Container innerContent = null!;
 
@@ -56,6 +60,12 @@ public DelayedResumeOverlay()
         [BackgroundDependencyLoader]
         private void load()
         {
+            Add(dim = new Box
+            {
+                RelativeSizeAxes = Axes.Both,
+                Colour = Color4.Black.Opacity(0.75f),
+            });
+
             Add(outerContent = new Circle
             {
                 Anchor = Anchor.Centre,
@@ -109,6 +119,8 @@ protected override void PopIn()
         {
             this.FadeIn();
 
+            dim.FadeIn();
+
             // The transition effects.
             outerContent.FadeIn().ScaleTo(Vector2.Zero).Then().ScaleTo(Vector2.One, 200, Easing.OutQuint);
             innerContent.FadeIn().ScaleTo(Vector2.Zero).Then().ScaleTo(Vector2.One, 400, Easing.OutElasticHalf);
@@ -143,9 +155,13 @@ protected override void PopOut()
             {
                 countdownProgress.ScaleTo(2f, 300, Easing.OutQuint);
                 countdownProgress.FadeOut(300, Easing.OutQuint);
+                dim.FadeOut(300, Easing.OutQuint);
             }
             else
+            {
                 countdownProgress.FadeOut();
+                dim.FadeOut(GameplayMenuOverlay.TRANSITION_DURATION, Easing.InQuint);
+            }
 
             scheduledResume?.Cancel();
         }
diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
index da239d585e..e87c04363d 100644
--- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs
+++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Screens.Play
 {
     public abstract partial class GameplayMenuOverlay : OverlayContainer, IKeyBindingHandler<GlobalAction>
     {
-        protected const int TRANSITION_DURATION = 200;
+        public const int TRANSITION_DURATION = 200;
 
         private const int button_height = 70;
         private const float background_alpha = 0.75f;

frenzibyte avatar Mar 16 '24 04:03 frenzibyte

Dim is too harsh, the idea is to prepare for gameplay so you need to be able to see it.

peppy avatar Mar 16 '24 04:03 peppy

@peppy asked me to whip some sound design for this, so here's something I quickly put together:

https://github.com/ppy/osu/assets/272140/814952ea-5d68-425d-8ac4-88487bb608db

Feel free to adjust to taste or whatever. PR for sample: https://github.com/ppy/osu-resources/pull/314

diff
diff --git a/osu.Game/Screens/Play/DelayedResumeOverlay.cs b/osu.Game/Screens/Play/DelayedResumeOverlay.cs
index 8bb3ae8182..9de6f94d59 100644
--- a/osu.Game/Screens/Play/DelayedResumeOverlay.cs
+++ b/osu.Game/Screens/Play/DelayedResumeOverlay.cs
@@ -3,6 +3,8 @@
 
 using System;
 using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Containers;
 using osu.Framework.Graphics.Shapes;
@@ -47,6 +49,8 @@ public partial class DelayedResumeOverlay : ResumeOverlay
         private SpriteText countdownText = null!;
         private CircularProgress countdownProgress = null!;
 
+        private Sample sampleCountdown = null!;
+
         public DelayedResumeOverlay()
         {
             Anchor = Anchor.Centre;
@@ -54,7 +58,7 @@ public DelayedResumeOverlay()
         }
 
         [BackgroundDependencyLoader]
-        private void load()
+        private void load(AudioManager audio)
         {
             Add(outerContent = new Circle
             {
@@ -103,6 +107,8 @@ private void load()
                     }
                 }
             });
+
+            sampleCountdown = audio.Samples.Get(@"Gameplay/resume-countdown");
         }
 
         protected override void PopIn()
@@ -164,13 +170,20 @@ private void updateCountdown()
             countdownProgress.Progress = amountTimePassed;
             countdownProgress.InnerRadius = progress_stroke_width / progress_size / countdownProgress.Scale.X;
 
-            if (countdownCount != newCount && newCount > 0)
+            if (countdownCount != newCount)
             {
-                countdownText.Text = Math.Max(1, newCount).ToString();
-                countdownText.ScaleTo(0.25f).Then().ScaleTo(1, 200, Easing.OutQuint);
-                outerContent.Delay(25).Then().ScaleTo(1.05f, 100).Then().ScaleTo(1f, 200, Easing.Out);
+                if (newCount > 0)
+                {
+                    countdownText.Text = Math.Max(1, newCount).ToString();
+                    countdownText.ScaleTo(0.25f).Then().ScaleTo(1, 200, Easing.OutQuint);
+                    outerContent.Delay(25).Then().ScaleTo(1.05f, 100).Then().ScaleTo(1f, 200, Easing.Out);
+
+                    countdownBackground.FlashColour(colourProvider.Background3, 400, Easing.Out);
+                }
 
-                countdownBackground.FlashColour(colourProvider.Background3, 400, Easing.Out);
+                var chan = sampleCountdown.GetChannel();
+                chan.Frequency.Value = newCount == 0 ? 0.5f : 1;
+                chan.Play();
             }
 
             countdownCount = newCount;

nekodex avatar Mar 21 '24 02:03 nekodex