mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 12:57:36 +08:00
Merge pull request #29500 from frenzibyte/fix-pausing-for-the-millionth-time
Fix oversight in osu! pause input handling
This commit is contained in:
commit
7c0550d251
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
@ -35,9 +36,11 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
OsuResumeOverlayInputBlocker? inputBlocker = null;
|
OsuResumeOverlayInputBlocker? inputBlocker = null;
|
||||||
|
|
||||||
if (drawableRuleset != null)
|
var drawableOsuRuleset = (DrawableOsuRuleset?)drawableRuleset;
|
||||||
|
|
||||||
|
if (drawableOsuRuleset != null)
|
||||||
{
|
{
|
||||||
var osuPlayfield = (OsuPlayfield)drawableRuleset.Playfield;
|
var osuPlayfield = drawableOsuRuleset.Playfield;
|
||||||
osuPlayfield.AttachResumeOverlayInputBlocker(inputBlocker = new OsuResumeOverlayInputBlocker());
|
osuPlayfield.AttachResumeOverlayInputBlocker(inputBlocker = new OsuResumeOverlayInputBlocker());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,13 +48,14 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
Child = clickToResumeCursor = new OsuClickToResumeCursor
|
Child = clickToResumeCursor = new OsuClickToResumeCursor
|
||||||
{
|
{
|
||||||
ResumeRequested = () =>
|
ResumeRequested = action =>
|
||||||
{
|
{
|
||||||
// since the user had to press a button to tap the resume cursor,
|
// since the user had to press a button to tap the resume cursor,
|
||||||
// block that press event from potentially reaching a hit circle that's behind the cursor.
|
// block that press event from potentially reaching a hit circle that's behind the cursor.
|
||||||
// we cannot do this from OsuClickToResumeCursor directly since we're in a different input manager tree than the gameplay one,
|
// we cannot do this from OsuClickToResumeCursor directly since we're in a different input manager tree than the gameplay one,
|
||||||
// so we rely on a dedicated input blocking component that's implanted in there to do that for us.
|
// so we rely on a dedicated input blocking component that's implanted in there to do that for us.
|
||||||
if (inputBlocker != null)
|
// note this only matters when the user didn't pause while they were holding the same key that they are resuming with.
|
||||||
|
if (inputBlocker != null && !drawableOsuRuleset.AsNonNull().KeyBindingInputManager.PressedActions.Contains(action))
|
||||||
inputBlocker.BlockNextPress = true;
|
inputBlocker.BlockNextPress = true;
|
||||||
|
|
||||||
Resume();
|
Resume();
|
||||||
@ -94,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
public override bool HandlePositionalInput => true;
|
public override bool HandlePositionalInput => true;
|
||||||
|
|
||||||
public Action? ResumeRequested;
|
public Action<OsuAction>? ResumeRequested;
|
||||||
private Container scaleTransitionContainer = null!;
|
private Container scaleTransitionContainer = null!;
|
||||||
|
|
||||||
public OsuClickToResumeCursor()
|
public OsuClickToResumeCursor()
|
||||||
@ -136,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
scaleTransitionContainer.ScaleTo(2, TRANSITION_TIME, Easing.OutQuint);
|
scaleTransitionContainer.ScaleTo(2, TRANSITION_TIME, Easing.OutQuint);
|
||||||
ResumeRequested?.Invoke();
|
ResumeRequested?.Invoke(e.Action);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
Position = OsuPlayfield.BASE_SIZE / 2,
|
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||||
StartTime = 5000,
|
StartTime = 5000,
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||||
|
StartTime = 10000,
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||||
|
StartTime = 15000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -256,7 +266,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestOsuRegisterInputFromPressingOrangeCursorButPressIsBlocked()
|
public void TestOsuHitCircleNotReceivingInputOnResume()
|
||||||
{
|
{
|
||||||
KeyCounter counter = null!;
|
KeyCounter counter = null!;
|
||||||
|
|
||||||
@ -281,6 +291,64 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddAssert("button is released in kbc", () => !Player.DrawableRuleset.Playfield.FindClosestParent<OsuInputManager>()!.PressedActions.Any());
|
AddAssert("button is released in kbc", () => !Player.DrawableRuleset.Playfield.FindClosestParent<OsuInputManager>()!.PressedActions.Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOsuHitCircleNotReceivingInputOnResume_PauseWhileHoldingSameKey()
|
||||||
|
{
|
||||||
|
KeyCounter counter = null!;
|
||||||
|
|
||||||
|
loadPlayer(() => new OsuRuleset());
|
||||||
|
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<OsuAction> actionTrigger && actionTrigger.Action == OsuAction.LeftButton));
|
||||||
|
|
||||||
|
AddStep("press Z", () => InputManager.PressKey(Key.Z));
|
||||||
|
AddAssert("circle hit", () => Player.ScoreProcessor.HighestCombo.Value, () => Is.EqualTo(1));
|
||||||
|
|
||||||
|
AddStep("pause", () => Player.Pause());
|
||||||
|
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
||||||
|
|
||||||
|
AddStep("resume", () => Player.Resume());
|
||||||
|
AddStep("go to resume cursor", () => InputManager.MoveMouseTo(this.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single()));
|
||||||
|
AddStep("press Z to resume", () => InputManager.PressKey(Key.Z));
|
||||||
|
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
||||||
|
|
||||||
|
checkKey(() => counter, 1, false);
|
||||||
|
|
||||||
|
seekTo(5000);
|
||||||
|
|
||||||
|
AddStep("press Z", () => InputManager.PressKey(Key.Z));
|
||||||
|
|
||||||
|
checkKey(() => counter, 2, true);
|
||||||
|
AddAssert("circle hit", () => Player.ScoreProcessor.HighestCombo.Value, () => Is.EqualTo(2));
|
||||||
|
|
||||||
|
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
||||||
|
checkKey(() => counter, 2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOsuHitCircleNotReceivingInputOnResume_PauseWhileHoldingOtherKey()
|
||||||
|
{
|
||||||
|
loadPlayer(() => new OsuRuleset());
|
||||||
|
|
||||||
|
AddStep("press X", () => InputManager.PressKey(Key.X));
|
||||||
|
AddAssert("circle hit", () => Player.ScoreProcessor.HighestCombo.Value, () => Is.EqualTo(1));
|
||||||
|
|
||||||
|
seekTo(5000);
|
||||||
|
|
||||||
|
AddStep("pause", () => Player.Pause());
|
||||||
|
AddStep("release X", () => InputManager.ReleaseKey(Key.X));
|
||||||
|
|
||||||
|
AddStep("resume", () => Player.Resume());
|
||||||
|
AddStep("go to resume cursor", () => InputManager.MoveMouseTo(this.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single()));
|
||||||
|
AddStep("press Z to resume", () => InputManager.PressKey(Key.Z));
|
||||||
|
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
||||||
|
|
||||||
|
AddAssert("circle not hit", () => Player.ScoreProcessor.HighestCombo.Value, () => Is.EqualTo(1));
|
||||||
|
|
||||||
|
AddStep("press X", () => InputManager.PressKey(Key.X));
|
||||||
|
AddStep("release X", () => InputManager.ReleaseKey(Key.X));
|
||||||
|
|
||||||
|
AddAssert("circle hit", () => Player.ScoreProcessor.HighestCombo.Value, () => Is.EqualTo(2));
|
||||||
|
}
|
||||||
|
|
||||||
private void loadPlayer(Func<Ruleset> createRuleset)
|
private void loadPlayer(Func<Ruleset> createRuleset)
|
||||||
{
|
{
|
||||||
AddStep("set ruleset", () => currentRuleset = createRuleset());
|
AddStep("set ruleset", () => currentRuleset = createRuleset());
|
||||||
@ -288,12 +356,17 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1);
|
AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1);
|
||||||
AddUntilStep("wait for hud", () => Player.HUDOverlay.ChildrenOfType<SkinnableContainer>().All(s => s.ComponentsLoaded));
|
AddUntilStep("wait for hud", () => Player.HUDOverlay.ChildrenOfType<SkinnableContainer>().All(s => s.ComponentsLoaded));
|
||||||
|
|
||||||
AddStep("seek to gameplay", () => Player.GameplayClockContainer.Seek(0));
|
seekTo(0);
|
||||||
AddUntilStep("wait for seek to finish", () => Player.DrawableRuleset.FrameStableClock.CurrentTime, () => Is.EqualTo(0).Within(500));
|
|
||||||
AddAssert("not in break", () => !Player.IsBreakTime.Value);
|
AddAssert("not in break", () => !Player.IsBreakTime.Value);
|
||||||
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.DrawableRuleset.Playfield));
|
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.DrawableRuleset.Playfield));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void seekTo(double time)
|
||||||
|
{
|
||||||
|
AddStep($"seek to {time}ms", () => Player.GameplayClockContainer.Seek(time));
|
||||||
|
AddUntilStep("wait for seek to finish", () => Player.DrawableRuleset.FrameStableClock.CurrentTime, () => Is.EqualTo(time).Within(500));
|
||||||
|
}
|
||||||
|
|
||||||
private void checkKey(Func<KeyCounter> counter, int count, bool active)
|
private void checkKey(Func<KeyCounter> counter, int count, bool active)
|
||||||
{
|
{
|
||||||
AddAssert($"key count = {count}", () => counter().CountPresses.Value, () => Is.EqualTo(count));
|
AddAssert($"key count = {count}", () => counter().CountPresses.Value, () => Is.EqualTo(count));
|
||||||
|
Loading…
Reference in New Issue
Block a user