diff --git a/osu.Game/Screens/Utility/CircleGameplay.cs b/osu.Game/Screens/Utility/CircleGameplay.cs new file mode 100644 index 0000000000..0667a54ced --- /dev/null +++ b/osu.Game/Screens/Utility/CircleGameplay.cs @@ -0,0 +1,12 @@ +// 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.Graphics.Containers; + +namespace osu.Game.Screens.Utility +{ + public class CircleGameplay : CompositeDrawable + { + } +} diff --git a/osu.Game/Screens/Utility/LatencyArea.cs b/osu.Game/Screens/Utility/LatencyArea.cs index 2ef48bb571..5b48a10a49 100644 --- a/osu.Game/Screens/Utility/LatencyArea.cs +++ b/osu.Game/Screens/Utility/LatencyArea.cs @@ -9,10 +9,9 @@ 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 osu.Game.Screens.Utility.SampleComponents; using osuTK.Input; namespace osu.Game.Screens.Utility @@ -28,10 +27,14 @@ namespace osu.Game.Screens.Utility private readonly Key key; + private Container visualContent = null!; + public readonly int? TargetFrameRate; public readonly BindableBool IsActiveArea = new BindableBool(); + public readonly Bindable VisualMode = new Bindable(); + public LatencyArea(Key key, int? targetFrameRate) { this.key = key; @@ -61,20 +64,9 @@ namespace osu.Game.Screens.Utility Origin = Anchor.TopCentre, Action = () => ReportUserBest?.Invoke(), }, - new Container + visualContent = new Container { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new LatencyMovableBox(IsActiveArea) - { - RelativeSizeAxes = Axes.Both, - }, - new LatencyCursorContainer(IsActiveArea) - { - RelativeSizeAxes = Axes.Both, - }, - } }, }; @@ -82,6 +74,57 @@ namespace osu.Game.Screens.Utility { background.FadeColour(active.NewValue ? overlayColourProvider.Background4 : overlayColourProvider.Background6, 200, Easing.OutQuint); }, true); + + VisualMode.BindValueChanged(mode => + { + switch (mode.NewValue) + { + case LatencyVisualMode.Simple: + visualContent.Children = new Drawable[] + { + new LatencyMovableBox(IsActiveArea) + { + RelativeSizeAxes = Axes.Both, + }, + new LatencyCursorContainer(IsActiveArea) + { + RelativeSizeAxes = Axes.Both, + }, + }; + break; + + case LatencyVisualMode.CircleGameplay: + visualContent.Children = new Drawable[] + { + new CircleGameplay + { + RelativeSizeAxes = Axes.Both, + }, + new LatencyCursorContainer(IsActiveArea) + { + RelativeSizeAxes = Axes.Both, + }, + }; + break; + + case LatencyVisualMode.ScrollingGameplay: + visualContent.Children = new Drawable[] + { + new ScrollingGameplay + { + RelativeSizeAxes = Axes.Both, + }, + new LatencyCursorContainer(IsActiveArea) + { + RelativeSizeAxes = Axes.Both, + }, + }; + break; + + default: + throw new ArgumentOutOfRangeException(); + } + }, true); } protected override bool OnMouseMove(MouseMoveEvent e) @@ -102,140 +145,5 @@ namespace osu.Game.Screens.Utility 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; - box.Colour = overlayColourProvider.Colour1; - 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/Utility/LatencyCertifierScreen.cs b/osu.Game/Screens/Utility/LatencyCertifierScreen.cs index 0a9d98450f..00821ab773 100644 --- a/osu.Game/Screens/Utility/LatencyCertifierScreen.cs +++ b/osu.Game/Screens/Utility/LatencyCertifierScreen.cs @@ -7,6 +7,7 @@ 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; @@ -62,6 +63,8 @@ namespace osu.Game.Screens.Utility [Resolved] private FrameworkConfigManager config { get; set; } = null!; + private readonly Bindable visualMode = new Bindable(); + private const int rounds_to_complete = 5; private const int rounds_to_complete_certified = 20; @@ -124,8 +127,9 @@ namespace osu.Game.Screens.Utility RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Text = @"Welcome to the latency certifier! -Use the arrow keys, Z/X/J/K to move the square. +Use the arrow keys, Z/X/F/J to control the display. Use the Tab key to change focus. +Change display modes with Space. Do whatever you need to try and perceive the difference in latency, then choose your best side. ", }, @@ -182,6 +186,10 @@ Do whatever you need to try and perceive the difference in latency, then choose { switch (e.Key) { + case Key.Space: + visualMode.Value = (LatencyVisualMode)(((int)visualMode.Value + 1) % 3); + return true; + case Key.Tab: var firstArea = mainArea.FirstOrDefault(a => !a.IsActiveArea.Value); if (firstArea != null) @@ -301,7 +309,9 @@ Do whatever you need to try and perceive the difference in latency, then choose isCertifying = true; changeDifficulty(DifficultyLevel - 1); }, - TooltipText = isPass ? $"Chain {rounds_to_complete_certified} rounds to confirm your perception!" : "You've reached your limits. Go to the previous level to complete certification!", + TooltipText = isPass + ? $"Chain {rounds_to_complete_certified} rounds to confirm your perception!" + : "You've reached your limits. Go to the previous level to complete certification!", }); } } @@ -386,12 +396,14 @@ Do whatever you need to try and perceive the difference in latency, then choose new LatencyArea(Key.Number1, betterSide == 1 ? mapDifficultyToTargetFrameRate(DifficultyLevel) : (int?)null) { Width = 0.5f, + VisualMode = { BindTarget = visualMode }, IsActiveArea = { Value = true }, ReportUserBest = () => recordResult(betterSide == 0), }, new LatencyArea(Key.Number2, betterSide == 0 ? mapDifficultyToTargetFrameRate(DifficultyLevel) : (int?)null) { Width = 0.5f, + VisualMode = { BindTarget = visualMode }, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, ReportUserBest = () => recordResult(betterSide == 1) diff --git a/osu.Game/Screens/Utility/LatencyVisualMode.cs b/osu.Game/Screens/Utility/LatencyVisualMode.cs new file mode 100644 index 0000000000..55dab86f9e --- /dev/null +++ b/osu.Game/Screens/Utility/LatencyVisualMode.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable +namespace osu.Game.Screens.Utility +{ + public enum LatencyVisualMode + { + Simple, + CircleGameplay, + ScrollingGameplay, + } +} diff --git a/osu.Game/Screens/Utility/SampleComponents/LatencyCursorContainer.cs b/osu.Game/Screens/Utility/SampleComponents/LatencyCursorContainer.cs new file mode 100644 index 0000000000..753310c3b3 --- /dev/null +++ b/osu.Game/Screens/Utility/SampleComponents/LatencyCursorContainer.cs @@ -0,0 +1,67 @@ +// 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.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.SampleComponents +{ + 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/Utility/SampleComponents/LatencyMovableBox.cs b/osu.Game/Screens/Utility/SampleComponents/LatencyMovableBox.cs new file mode 100644 index 0000000000..418d45489f --- /dev/null +++ b/osu.Game/Screens/Utility/SampleComponents/LatencyMovableBox.cs @@ -0,0 +1,102 @@ +// 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.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.SampleComponents +{ + 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; + box.Colour = overlayColourProvider.Colour1; + 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.F: + case Key.Up: + box.Y = MathHelper.Clamp(box.Y - movementAmount, 0.1f, 0.9f); + break; + + case Key.K: + 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; + } + } +} diff --git a/osu.Game/Screens/Utility/ScrollingGameplay.cs b/osu.Game/Screens/Utility/ScrollingGameplay.cs new file mode 100644 index 0000000000..31920d27e7 --- /dev/null +++ b/osu.Game/Screens/Utility/ScrollingGameplay.cs @@ -0,0 +1,12 @@ +// 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.Graphics.Containers; + +namespace osu.Game.Screens.Utility +{ + public class ScrollingGameplay : CompositeDrawable + { + } +}