mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 20:22:55 +08:00
Merge pull request #9883 from swoolcock/confine-during-gameplay
Add "During Gameplay" option for mouse confining
This commit is contained in:
commit
8dddd8aff5
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
@ -23,33 +24,41 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestGameplayOverlayActivation()
|
public void TestGameplayOverlayActivation()
|
||||||
{
|
{
|
||||||
|
AddAssert("local user playing", () => Player.LocalUserPlaying.Value);
|
||||||
AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestGameplayOverlayActivationPaused()
|
public void TestGameplayOverlayActivationPaused()
|
||||||
{
|
{
|
||||||
|
AddAssert("local user playing", () => Player.LocalUserPlaying.Value);
|
||||||
AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
||||||
AddStep("pause gameplay", () => Player.Pause());
|
AddStep("pause gameplay", () => Player.Pause());
|
||||||
|
AddAssert("local user not playing", () => !Player.LocalUserPlaying.Value);
|
||||||
AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
|
AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestGameplayOverlayActivationReplayLoaded()
|
public void TestGameplayOverlayActivationReplayLoaded()
|
||||||
{
|
{
|
||||||
|
AddAssert("local user playing", () => Player.LocalUserPlaying.Value);
|
||||||
AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
||||||
AddStep("load a replay", () => Player.DrawableRuleset.HasReplayLoaded.Value = true);
|
AddStep("load a replay", () => Player.DrawableRuleset.HasReplayLoaded.Value = true);
|
||||||
|
AddAssert("local user not playing", () => !Player.LocalUserPlaying.Value);
|
||||||
AddAssert("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
|
AddAssert("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestGameplayOverlayActivationBreaks()
|
public void TestGameplayOverlayActivationBreaks()
|
||||||
{
|
{
|
||||||
|
AddAssert("local user playing", () => Player.LocalUserPlaying.Value);
|
||||||
AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
||||||
AddStep("seek to break", () => Player.GameplayClockContainer.Seek(Beatmap.Value.Beatmap.Breaks.First().StartTime));
|
AddStep("seek to break", () => Player.GameplayClockContainer.Seek(Beatmap.Value.Beatmap.Breaks.First().StartTime));
|
||||||
AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
|
AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
|
||||||
|
AddAssert("local user not playing", () => !Player.LocalUserPlaying.Value);
|
||||||
AddStep("seek to break end", () => Player.GameplayClockContainer.Seek(Beatmap.Value.Beatmap.Breaks.First().EndTime));
|
AddStep("seek to break end", () => Player.GameplayClockContainer.Seek(Beatmap.Value.Beatmap.Breaks.First().EndTime));
|
||||||
AddUntilStep("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
AddUntilStep("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
||||||
|
AddAssert("local user playing", () => Player.LocalUserPlaying.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OverlayTestPlayer();
|
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OverlayTestPlayer();
|
||||||
@ -57,6 +66,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
protected class OverlayTestPlayer : TestPlayer
|
protected class OverlayTestPlayer : TestPlayer
|
||||||
{
|
{
|
||||||
public new OverlayActivation OverlayActivationMode => base.OverlayActivationMode.Value;
|
public new OverlayActivation OverlayActivationMode => base.OverlayActivationMode.Value;
|
||||||
|
public new Bindable<bool> LocalUserPlaying => base.LocalUserPlaying;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Configuration.Tracking;
|
|||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Input;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
@ -69,6 +70,7 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
Set(OsuSetting.MouseDisableButtons, false);
|
Set(OsuSetting.MouseDisableButtons, false);
|
||||||
Set(OsuSetting.MouseDisableWheel, false);
|
Set(OsuSetting.MouseDisableWheel, false);
|
||||||
|
Set(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay);
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
Set(OsuSetting.ShowFpsDisplay, false);
|
Set(OsuSetting.ShowFpsDisplay, false);
|
||||||
@ -194,6 +196,7 @@ namespace osu.Game.Configuration
|
|||||||
FadePlayfieldWhenHealthLow,
|
FadePlayfieldWhenHealthLow,
|
||||||
MouseDisableButtons,
|
MouseDisableButtons,
|
||||||
MouseDisableWheel,
|
MouseDisableWheel,
|
||||||
|
ConfineMouseMode,
|
||||||
AudioOffset,
|
AudioOffset,
|
||||||
VolumeInactive,
|
VolumeInactive,
|
||||||
MenuMusic,
|
MenuMusic,
|
||||||
|
61
osu.Game/Input/ConfineMouseTracker.cs
Normal file
61
osu.Game/Input/ConfineMouseTracker.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// 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.Configuration;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
|
||||||
|
namespace osu.Game.Input
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Connects <see cref="OsuSetting.ConfineMouseMode"/> with <see cref="FrameworkSetting.ConfineMouseMode"/>.
|
||||||
|
/// If <see cref="OsuGame.LocalUserPlaying"/> is true, we should also confine the mouse cursor if it has been
|
||||||
|
/// requested with <see cref="OsuConfineMouseMode.DuringGameplay"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class ConfineMouseTracker : Component
|
||||||
|
{
|
||||||
|
private Bindable<ConfineMouseMode> frameworkConfineMode;
|
||||||
|
private Bindable<OsuConfineMouseMode> osuConfineMode;
|
||||||
|
private IBindable<bool> localUserPlaying;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuGame game, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager)
|
||||||
|
{
|
||||||
|
frameworkConfineMode = frameworkConfigManager.GetBindable<ConfineMouseMode>(FrameworkSetting.ConfineMouseMode);
|
||||||
|
osuConfineMode = osuConfigManager.GetBindable<OsuConfineMouseMode>(OsuSetting.ConfineMouseMode);
|
||||||
|
localUserPlaying = game.LocalUserPlaying.GetBoundCopy();
|
||||||
|
|
||||||
|
osuConfineMode.ValueChanged += _ => updateConfineMode();
|
||||||
|
localUserPlaying.BindValueChanged(_ => updateConfineMode(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateConfineMode()
|
||||||
|
{
|
||||||
|
// confine mode is unavailable on some platforms
|
||||||
|
if (frameworkConfineMode.Disabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (osuConfineMode.Value)
|
||||||
|
{
|
||||||
|
case OsuConfineMouseMode.Never:
|
||||||
|
frameworkConfineMode.Value = ConfineMouseMode.Never;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OsuConfineMouseMode.Fullscreen:
|
||||||
|
frameworkConfineMode.Value = ConfineMouseMode.Fullscreen;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OsuConfineMouseMode.DuringGameplay:
|
||||||
|
frameworkConfineMode.Value = localUserPlaying.Value ? ConfineMouseMode.Always : ConfineMouseMode.Never;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OsuConfineMouseMode.Always:
|
||||||
|
frameworkConfineMode.Value = ConfineMouseMode.Always;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
osu.Game/Input/OsuConfineMouseMode.cs
Normal file
37
osu.Game/Input/OsuConfineMouseMode.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// 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.ComponentModel;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Input
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines the situations in which the mouse cursor should be confined to the window.
|
||||||
|
/// Expands upon <see cref="ConfineMouseMode"/> by providing the option to confine during gameplay.
|
||||||
|
/// </summary>
|
||||||
|
public enum OsuConfineMouseMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The mouse cursor will be free to move outside the game window.
|
||||||
|
/// </summary>
|
||||||
|
Never,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The mouse cursor will be locked to the window bounds while in fullscreen mode.
|
||||||
|
/// </summary>
|
||||||
|
Fullscreen,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The mouse cursor will be locked to the window bounds during gameplay,
|
||||||
|
/// but may otherwise move freely.
|
||||||
|
/// </summary>
|
||||||
|
[Description("During Gameplay")]
|
||||||
|
DuringGameplay,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The mouse cursor will always be locked to the window bounds while the game has focus.
|
||||||
|
/// </summary>
|
||||||
|
Always
|
||||||
|
}
|
||||||
|
}
|
@ -95,6 +95,15 @@ namespace osu.Game
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly IBindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>();
|
public readonly IBindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the local user is currently interacting with the game in a way that should not be interrupted.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is exclusively managed by <see cref="Player"/>. If other components are mutating this state, a more
|
||||||
|
/// resilient method should be used to ensure correct state.
|
||||||
|
/// </remarks>
|
||||||
|
public Bindable<bool> LocalUserPlaying = new BindableBool();
|
||||||
|
|
||||||
protected OsuScreenStack ScreenStack;
|
protected OsuScreenStack ScreenStack;
|
||||||
|
|
||||||
protected BackButton BackButton;
|
protected BackButton BackButton;
|
||||||
@ -577,7 +586,8 @@ namespace osu.Game
|
|||||||
rightFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both },
|
rightFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both },
|
||||||
leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both },
|
leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both },
|
||||||
topMostOverlayContent = new Container { RelativeSizeAxes = Axes.Both },
|
topMostOverlayContent = new Container { RelativeSizeAxes = Axes.Both },
|
||||||
idleTracker
|
idleTracker,
|
||||||
|
new ConfineMouseTracker()
|
||||||
});
|
});
|
||||||
|
|
||||||
ScreenStack.ScreenPushed += screenPushed;
|
ScreenStack.ScreenPushed += screenPushed;
|
||||||
@ -947,6 +957,9 @@ namespace osu.Game
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset on screen change for sanity.
|
||||||
|
LocalUserPlaying.Value = false;
|
||||||
|
|
||||||
if (current is IOsuScreen currentOsuScreen)
|
if (current is IOsuScreen currentOsuScreen)
|
||||||
OverlayActivationMode.UnbindFrom(currentOsuScreen.OverlayActivationMode);
|
OverlayActivationMode.UnbindFrom(currentOsuScreen.OverlayActivationMode);
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Input;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings.Sections.Input
|
namespace osu.Game.Overlays.Settings.Sections.Input
|
||||||
{
|
{
|
||||||
@ -47,10 +47,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
|||||||
LabelText = "Map absolute input to window",
|
LabelText = "Map absolute input to window",
|
||||||
Current = config.GetBindable<bool>(FrameworkSetting.MapAbsoluteInputToWindow)
|
Current = config.GetBindable<bool>(FrameworkSetting.MapAbsoluteInputToWindow)
|
||||||
},
|
},
|
||||||
new SettingsEnumDropdown<ConfineMouseMode>
|
new SettingsEnumDropdown<OsuConfineMouseMode>
|
||||||
{
|
{
|
||||||
LabelText = "Confine mouse cursor to window",
|
LabelText = "Confine mouse cursor to window",
|
||||||
Current = config.GetBindable<ConfineMouseMode>(FrameworkSetting.ConfineMouseMode),
|
Current = osuConfig.GetBindable<OsuConfineMouseMode>(OsuSetting.ConfineMouseMode)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
|
@ -68,6 +68,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly Bindable<bool> storyboardReplacesBackground = new Bindable<bool>();
|
private readonly Bindable<bool> storyboardReplacesBackground = new Bindable<bool>();
|
||||||
|
|
||||||
|
protected readonly Bindable<bool> LocalUserPlaying = new Bindable<bool>();
|
||||||
|
|
||||||
public int RestartCount;
|
public int RestartCount;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
@ -155,8 +157,8 @@ namespace osu.Game.Screens.Play
|
|||||||
DrawableRuleset.SetRecordTarget(recordingReplay = new Replay());
|
DrawableRuleset.SetRecordTarget(recordingReplay = new Replay());
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(AudioManager audio, OsuConfigManager config)
|
private void load(AudioManager audio, OsuConfigManager config, OsuGame game)
|
||||||
{
|
{
|
||||||
Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray();
|
Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray();
|
||||||
|
|
||||||
@ -172,6 +174,9 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
||||||
|
|
||||||
|
if (game != null)
|
||||||
|
LocalUserPlaying.BindTo(game.LocalUserPlaying);
|
||||||
|
|
||||||
DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value);
|
DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value);
|
||||||
|
|
||||||
ScoreProcessor = ruleset.CreateScoreProcessor();
|
ScoreProcessor = ruleset.CreateScoreProcessor();
|
||||||
@ -219,9 +224,9 @@ namespace osu.Game.Screens.Play
|
|||||||
skipOverlay.Hide();
|
skipOverlay.Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawableRuleset.IsPaused.BindValueChanged(_ => updateOverlayActivationMode());
|
DrawableRuleset.IsPaused.BindValueChanged(_ => updateGameplayState());
|
||||||
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updateOverlayActivationMode());
|
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updateGameplayState());
|
||||||
breakTracker.IsBreakTime.BindValueChanged(_ => updateOverlayActivationMode());
|
breakTracker.IsBreakTime.BindValueChanged(_ => updateGameplayState());
|
||||||
|
|
||||||
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
||||||
|
|
||||||
@ -353,14 +358,11 @@ namespace osu.Game.Screens.Play
|
|||||||
HUDOverlay.KeyCounter.IsCounting = !isBreakTime.NewValue;
|
HUDOverlay.KeyCounter.IsCounting = !isBreakTime.NewValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateOverlayActivationMode()
|
private void updateGameplayState()
|
||||||
{
|
{
|
||||||
bool canTriggerOverlays = DrawableRuleset.IsPaused.Value || breakTracker.IsBreakTime.Value;
|
bool inGameplay = !DrawableRuleset.HasReplayLoaded.Value && !DrawableRuleset.IsPaused.Value && !breakTracker.IsBreakTime.Value;
|
||||||
|
OverlayActivationMode.Value = inGameplay ? OverlayActivation.Disabled : OverlayActivation.UserTriggered;
|
||||||
if (DrawableRuleset.HasReplayLoaded.Value || canTriggerOverlays)
|
LocalUserPlaying.Value = inGameplay;
|
||||||
OverlayActivationMode.Value = OverlayActivation.UserTriggered;
|
|
||||||
else
|
|
||||||
OverlayActivationMode.Value = OverlayActivation.Disabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePauseOnFocusLostState() =>
|
private void updatePauseOnFocusLostState() =>
|
||||||
@ -661,7 +663,7 @@ namespace osu.Game.Screens.Play
|
|||||||
foreach (var mod in Mods.Value.OfType<IApplicableToTrack>())
|
foreach (var mod in Mods.Value.OfType<IApplicableToTrack>())
|
||||||
mod.ApplyToTrack(musicController.CurrentTrack);
|
mod.ApplyToTrack(musicController.CurrentTrack);
|
||||||
|
|
||||||
updateOverlayActivationMode();
|
updateGameplayState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnSuspending(IScreen next)
|
public override void OnSuspending(IScreen next)
|
||||||
|
Loading…
Reference in New Issue
Block a user