From 95d15a703eb33b37baa10d90d56fdc0ce4df550f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 11:36:41 +0200 Subject: [PATCH 1/5] Improve legibility of scoring test scene - Add black background to avoid clashes with background images. - Use sorta-tetradic colours for the four plots. --- .../Visual/Gameplay/TestSceneScoring.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index c722d67ac9..4cbc046877 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -36,6 +37,9 @@ namespace osu.Game.Tests.Visual.Gameplay private FillFlowContainer legend = null!; + [Resolved] + private OsuColour colours { get; set; } = null!; + [Test] public void TestBasic() { @@ -43,6 +47,11 @@ namespace osu.Game.Tests.Visual.Gameplay { Children = new Drawable[] { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Black + }, new GridContainer { RelativeSizeAxes = Axes.Both, @@ -125,8 +134,8 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.Clear(); legend.Clear(); - runForProcessor("lazer-standardised", Color4.YellowGreen, new OsuScoreProcessor(), ScoringMode.Standardised); - runForProcessor("lazer-classic", Color4.MediumPurple, new OsuScoreProcessor(), ScoringMode.Classic); + runForProcessor("lazer-standardised", colours.Green1, new OsuScoreProcessor(), ScoringMode.Standardised); + runForProcessor("lazer-classic", colours.Blue1, new OsuScoreProcessor(), ScoringMode.Classic); runScoreV1(); runScoreV2(); @@ -156,7 +165,7 @@ namespace osu.Game.Tests.Visual.Gameplay currentCombo++; } - runForAlgorithm("ScoreV1 (classic)", Color4.Purple, + runForAlgorithm("ScoreV1 (classic)", colours.Purple1, () => applyHitV1(base_great), () => applyHitV1(base_ok), () => applyHitV1(0), @@ -199,7 +208,7 @@ namespace osu.Game.Tests.Visual.Gameplay currentHits++; } - runForAlgorithm("ScoreV2", Color4.OrangeRed, + runForAlgorithm("ScoreV2", colours.Red1, () => applyHitV2(base_great), () => applyHitV2(base_ok), () => From 3cf8082aa7b2877a7f533074ad697b4329cf391f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 12:07:12 +0200 Subject: [PATCH 2/5] Add capability to toggle visibility of graph --- .../Visual/Gameplay/TestSceneScoring.cs | 92 ++++++++++++++++--- 1 file changed, 77 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 4cbc046877..d7974b63e7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; 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.Cursor; @@ -75,7 +76,7 @@ namespace osu.Game.Tests.Visual.Gameplay legend = new FillFlowContainer { Padding = new MarginPadding(20), - Direction = FillDirection.Full, + Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }, @@ -263,7 +264,8 @@ namespace osu.Game.Tests.Visual.Gameplay results.Add(getTotalScore()); } - graphs.Add(new LineGraph + LineGraph graph; + graphs.Add(graph = new LineGraph { Name = name, RelativeSizeAxes = Axes.Both, @@ -271,20 +273,9 @@ namespace osu.Game.Tests.Visual.Gameplay Values = results }); - legend.Add(new OsuSpriteText + legend.Add(new LegendEntry(name, getTotalScore(), graph) { - Colour = colour, - RelativeSizeAxes = Axes.X, - Width = 0.5f, - Text = $"{FontAwesome.Solid.Circle.Icon} {name}" - }); - - legend.Add(new OsuSpriteText - { - Colour = colour, - RelativeSizeAxes = Axes.X, - Width = 0.5f, - Text = $"final score {getTotalScore():#,0}" + AccentColour = colour }); } } @@ -505,4 +496,75 @@ namespace osu.Game.Tests.Visual.Gameplay public void Move(Vector2 pos) => this.MoveTo(pos); } } + + public partial class LegendEntry : OsuClickableContainer, IHasAccentColour + { + public Color4 AccentColour { get; set; } + + public BindableBool Visible { get; } = new BindableBool(true); + + private readonly string description; + private readonly long finalScore; + private readonly LineGraph lineGraph; + + private OsuSpriteText descriptionText = null!; + private OsuSpriteText finalScoreText = null!; + + public LegendEntry(string description, long finalScore, LineGraph lineGraph) + { + this.description = description; + this.finalScore = finalScore; + this.lineGraph = lineGraph; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Content.RelativeSizeAxes = Axes.X; + AutoSizeAxes = Content.AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + descriptionText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + finalScoreText = new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Font = OsuFont.Default.With(fixedWidth: true) + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Visible.BindValueChanged(_ => updateState(), true); + Action = Visible.Toggle; + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateState(); + base.OnHoverLost(e); + } + + private void updateState() + { + Colour = IsHovered ? AccentColour.Lighten(0.2f) : AccentColour; + + descriptionText.Text = $"{(Visible.Value ? FontAwesome.Solid.CheckCircle.Icon : FontAwesome.Solid.Circle.Icon)} {description}"; + finalScoreText.Text = finalScore.ToString("#,0"); + lineGraph.Alpha = Visible.Value ? 1 : 0; + } + } } From 607ceeccb98e8cfd269bdf17156cbf2432eaab98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 12:19:50 +0200 Subject: [PATCH 3/5] Persist visibility state of graphs --- .../Visual/Gameplay/TestSceneScoring.cs | 99 +++++++++++++------ 1 file changed, 67 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index d7974b63e7..0162d57cd5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -38,6 +38,11 @@ namespace osu.Game.Tests.Visual.Gameplay private FillFlowContainer legend = null!; + private readonly BindableBool standardisedVisible = new BindableBool(true); + private readonly BindableBool classicVisible = new BindableBool(true); + private readonly BindableBool scoreV1Visible = new BindableBool(true); + private readonly BindableBool scoreV2Visible = new BindableBool(true); + [Resolved] private OsuColour colours { get; set; } = null!; @@ -135,8 +140,8 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.Clear(); legend.Clear(); - runForProcessor("lazer-standardised", colours.Green1, new OsuScoreProcessor(), ScoringMode.Standardised); - runForProcessor("lazer-classic", colours.Blue1, new OsuScoreProcessor(), ScoringMode.Classic); + runForProcessor("lazer-standardised", colours.Green1, new OsuScoreProcessor(), ScoringMode.Standardised, standardisedVisible); + runForProcessor("lazer-classic", colours.Blue1, new OsuScoreProcessor(), ScoringMode.Classic, classicVisible); runScoreV1(); runScoreV2(); @@ -166,17 +171,22 @@ namespace osu.Game.Tests.Visual.Gameplay currentCombo++; } - runForAlgorithm("ScoreV1 (classic)", colours.Purple1, - () => applyHitV1(base_great), - () => applyHitV1(base_ok), - () => applyHitV1(0), - () => + runForAlgorithm(new ScoringAlgorithm + { + Name = "ScoreV1 (classic)", + Colour = colours.Purple1, + ApplyHit = () => applyHitV1(base_great), + ApplyNonPerfect = () => applyHitV1(base_ok), + ApplyMiss = () => applyHitV1(0), + GetTotalScore = () => { // Arbitrary value chosen towards the upper range. const double score_multiplier = 4; return (int)(totalScore * score_multiplier); - }); + }, + Visible = scoreV1Visible + }); } private void runScoreV2() @@ -209,15 +219,19 @@ namespace osu.Game.Tests.Visual.Gameplay currentHits++; } - runForAlgorithm("ScoreV2", colours.Red1, - () => applyHitV2(base_great), - () => applyHitV2(base_ok), - () => + runForAlgorithm(new ScoringAlgorithm + { + Name = "ScoreV2", + Colour = colours.Red1, + ApplyHit = () => applyHitV2(base_great), + ApplyNonPerfect = () => applyHitV2(base_ok), + ApplyMiss = () => { currentHits++; maxBaseScore += base_great; currentCombo = 0; - }, () => + }, + GetTotalScore = () => { double accuracy = currentBaseScore / maxBaseScore; @@ -226,10 +240,12 @@ namespace osu.Game.Tests.Visual.Gameplay 700000 * comboPortion / comboPortionMax + 300000 * Math.Pow(accuracy, 10) * ((double)currentHits / maxCombo) ); - }); + }, + Visible = scoreV2Visible + }); } - private void runForProcessor(string name, Color4 colour, ScoreProcessor processor, ScoringMode mode) + private void runForProcessor(string name, Color4 colour, ScoreProcessor processor, ScoringMode mode, BindableBool visibility) { int maxCombo = sliderMaxCombo.Current.Value; @@ -239,14 +255,19 @@ namespace osu.Game.Tests.Visual.Gameplay processor.ApplyBeatmap(beatmap); - runForAlgorithm(name, colour, - () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Great }), - () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Ok }), - () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Miss }), - () => processor.GetDisplayScore(mode)); + runForAlgorithm(new ScoringAlgorithm + { + Name = name, + Colour = colour, + ApplyHit = () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Great }), + ApplyNonPerfect = () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Ok }), + ApplyMiss = () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Miss }), + GetTotalScore = () => processor.GetDisplayScore(mode), + Visible = visibility + }); } - private void runForAlgorithm(string name, Color4 colour, Action applyHit, Action applyNonPerfect, Action applyMiss, Func getTotalScore) + private void runForAlgorithm(ScoringAlgorithm scoringAlgorithm) { int maxCombo = sliderMaxCombo.Current.Value; @@ -255,31 +276,42 @@ namespace osu.Game.Tests.Visual.Gameplay for (int i = 0; i < maxCombo; i++) { if (graphs.MissLocations.Contains(i)) - applyMiss(); + scoringAlgorithm.ApplyMiss(); else if (graphs.NonPerfectLocations.Contains(i)) - applyNonPerfect(); + scoringAlgorithm.ApplyNonPerfect(); else - applyHit(); + scoringAlgorithm.ApplyHit(); - results.Add(getTotalScore()); + results.Add(scoringAlgorithm.GetTotalScore()); } LineGraph graph; graphs.Add(graph = new LineGraph { - Name = name, + Name = scoringAlgorithm.Name, RelativeSizeAxes = Axes.Both, - LineColour = colour, + LineColour = scoringAlgorithm.Colour, Values = results }); - legend.Add(new LegendEntry(name, getTotalScore(), graph) + legend.Add(new LegendEntry(scoringAlgorithm, graph) { - AccentColour = colour + AccentColour = scoringAlgorithm.Colour, }); } } + public class ScoringAlgorithm + { + public string Name { get; init; } = null!; + public Color4 Colour { get; init; } + public Action ApplyHit { get; init; } = () => { }; + public Action ApplyNonPerfect { get; init; } = () => { }; + public Action ApplyMiss { get; init; } = () => { }; + public Func GetTotalScore { get; init; } = null!; + public BindableBool Visible { get; init; } = null!; + } + public partial class GraphContainer : Container, IHasCustomTooltip> { public readonly BindableList MissLocations = new BindableList(); @@ -510,10 +542,13 @@ namespace osu.Game.Tests.Visual.Gameplay private OsuSpriteText descriptionText = null!; private OsuSpriteText finalScoreText = null!; - public LegendEntry(string description, long finalScore, LineGraph lineGraph) + public LegendEntry(ScoringAlgorithm scoringAlgorithm, LineGraph lineGraph) { - this.description = description; - this.finalScore = finalScore; + description = scoringAlgorithm.Name; + finalScore = scoringAlgorithm.GetTotalScore(); + AccentColour = scoringAlgorithm.Colour; + Visible.BindTo(scoringAlgorithm.Visible); + this.lineGraph = lineGraph; } From 15708ee46546562ffea09cd21e1af76491df17ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 13:27:40 +0200 Subject: [PATCH 4/5] Add toggle for showing relative/absolute score values --- .../Visual/Gameplay/TestSceneScoring.cs | 59 +++++++++++++++---- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 0162d57cd5..3cd79fa729 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -35,8 +35,9 @@ namespace osu.Game.Tests.Visual.Gameplay { private GraphContainer graphs = null!; private SettingsSlider sliderMaxCombo = null!; + private SettingsCheckbox scaleToMax = null!; - private FillFlowContainer legend = null!; + private FillFlowContainer legend = null!; private readonly BindableBool standardisedVisible = new BindableBool(true); private readonly BindableBool classicVisible = new BindableBool(true); @@ -78,7 +79,7 @@ namespace osu.Game.Tests.Visual.Gameplay }, new Drawable[] { - legend = new FillFlowContainer + legend = new FillFlowContainer { Padding = new MarginPadding(20), Direction = FillDirection.Vertical, @@ -93,26 +94,31 @@ namespace osu.Game.Tests.Visual.Gameplay Padding = new MarginPadding(20), RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Direction = FillDirection.Full, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), Children = new Drawable[] { sliderMaxCombo = new SettingsSlider { - Width = 0.5f, TransferValueOnCommit = true, Current = new BindableInt(1024) { MinValue = 96, MaxValue = 8192, }, - LabelText = "max combo", + LabelText = "Max combo", + }, + scaleToMax = new SettingsCheckbox + { + LabelText = "Rescale plots to 100%", + Current = { Value = true, Default = true } }, new OsuTextFlowContainer { RelativeSizeAxes = Axes.X, - Width = 0.5f, AutoSizeAxes = Axes.Y, - Text = $"Left click to add miss\nRight click to add OK/{base_ok}" + Text = $"Left click to add miss\nRight click to add OK/{base_ok}", + Margin = new MarginPadding { Top = 20 } } } }, @@ -122,6 +128,12 @@ namespace osu.Game.Tests.Visual.Gameplay }; sliderMaxCombo.Current.BindValueChanged(_ => rerun()); + scaleToMax.Current.BindValueChanged(_ => rerun()); + + standardisedVisible.BindValueChanged(_ => rescalePlots()); + classicVisible.BindValueChanged(_ => rescalePlots()); + scoreV1Visible.BindValueChanged(_ => rescalePlots()); + scoreV2Visible.BindValueChanged(_ => rescalePlots()); graphs.MissLocations.BindCollectionChanged((_, __) => rerun()); graphs.NonPerfectLocations.BindCollectionChanged((_, __) => rerun()); @@ -145,6 +157,24 @@ namespace osu.Game.Tests.Visual.Gameplay runScoreV1(); runScoreV2(); + + rescalePlots(); + } + + private void rescalePlots() + { + if (!scaleToMax.Current.Value && legend.Any(entry => entry.Visible.Value)) + { + long maxScore = legend.Where(entry => entry.Visible.Value).Max(entry => entry.FinalScore); + + foreach (var graph in graphs) + graph.Height = graph.Values.Max() / maxScore; + } + else + { + foreach (var graph in graphs) + graph.Height = 1; + } } private void runScoreV1() @@ -289,6 +319,8 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.Add(graph = new LineGraph { Name = scoringAlgorithm.Name, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, LineColour = scoringAlgorithm.Colour, Values = results @@ -312,14 +344,14 @@ namespace osu.Game.Tests.Visual.Gameplay public BindableBool Visible { get; init; } = null!; } - public partial class GraphContainer : Container, IHasCustomTooltip> + public partial class GraphContainer : Container, IHasCustomTooltip> { public readonly BindableList MissLocations = new BindableList(); public readonly BindableList NonPerfectLocations = new BindableList(); public Bindable MaxCombo = new Bindable(); - protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; + protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; private readonly Box hoverLine; @@ -470,7 +502,7 @@ namespace osu.Game.Tests.Visual.Gameplay public ITooltip> GetCustomTooltip() => tooltip ??= new GraphTooltip(this); - public IEnumerable TooltipContent => Content.OfType(); + public IEnumerable TooltipContent => Content; public partial class GraphTooltip : CompositeDrawable, ITooltip> { @@ -535,8 +567,9 @@ namespace osu.Game.Tests.Visual.Gameplay public BindableBool Visible { get; } = new BindableBool(true); + public readonly long FinalScore; + private readonly string description; - private readonly long finalScore; private readonly LineGraph lineGraph; private OsuSpriteText descriptionText = null!; @@ -545,7 +578,7 @@ namespace osu.Game.Tests.Visual.Gameplay public LegendEntry(ScoringAlgorithm scoringAlgorithm, LineGraph lineGraph) { description = scoringAlgorithm.Name; - finalScore = scoringAlgorithm.GetTotalScore(); + FinalScore = scoringAlgorithm.GetTotalScore(); AccentColour = scoringAlgorithm.Colour; Visible.BindTo(scoringAlgorithm.Visible); @@ -598,7 +631,7 @@ namespace osu.Game.Tests.Visual.Gameplay Colour = IsHovered ? AccentColour.Lighten(0.2f) : AccentColour; descriptionText.Text = $"{(Visible.Value ? FontAwesome.Solid.CheckCircle.Icon : FontAwesome.Solid.Circle.Icon)} {description}"; - finalScoreText.Text = finalScore.ToString("#,0"); + finalScoreText.Text = FinalScore.ToString("#,0"); lineGraph.Alpha = Visible.Value ? 1 : 0; } } From 3981e2e957ab14d1d8f23b2e65d8f910c18765fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 13:29:02 +0200 Subject: [PATCH 5/5] Only show visible graph values in tooltip --- osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 3cd79fa729..0e64af9920 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -550,6 +550,8 @@ namespace osu.Game.Tests.Visual.Gameplay foreach (var graph in content) { + if (graph.Alpha == 0) continue; + float valueAtHover = graph.Values.ElementAt(relevantCombo); float ofTotal = valueAtHover / graph.Values.Last();