mirror of
https://github.com/ppy/osu.git
synced 2025-02-22 05:23:05 +08:00
Merge pull request #4529 from peppy/resume-cursor-2
Add osu! click-to-resume support
This commit is contained in:
commit
b59e86f12e
70
osu.Game.Rulesets.Osu.Tests/TestCaseResumeOverlay.cs
Normal file
70
osu.Game.Rulesets.Osu.Tests/TestCaseResumeOverlay.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
public class TestCaseResumeOverlay : ManualInputManagerTestCase
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(OsuResumeOverlay),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestCaseResumeOverlay()
|
||||||
|
{
|
||||||
|
ManualOsuInputManager osuInputManager;
|
||||||
|
CursorContainer cursor;
|
||||||
|
ResumeOverlay resume;
|
||||||
|
|
||||||
|
bool resumeFired = false;
|
||||||
|
|
||||||
|
Child = osuInputManager = new ManualOsuInputManager(new OsuRuleset().RulesetInfo)
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
cursor = new CursorContainer(),
|
||||||
|
resume = new OsuResumeOverlay
|
||||||
|
{
|
||||||
|
GameplayCursor = cursor
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
resume.ResumeAction = () => resumeFired = true;
|
||||||
|
|
||||||
|
AddStep("move mouse to center", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre));
|
||||||
|
AddStep("show", () => resume.Show());
|
||||||
|
|
||||||
|
AddStep("move mouse away", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.TopLeft));
|
||||||
|
AddStep("click", () => osuInputManager.GameClick());
|
||||||
|
AddAssert("not dismissed", () => !resumeFired && resume.State == Visibility.Visible);
|
||||||
|
|
||||||
|
AddStep("move mouse back", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre));
|
||||||
|
AddStep("click", () => osuInputManager.GameClick());
|
||||||
|
AddAssert("dismissed", () => resumeFired && resume.State == Visibility.Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManualOsuInputManager : OsuInputManager
|
||||||
|
{
|
||||||
|
public ManualOsuInputManager(RulesetInfo ruleset)
|
||||||
|
: base(ruleset)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GameClick()
|
||||||
|
{
|
||||||
|
KeyBindingContainer.TriggerPressed(OsuAction.LeftButton);
|
||||||
|
KeyBindingContainer.TriggerReleased(OsuAction.LeftButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
148
osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
Normal file
148
osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||||
|
{
|
||||||
|
public class OsuCursor : SkinReloadableDrawable
|
||||||
|
{
|
||||||
|
private bool cursorExpand;
|
||||||
|
|
||||||
|
private Bindable<double> cursorScale;
|
||||||
|
private Bindable<bool> autoCursorScale;
|
||||||
|
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||||
|
|
||||||
|
private Container expandTarget;
|
||||||
|
private Drawable scaleTarget;
|
||||||
|
|
||||||
|
public OsuCursor()
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
Size = new Vector2(28);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
|
{
|
||||||
|
cursorExpand = skin.GetValue<SkinConfiguration, bool>(s => s.CursorExpand ?? true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuConfigManager config, IBindable<WorkingBeatmap> beatmap)
|
||||||
|
{
|
||||||
|
InternalChild = expandTarget = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Child = scaleTarget = new SkinnableDrawable("cursor", _ => new CircularContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
BorderThickness = Size.X / 6,
|
||||||
|
BorderColour = Color4.White,
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Colour = Color4.Pink.Opacity(0.5f),
|
||||||
|
Radius = 5,
|
||||||
|
},
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
},
|
||||||
|
new CircularContainer
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
BorderThickness = Size.X / 3,
|
||||||
|
BorderColour = Color4.White.Opacity(0.5f),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new CircularContainer
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Scale = new Vector2(0.1f),
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.White,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, restrictSize: false)
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.beatmap.BindTo(beatmap);
|
||||||
|
this.beatmap.ValueChanged += _ => calculateScale();
|
||||||
|
|
||||||
|
cursorScale = config.GetBindable<double>(OsuSetting.GameplayCursorSize);
|
||||||
|
cursorScale.ValueChanged += _ => calculateScale();
|
||||||
|
|
||||||
|
autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize);
|
||||||
|
autoCursorScale.ValueChanged += _ => calculateScale();
|
||||||
|
|
||||||
|
calculateScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateScale()
|
||||||
|
{
|
||||||
|
float scale = (float)cursorScale.Value;
|
||||||
|
|
||||||
|
if (autoCursorScale.Value && beatmap.Value != null)
|
||||||
|
{
|
||||||
|
// if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
|
||||||
|
scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleTarget.Scale = new Vector2(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private const float pressed_scale = 1.2f;
|
||||||
|
private const float released_scale = 1f;
|
||||||
|
|
||||||
|
public void Expand()
|
||||||
|
{
|
||||||
|
if (!cursorExpand) return;
|
||||||
|
|
||||||
|
expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,9 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Skinning;
|
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||||
@ -88,136 +79,5 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
|
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
|
||||||
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
|
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OsuCursor : SkinReloadableDrawable
|
|
||||||
{
|
|
||||||
private bool cursorExpand;
|
|
||||||
|
|
||||||
private Bindable<double> cursorScale;
|
|
||||||
private Bindable<bool> autoCursorScale;
|
|
||||||
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
|
||||||
|
|
||||||
private Container expandTarget;
|
|
||||||
private Drawable scaleTarget;
|
|
||||||
|
|
||||||
public OsuCursor()
|
|
||||||
{
|
|
||||||
Origin = Anchor.Centre;
|
|
||||||
Size = new Vector2(28);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
|
||||||
{
|
|
||||||
cursorExpand = skin.GetValue<SkinConfiguration, bool>(s => s.CursorExpand ?? true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuConfigManager config, IBindable<WorkingBeatmap> beatmap)
|
|
||||||
{
|
|
||||||
InternalChild = expandTarget = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Child = scaleTarget = new SkinnableDrawable("cursor", _ => new CircularContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Masking = true,
|
|
||||||
BorderThickness = Size.X / 6,
|
|
||||||
BorderColour = Color4.White,
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Shadow,
|
|
||||||
Colour = Color4.Pink.Opacity(0.5f),
|
|
||||||
Radius = 5,
|
|
||||||
},
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0,
|
|
||||||
AlwaysPresent = true,
|
|
||||||
},
|
|
||||||
new CircularContainer
|
|
||||||
{
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Masking = true,
|
|
||||||
BorderThickness = Size.X / 3,
|
|
||||||
BorderColour = Color4.White.Opacity(0.5f),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0,
|
|
||||||
AlwaysPresent = true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new CircularContainer
|
|
||||||
{
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Scale = new Vector2(0.1f),
|
|
||||||
Masking = true,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4.White,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}, restrictSize: false)
|
|
||||||
{
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.beatmap.BindTo(beatmap);
|
|
||||||
this.beatmap.ValueChanged += _ => calculateScale();
|
|
||||||
|
|
||||||
cursorScale = config.GetBindable<double>(OsuSetting.GameplayCursorSize);
|
|
||||||
cursorScale.ValueChanged += _ => calculateScale();
|
|
||||||
|
|
||||||
autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize);
|
|
||||||
autoCursorScale.ValueChanged += _ => calculateScale();
|
|
||||||
|
|
||||||
calculateScale();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void calculateScale()
|
|
||||||
{
|
|
||||||
float scale = (float)cursorScale.Value;
|
|
||||||
|
|
||||||
if (autoCursorScale.Value && beatmap.Value != null)
|
|
||||||
{
|
|
||||||
// if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
|
|
||||||
scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
|
|
||||||
}
|
|
||||||
|
|
||||||
scaleTarget.Scale = new Vector2(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
private const float pressed_scale = 1.2f;
|
|
||||||
private const float released_scale = 1f;
|
|
||||||
|
|
||||||
public void Expand()
|
|
||||||
{
|
|
||||||
if (!cursorExpand) return;
|
|
||||||
|
|
||||||
expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ using osu.Game.Rulesets.Osu.Replays;
|
|||||||
using osu.Game.Rulesets.Osu.Scoring;
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.UI
|
namespace osu.Game.Rulesets.Osu.UI
|
||||||
{
|
{
|
||||||
@ -34,6 +35,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer();
|
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer();
|
||||||
|
|
||||||
|
protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay();
|
||||||
|
|
||||||
public override DrawableHitObject<OsuHitObject> CreateDrawableRepresentation(OsuHitObject h)
|
public override DrawableHitObject<OsuHitObject> CreateDrawableRepresentation(OsuHitObject h)
|
||||||
{
|
{
|
||||||
switch (h)
|
switch (h)
|
||||||
|
109
osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
Normal file
109
osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.UI
|
||||||
|
{
|
||||||
|
public class OsuResumeOverlay : ResumeOverlay
|
||||||
|
{
|
||||||
|
private OsuClickToResumeCursor clickToResumeCursor;
|
||||||
|
|
||||||
|
private GameplayCursorContainer localCursorContainer;
|
||||||
|
|
||||||
|
public override CursorContainer LocalCursor => State == Visibility.Visible ? localCursorContainer : null;
|
||||||
|
|
||||||
|
protected override string Message => "Click the orange cursor to resume";
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Add(clickToResumeCursor = new OsuClickToResumeCursor { ResumeRequested = Resume });
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Show()
|
||||||
|
{
|
||||||
|
base.Show();
|
||||||
|
clickToResumeCursor.ShowAt(GameplayCursor.ActiveCursor.Position);
|
||||||
|
|
||||||
|
if (localCursorContainer == null)
|
||||||
|
Add(localCursorContainer = new OsuCursorContainer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Hide()
|
||||||
|
{
|
||||||
|
localCursorContainer?.Expire();
|
||||||
|
localCursorContainer = null;
|
||||||
|
|
||||||
|
base.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e) => true;
|
||||||
|
|
||||||
|
public class OsuClickToResumeCursor : OsuCursor, IKeyBindingHandler<OsuAction>
|
||||||
|
{
|
||||||
|
public override bool HandlePositionalInput => true;
|
||||||
|
|
||||||
|
public Action ResumeRequested;
|
||||||
|
|
||||||
|
public OsuClickToResumeCursor()
|
||||||
|
{
|
||||||
|
RelativePositionAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
updateColour();
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
updateColour();
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(OsuAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case OsuAction.LeftButton:
|
||||||
|
case OsuAction.RightButton:
|
||||||
|
if (!IsHovered) return false;
|
||||||
|
|
||||||
|
this.ScaleTo(new Vector2(2), TRANSITION_TIME, Easing.OutQuint);
|
||||||
|
|
||||||
|
ResumeRequested?.Invoke();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(OsuAction action) => false;
|
||||||
|
|
||||||
|
public void ShowAt(Vector2 activeCursorPosition) => Schedule(() =>
|
||||||
|
{
|
||||||
|
updateColour();
|
||||||
|
this.MoveTo(activeCursorPosition);
|
||||||
|
this.ScaleTo(new Vector2(4)).Then().ScaleTo(Vector2.One, 1000, Easing.OutQuint);
|
||||||
|
});
|
||||||
|
|
||||||
|
private void updateColour()
|
||||||
|
{
|
||||||
|
this.FadeColour(IsHovered ? Color4.White : Color4.Orange, 400, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,19 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
@ -15,14 +21,52 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
protected new PausePlayer Player => (PausePlayer)base.Player;
|
protected new PausePlayer Player => (PausePlayer)base.Player;
|
||||||
|
|
||||||
|
private readonly Container content;
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
public TestCasePause()
|
public TestCasePause()
|
||||||
: base(new OsuRuleset())
|
: base(new OsuRuleset())
|
||||||
{
|
{
|
||||||
|
base.Content.Add(content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestPauseResume()
|
public void TestPauseResume()
|
||||||
{
|
{
|
||||||
|
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||||
|
pauseAndConfirm();
|
||||||
|
resumeAndConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResumeWithResumeOverlay()
|
||||||
|
{
|
||||||
|
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
|
||||||
|
AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1);
|
||||||
|
|
||||||
|
pauseAndConfirm();
|
||||||
|
resume();
|
||||||
|
|
||||||
|
confirmClockRunning(false);
|
||||||
|
confirmPauseOverlayShown(false);
|
||||||
|
|
||||||
|
AddStep("click to resume", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmClockRunning(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResumeWithResumeOverlaySkipped()
|
||||||
|
{
|
||||||
|
AddStep("move cursor to button", () =>
|
||||||
|
InputManager.MoveMouseTo(Player.HUDOverlay.HoldToQuit.Children.OfType<HoldToConfirmContainer>().First().ScreenSpaceDrawQuad.Centre));
|
||||||
|
AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1);
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
resumeAndConfirm();
|
resumeAndConfirm();
|
||||||
}
|
}
|
||||||
@ -30,6 +74,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestPauseTooSoon()
|
public void TestPauseTooSoon()
|
||||||
{
|
{
|
||||||
|
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
resumeAndConfirm();
|
resumeAndConfirm();
|
||||||
|
|
||||||
@ -144,9 +190,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
||||||
|
|
||||||
|
public new HUDOverlay HUDOverlay => base.HUDOverlay;
|
||||||
|
|
||||||
public bool FailOverlayVisible => FailOverlay.State == Visibility.Visible;
|
public bool FailOverlayVisible => FailOverlay.State == Visibility.Visible;
|
||||||
|
|
||||||
public bool PauseOverlayVisible => PauseOverlay.State == Visibility.Visible;
|
public bool PauseOverlayVisible => PauseOverlay.State == Visibility.Visible;
|
||||||
|
|
||||||
|
public PausePlayer()
|
||||||
|
{
|
||||||
|
PauseOnFocusLost = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,6 +150,13 @@ namespace osu.Game.Rulesets.UI
|
|||||||
Overlays = new Container { RelativeSizeAxes = Axes.Both }
|
Overlays = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if ((ResumeOverlay = CreateResumeOverlay()) != null)
|
||||||
|
{
|
||||||
|
AddInternal(CreateInputManager()
|
||||||
|
.WithChild(CreatePlayfieldAdjustmentContainer()
|
||||||
|
.WithChild(ResumeOverlay)));
|
||||||
|
}
|
||||||
|
|
||||||
applyRulesetMods(mods, config);
|
applyRulesetMods(mods, config);
|
||||||
|
|
||||||
loadObjects();
|
loadObjects();
|
||||||
@ -169,7 +176,21 @@ namespace osu.Game.Rulesets.UI
|
|||||||
mod.ApplyToDrawableHitObjects(Playfield.HitObjectContainer.Objects);
|
mod.ApplyToDrawableHitObjects(Playfield.HitObjectContainer.Objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void RequestResume(Action continueResume) => continueResume();
|
public override void RequestResume(Action continueResume)
|
||||||
|
{
|
||||||
|
if (ResumeOverlay != null && (Cursor == null || (Cursor.LastFrameState == Visibility.Visible && Contains(Cursor.ActiveCursor.ScreenSpaceDrawQuad.Centre))))
|
||||||
|
{
|
||||||
|
ResumeOverlay.GameplayCursor = Cursor;
|
||||||
|
ResumeOverlay.ResumeAction = continueResume;
|
||||||
|
ResumeOverlay.Show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
continueResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResumeOverlay ResumeOverlay { get; private set; }
|
||||||
|
|
||||||
|
protected virtual ResumeOverlay CreateResumeOverlay() => null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>.
|
/// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>.
|
||||||
|
@ -98,11 +98,6 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <returns>The cursor, or null if a cursor is not rqeuired.</returns>
|
/// <returns>The cursor, or null if a cursor is not rqeuired.</returns>
|
||||||
protected virtual GameplayCursorContainer CreateCursor() => null;
|
protected virtual GameplayCursorContainer CreateCursor() => null;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The target container to add the cursor after it is created.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual Container CursorTargetContainer => null;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a <see cref="Playfield"/> as a nested <see cref="Playfield"/>.
|
/// Registers a <see cref="Playfield"/> as a nested <see cref="Playfield"/>.
|
||||||
/// This does not add the <see cref="Playfield"/> to the draw hierarchy.
|
/// This does not add the <see cref="Playfield"/> to the draw hierarchy.
|
||||||
|
@ -43,6 +43,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public bool HasFailed { get; private set; }
|
public bool HasFailed { get; private set; }
|
||||||
|
|
||||||
|
public bool PauseOnFocusLost { get; set; } = true;
|
||||||
|
|
||||||
private Bindable<bool> mouseWheelDisabled;
|
private Bindable<bool> mouseWheelDisabled;
|
||||||
|
|
||||||
private readonly Bindable<bool> storyboardReplacesBackground = new Bindable<bool>();
|
private readonly Bindable<bool> storyboardReplacesBackground = new Bindable<bool>();
|
||||||
@ -382,7 +384,7 @@ namespace osu.Game.Screens.Play
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
// eagerly pause when we lose window focus (if we are locally playing).
|
// eagerly pause when we lose window focus (if we are locally playing).
|
||||||
if (!Game.IsActive.Value)
|
if (PauseOnFocusLost && !Game.IsActive.Value)
|
||||||
Pause();
|
Pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
74
osu.Game/Screens/Play/ResumeOverlay.cs
Normal file
74
osu.Game/Screens/Play/ResumeOverlay.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An overlay which can be used to require further user actions before gameplay is resumed.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ResumeOverlay : OverlayContainer
|
||||||
|
{
|
||||||
|
public CursorContainer GameplayCursor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The action to be performed to complete resuming.
|
||||||
|
/// </summary>
|
||||||
|
public Action ResumeAction { private get; set; }
|
||||||
|
|
||||||
|
public virtual CursorContainer LocalCursor => null;
|
||||||
|
|
||||||
|
protected const float TRANSITION_TIME = 500;
|
||||||
|
|
||||||
|
protected override bool BlockPositionalInput => false;
|
||||||
|
|
||||||
|
protected abstract string Message { get; }
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||||
|
|
||||||
|
protected ResumeOverlay()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Resume()
|
||||||
|
{
|
||||||
|
ResumeAction?.Invoke();
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
RelativePositionAxes = Axes.Both,
|
||||||
|
Y = 0.4f,
|
||||||
|
Text = Message,
|
||||||
|
Font = OsuFont.GetFont(size: 30),
|
||||||
|
Spacing = new Vector2(5, 0),
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Colour = colours.Yellow,
|
||||||
|
Shadow = true,
|
||||||
|
ShadowColour = new Color4(0, 0, 0, 0.25f)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn() => this.FadeIn(TRANSITION_TIME, Easing.OutQuint);
|
||||||
|
|
||||||
|
protected override void PopOut() => this.FadeOut(TRANSITION_TIME, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user