diff --git a/osu.Game.Tests/Visual/Settings/TestSceneLatencyComparer.cs b/osu.Game.Tests/Visual/Settings/TestSceneLatencyComparer.cs index 4706ecb149..e4b9b21470 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneLatencyComparer.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneLatencyComparer.cs @@ -7,7 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; -using osu.Game.Screens; +using osu.Game.Screens.Utility; using osuTK.Input; namespace osu.Game.Tests.Visual.Settings @@ -28,11 +28,11 @@ namespace osu.Game.Tests.Visual.Settings for (int i = 0; i < 4; i++) { clickCorrectUntilResults(); - AddAssert("check at results", () => !latencyComparer.ChildrenOfType().Any()); + AddAssert("check at results", () => !latencyComparer.ChildrenOfType().Any()); AddStep("hit c to continue", () => InputManager.Key(Key.C)); } - AddAssert("check at results", () => !latencyComparer.ChildrenOfType().Any()); + AddAssert("check at results", () => !latencyComparer.ChildrenOfType().Any()); AddAssert("check no buttons", () => !latencyComparer.ChildrenOfType().Any()); } @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("click correct button until results", () => { var latencyArea = latencyComparer - .ChildrenOfType() + .ChildrenOfType() .SingleOrDefault(a => a.TargetFrameRate == 0); // reached results diff --git a/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs index 318cdbd6a7..80e5235449 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs @@ -9,6 +9,7 @@ using osu.Framework.Screens; using osu.Game.Localisation; using osu.Game.Screens; using osu.Game.Screens.Import; +using osu.Game.Screens.Utility; namespace osu.Game.Overlays.Settings.Sections.DebugSettings { diff --git a/osu.Game/Screens/Utility/ButtonWithKeyBind.cs b/osu.Game/Screens/Utility/ButtonWithKeyBind.cs new file mode 100644 index 0000000000..20e39ffa6a --- /dev/null +++ b/osu.Game/Screens/Utility/ButtonWithKeyBind.cs @@ -0,0 +1,53 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable +using osu.Framework.Allocation; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Settings; +using osuTK.Input; + +namespace osu.Game.Screens.Utility +{ + public class ButtonWithKeyBind : SettingsButton + { + private readonly Key key; + + public ButtonWithKeyBind(Key key) + { + this.key = key; + } + + public override LocalisableString Text + { + get => base.Text; + set => base.Text = $"{value} (Press {key.ToString().Replace("Number", string.Empty)})"; + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (!e.Repeat && e.Key == key) + { + TriggerClick(); + return true; + } + + return base.OnKeyDown(e); + } + + [Resolved] + private OverlayColourProvider overlayColourProvider { get; set; } = null!; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Height = 100; + SpriteText.Colour = overlayColourProvider.Background6; + SpriteText.Font = OsuFont.TorusAlternate.With(size: 34); + } + } +} diff --git a/osu.Game/Screens/Utility/LatencyArea.cs b/osu.Game/Screens/Utility/LatencyArea.cs new file mode 100644 index 0000000000..a82efa1e26 --- /dev/null +++ b/osu.Game/Screens/Utility/LatencyArea.cs @@ -0,0 +1,239 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.Input.Events; +using osu.Game.Overlays; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Screens.Utility +{ + public class LatencyArea : CompositeDrawable + { + [Resolved] + private OverlayColourProvider overlayColourProvider { get; set; } = null!; + + public Action? ReportUserBest { get; set; } + + private Drawable? background; + + private readonly Key key; + + public readonly int TargetFrameRate; + + public readonly BindableBool IsActiveArea = new BindableBool(); + + public LatencyArea(Key key, int targetFrameRate) + { + this.key = key; + TargetFrameRate = targetFrameRate; + + RelativeSizeAxes = Axes.Both; + Masking = true; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + InternalChildren = new[] + { + background = new Box + { + Colour = overlayColourProvider.Background6, + RelativeSizeAxes = Axes.Both, + }, + new ButtonWithKeyBind(key) + { + Text = "Feels better", + Y = 20, + Width = 0.8f, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Action = () => ReportUserBest?.Invoke(), + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new LatencyMovableBox(IsActiveArea) + { + RelativeSizeAxes = Axes.Both, + }, + new LatencyCursorContainer(IsActiveArea) + { + RelativeSizeAxes = Axes.Both, + }, + } + }, + }; + + IsActiveArea.BindValueChanged(active => + { + background.FadeColour(active.NewValue ? overlayColourProvider.Background4 : overlayColourProvider.Background6, 200, Easing.OutQuint); + }, true); + } + + protected override bool OnMouseMove(MouseMoveEvent e) + { + IsActiveArea.Value = true; + return base.OnMouseMove(e); + } + + private double lastFrameTime; + + public override bool UpdateSubTree() + { + double elapsed = Clock.CurrentTime - lastFrameTime; + if (TargetFrameRate > 0 && elapsed < 1000.0 / TargetFrameRate) + return false; + + lastFrameTime = Clock.CurrentTime; + + return base.UpdateSubTree(); + } + + public class LatencyMovableBox : CompositeDrawable + { + private Box box = null!; + private InputManager inputManager = null!; + + private readonly BindableBool isActive; + + [Resolved] + private OverlayColourProvider overlayColourProvider { get; set; } = null!; + + public LatencyMovableBox(BindableBool isActive) + { + this.isActive = isActive; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + + InternalChild = box = new Box + { + Size = new Vector2(40), + RelativePositionAxes = Axes.Both, + Position = new Vector2(0.5f), + Origin = Anchor.Centre, + Colour = overlayColourProvider.Colour1, + }; + } + + protected override bool OnHover(HoverEvent e) => false; + + private double? lastFrameTime; + + protected override void Update() + { + base.Update(); + + if (!isActive.Value) + { + lastFrameTime = null; + return; + } + + if (lastFrameTime != null) + { + float movementAmount = (float)(Clock.CurrentTime - lastFrameTime) / 400; + + var buttons = inputManager.CurrentState.Keyboard.Keys; + + box.Colour = buttons.HasAnyButtonPressed ? overlayColourProvider.Content1 : overlayColourProvider.Colour1; + + foreach (var key in buttons) + { + switch (key) + { + case Key.K: + case Key.Up: + box.Y = MathHelper.Clamp(box.Y - movementAmount, 0.1f, 0.9f); + break; + + case Key.J: + case Key.Down: + box.Y = MathHelper.Clamp(box.Y + movementAmount, 0.1f, 0.9f); + break; + + case Key.Z: + case Key.Left: + box.X = MathHelper.Clamp(box.X - movementAmount, 0.1f, 0.9f); + break; + + case Key.X: + case Key.Right: + box.X = MathHelper.Clamp(box.X + movementAmount, 0.1f, 0.9f); + break; + } + } + } + + lastFrameTime = Clock.CurrentTime; + } + } + + public class LatencyCursorContainer : CompositeDrawable + { + private Circle cursor = null!; + private InputManager inputManager = null!; + + private readonly BindableBool isActive; + + [Resolved] + private OverlayColourProvider overlayColourProvider { get; set; } = null!; + + public LatencyCursorContainer(BindableBool isActive) + { + this.isActive = isActive; + Masking = true; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + InternalChild = cursor = new Circle + { + Size = new Vector2(40), + Origin = Anchor.Centre, + Colour = overlayColourProvider.Colour2, + }; + + inputManager = GetContainingInputManager(); + } + + protected override bool OnHover(HoverEvent e) => false; + + protected override void Update() + { + cursor.Colour = inputManager.CurrentState.Mouse.IsPressed(MouseButton.Left) ? overlayColourProvider.Content1 : overlayColourProvider.Colour2; + + if (isActive.Value) + { + cursor.Position = ToLocalSpace(inputManager.CurrentState.Mouse.Position); + cursor.Alpha = 1; + } + else + { + cursor.Alpha = 0; + } + + base.Update(); + } + } + } +} diff --git a/osu.Game/Screens/LatencyComparerScreen.cs b/osu.Game/Screens/Utility/LatencyComparerScreen.cs similarity index 59% rename from osu.Game/Screens/LatencyComparerScreen.cs rename to osu.Game/Screens/Utility/LatencyComparerScreen.cs index 45e62646b1..1e859a2956 100644 --- a/osu.Game/Screens/LatencyComparerScreen.cs +++ b/osu.Game/Screens/Utility/LatencyComparerScreen.cs @@ -7,7 +7,6 @@ using System; using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -16,9 +15,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Platform.Windows; using osu.Framework.Screens; @@ -27,11 +24,10 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; -using osu.Game.Overlays.Settings; using osuTK; using osuTK.Input; -namespace osu.Game.Screens +namespace osu.Game.Screens.Utility { public class LatencyComparerScreen : OsuScreen { @@ -174,7 +170,6 @@ Do whatever you need to try and perceive the difference in latency, then choose protected override void LoadComplete() { base.LoadComplete(); - loadNextRound(); } @@ -192,55 +187,6 @@ Do whatever you need to try and perceive the difference in latency, then choose return base.OnKeyDown(e); } - private void recordResult(bool correct) - { - explanatoryText.FadeOut(500, Easing.OutQuint); - - if (correct) - correctCount++; - - if (round < targetRoundCount) - loadNextRound(); - else - { - showResults(); - } - } - - private void loadNextRound() - { - round++; - statusText.Text = $"Level {difficultyLevel}\nRound {round} of {targetRoundCount}"; - - mainArea.Clear(); - - int betterSide = RNG.Next(0, 2); - - mainArea.Add(new LatencyArea(Key.Number1, betterSide == 1 ? mapDifficultyToTargetFrameRate(difficultyLevel) : 0) - { - Width = 0.5f, - ReportBetter = () => recordResult(betterSide == 0), - IsActiveArea = { Value = true } - }); - - mainArea.Add(new LatencyArea(Key.Number2, betterSide == 0 ? mapDifficultyToTargetFrameRate(difficultyLevel) : 0) - { - Width = 0.5f, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - ReportBetter = () => recordResult(betterSide == 1) - }); - - foreach (var area in mainArea) - { - area.IsActiveArea.BindValueChanged(active => - { - if (active.NewValue) - mainArea.Children.First(a => a != area).IsActiveArea.Value = false; - }); - } - } - private void showResults() { mainArea.Clear(); @@ -340,7 +286,7 @@ Do whatever you need to try and perceive the difference in latency, then choose Padding = new MarginPadding(20), Children = new Drawable[] { - new Button(Key.Enter) + new ButtonWithKeyBind(Key.Enter) { Text = "Continue to next level", BackgroundColour = colours.Red2, @@ -350,7 +296,7 @@ Do whatever you need to try and perceive the difference in latency, then choose Enabled = { Value = string.IsNullOrEmpty(cannotIncreaseReason) }, TooltipText = cannotIncreaseReason }, - new Button(Key.D) + new ButtonWithKeyBind(Key.D) { Text = difficultyLevel == 1 ? "Retry" : "Return to last level", BackgroundColour = colours.Green, @@ -358,7 +304,7 @@ Do whatever you need to try and perceive the difference in latency, then choose Origin = Anchor.Centre, Action = () => changeDifficulty(Math.Max(difficultyLevel - 1, 1)), }, - new Button(Key.C) + new ButtonWithKeyBind(Key.C) { Text = $"Continue towards certification at this level ({certificationRemaining} more)", Anchor = Anchor.Centre, @@ -376,9 +322,9 @@ Do whatever you need to try and perceive the difference in latency, then choose }); } - private void changeDifficulty(int diff) + private void changeDifficulty(int difficulty) { - Debug.Assert(diff > 0); + Debug.Assert(difficulty > 0); resultsArea.Clear(); @@ -388,10 +334,61 @@ Do whatever you need to try and perceive the difference in latency, then choose lastPoll = 0; targetRoundCount = rounds_to_complete; - difficultyLevel = diff; + difficultyLevel = difficulty; + loadNextRound(); } + private void loadNextRound() + { + round++; + statusText.Text = $"Level {difficultyLevel}\nRound {round} of {targetRoundCount}"; + + mainArea.Clear(); + + int betterSide = RNG.Next(0, 2); + + mainArea.AddRange(new[] + { + new LatencyArea(Key.Number1, betterSide == 1 ? mapDifficultyToTargetFrameRate(difficultyLevel) : 0) + { + Width = 0.5f, + IsActiveArea = { Value = true }, + ReportUserBest = () => recordResult(betterSide == 0), + }, + new LatencyArea(Key.Number2, betterSide == 0 ? mapDifficultyToTargetFrameRate(difficultyLevel) : 0) + { + Width = 0.5f, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + ReportUserBest = () => recordResult(betterSide == 1) + } + }); + + foreach (var area in mainArea) + { + area.IsActiveArea.BindValueChanged(active => + { + if (active.NewValue) + mainArea.Children.First(a => a != area).IsActiveArea.Value = false; + }); + } + } + + private void recordResult(bool correct) + { + // Fading this out will improve the frame rate after the first round due to less text on screen. + explanatoryText.FadeOut(500, Easing.OutQuint); + + if (correct) + correctCount++; + + if (round < targetRoundCount) + loadNextRound(); + else + showResults(); + } + private static int mapDifficultyToTargetFrameRate(int difficulty) { switch (difficulty) @@ -427,265 +424,5 @@ Do whatever you need to try and perceive the difference in latency, then choose return 1000 + ((difficulty - 10) * 500); } } - - public class LatencyArea : CompositeDrawable - { - [Resolved] - private OverlayColourProvider overlayColourProvider { get; set; } = null!; - - public Action? ReportBetter { get; set; } - - private Drawable? background; - - private readonly Key key; - - public readonly int TargetFrameRate; - - public readonly BindableBool IsActiveArea = new BindableBool(); - - public LatencyArea(Key key, int targetFrameRate) - { - this.key = key; - TargetFrameRate = targetFrameRate; - - RelativeSizeAxes = Axes.Both; - Masking = true; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - InternalChildren = new[] - { - background = new Box - { - Colour = overlayColourProvider.Background6, - RelativeSizeAxes = Axes.Both, - }, - new Button(key) - { - Text = "Feels better", - Y = 20, - Width = 0.8f, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Action = () => ReportBetter?.Invoke(), - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new LatencyMovableBox(IsActiveArea) - { - RelativeSizeAxes = Axes.Both, - }, - new LatencyCursorContainer(IsActiveArea) - { - RelativeSizeAxes = Axes.Both, - }, - } - }, - }; - - IsActiveArea.BindValueChanged(active => - { - background.FadeColour(active.NewValue ? overlayColourProvider.Background4 : overlayColourProvider.Background6, 200, Easing.OutQuint); - }, true); - } - - protected override bool OnMouseMove(MouseMoveEvent e) - { - IsActiveArea.Value = true; - return base.OnMouseMove(e); - } - - private double lastFrameTime; - - public override bool UpdateSubTree() - { - double elapsed = Clock.CurrentTime - lastFrameTime; - if (TargetFrameRate > 0 && elapsed < 1000.0 / TargetFrameRate) - return false; - - lastFrameTime = Clock.CurrentTime; - - return base.UpdateSubTree(); - } - - public class LatencyMovableBox : CompositeDrawable - { - private Box box = null!; - private InputManager inputManager = null!; - - private readonly BindableBool isActive; - - [Resolved] - private OverlayColourProvider overlayColourProvider { get; set; } = null!; - - public LatencyMovableBox(BindableBool isActive) - { - this.isActive = isActive; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - inputManager = GetContainingInputManager(); - - InternalChild = box = new Box - { - Size = new Vector2(40), - RelativePositionAxes = Axes.Both, - Position = new Vector2(0.5f), - Origin = Anchor.Centre, - Colour = overlayColourProvider.Colour1, - }; - } - - protected override bool OnHover(HoverEvent e) => false; - - private double? lastFrameTime; - - protected override void Update() - { - base.Update(); - - if (!isActive.Value) - { - lastFrameTime = null; - return; - } - - if (lastFrameTime != null) - { - float movementAmount = (float)(Clock.CurrentTime - lastFrameTime) / 400; - - var buttons = inputManager.CurrentState.Keyboard.Keys; - - box.Colour = buttons.HasAnyButtonPressed ? overlayColourProvider.Content1 : overlayColourProvider.Colour1; - - foreach (var key in buttons) - { - switch (key) - { - case Key.K: - case Key.Up: - box.Y = MathHelper.Clamp(box.Y - movementAmount, 0.1f, 0.9f); - break; - - case Key.J: - case Key.Down: - box.Y = MathHelper.Clamp(box.Y + movementAmount, 0.1f, 0.9f); - break; - - case Key.Z: - case Key.Left: - box.X = MathHelper.Clamp(box.X - movementAmount, 0.1f, 0.9f); - break; - - case Key.X: - case Key.Right: - box.X = MathHelper.Clamp(box.X + movementAmount, 0.1f, 0.9f); - break; - } - } - } - - lastFrameTime = Clock.CurrentTime; - } - } - - public class LatencyCursorContainer : CompositeDrawable - { - private Circle cursor = null!; - private InputManager inputManager = null!; - - private readonly BindableBool isActive; - - [Resolved] - private OverlayColourProvider overlayColourProvider { get; set; } = null!; - - public LatencyCursorContainer(BindableBool isActive) - { - this.isActive = isActive; - Masking = true; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - InternalChild = cursor = new Circle - { - Size = new Vector2(40), - Origin = Anchor.Centre, - Colour = overlayColourProvider.Colour2, - }; - - inputManager = GetContainingInputManager(); - } - - protected override bool OnHover(HoverEvent e) => false; - - protected override void Update() - { - cursor.Colour = inputManager.CurrentState.Mouse.IsPressed(MouseButton.Left) ? overlayColourProvider.Content1 : overlayColourProvider.Colour2; - - if (isActive.Value) - { - cursor.Position = ToLocalSpace(inputManager.CurrentState.Mouse.Position); - cursor.Alpha = 1; - } - else - { - cursor.Alpha = 0; - } - - base.Update(); - } - } - } - - public class Button : SettingsButton - { - private readonly Key key; - - public Button(Key key) - { - this.key = key; - } - - public override LocalisableString Text - { - get => base.Text; - set => base.Text = $"{value} (Press {key.ToString().Replace("Number", string.Empty)})"; - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - if (!e.Repeat && e.Key == key) - { - TriggerClick(); - return true; - } - - return base.OnKeyDown(e); - } - - [Resolved] - private OverlayColourProvider overlayColourProvider { get; set; } = null!; - - protected override void LoadComplete() - { - base.LoadComplete(); - - Height = 100; - SpriteText.Colour = overlayColourProvider.Background6; - SpriteText.Font = OsuFont.TorusAlternate.With(size: 34); - } - } } }