From 6b0f3674c320f345caaff0068f6c00bfebdafb22 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Wed, 27 Jul 2022 08:51:55 +0800 Subject: [PATCH 01/27] implement `LegacySongProgress` --- osu.Game/Skinning/LegacySongProgress.cs | 101 ++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 osu.Game/Skinning/LegacySongProgress.cs diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs new file mode 100644 index 0000000000..57280fff07 --- /dev/null +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -0,0 +1,101 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable + +using System; +using System.Linq; +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.Graphics.UserInterface; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; +using osuTK; + +namespace osu.Game.Skinning +{ + public class LegacySongProgress : CompositeDrawable, ISkinnableDrawable + { + public bool UsesFixedAnchor { get; set; } + + [Resolved] + private GameplayClock gameplayClock { get; set; } + + [Resolved(canBeNull: true)] + private DrawableRuleset drawableRuleset { get; set; } + + [Resolved(canBeNull: true)] + private IBindable beatmap { get; set; } + + private double lastHitTime; + private double firstHitTime; + private double firstEventTime; + private CircularProgress pie; + + [BackgroundDependencyLoader] + private void load() + { + Size = new Vector2(35); + + InternalChildren = new Drawable[] + { + pie = new CircularProgress + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.5f, + }, + new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderColour = Colour4.White, + BorderThickness = 2, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + AlwaysPresent = true, + Alpha = 0, + } + }, + new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Colour4.White, + Size = new Vector2(3), + } + }; + + firstEventTime = beatmap?.Value.Storyboard.EarliestEventTime ?? 0; + firstHitTime = drawableRuleset.Objects.First().StartTime; + lastHitTime = drawableRuleset.Objects.Last().GetEndTime() + 1; + } + + protected override void Update() + { + base.Update(); + + double gameplayTime = gameplayClock?.CurrentTime ?? Time.Current; + + if (gameplayTime < firstHitTime) + { + pie.Scale = new Vector2(-1, 1); + pie.Anchor = Anchor.TopRight; + pie.Colour = Colour4.LimeGreen; + pie.Current.Value = 1 - Math.Clamp((gameplayTime - firstEventTime) / (firstHitTime - firstEventTime), 0, 1); + } + else + { + pie.Scale = new Vector2(1); + pie.Anchor = Anchor.TopLeft; + pie.Colour = Colour4.White; + pie.Current.Value = Math.Clamp((gameplayTime - firstHitTime) / (lastHitTime - firstHitTime), 0, 1); + } + } + } +} From a2320aeb278281c51f17f51386988e4c69574ba1 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Wed, 27 Jul 2022 08:52:27 +0800 Subject: [PATCH 02/27] replace `SongProgress` with `LegacySongProgress` --- osu.Game/Skinning/LegacySkin.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 34219722a1..c3872f4fa0 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -16,12 +16,13 @@ using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps.Formats; using osu.Game.Database; +using osu.Game.Extensions; using osu.Game.IO; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD.HitErrorMeters; +using osuTK; using osuTK.Graphics; namespace osu.Game.Skinning @@ -344,7 +345,15 @@ namespace osu.Game.Skinning accuracy.Y = container.ToLocalSpace(score.ScreenSpaceDrawQuad.BottomRight).Y; } - var songProgress = container.OfType().FirstOrDefault(); + var songProgress = container.OfType().FirstOrDefault(); + + if (songProgress != null && accuracy != null) + { + songProgress.Anchor = Anchor.TopRight; + songProgress.Origin = Anchor.CentreRight; + songProgress.X = -accuracy.ScreenSpaceDeltaToParentSpace(accuracy.ScreenSpaceDrawQuad.Size).X - 10; + songProgress.Y = container.ToLocalSpace(accuracy.ScreenSpaceDrawQuad.TopLeft).Y + (accuracy.ScreenSpaceDeltaToParentSpace(accuracy.ScreenSpaceDrawQuad.Size).Y / 2); + } var hitError = container.OfType().FirstOrDefault(); @@ -354,12 +363,6 @@ namespace osu.Game.Skinning hitError.Origin = Anchor.CentreLeft; hitError.Rotation = -90; } - - if (songProgress != null) - { - if (hitError != null) hitError.Y -= SongProgress.MAX_HEIGHT; - if (combo != null) combo.Y -= SongProgress.MAX_HEIGHT; - } }) { Children = new Drawable[] @@ -368,7 +371,7 @@ namespace osu.Game.Skinning new LegacyScoreCounter(), new LegacyAccuracyCounter(), new LegacyHealthDisplay(), - new SongProgress(), + new LegacySongProgress(), new BarHitErrorMeter(), } }; From 842ab3c5c1c4a08ae0518223e16537590405559b Mon Sep 17 00:00:00 2001 From: Nitrous Date: Wed, 27 Jul 2022 09:41:58 +0800 Subject: [PATCH 03/27] remove unused using --- osu.Game/Skinning/LegacySkin.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index c3872f4fa0..66258fb4a2 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -22,7 +22,6 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD.HitErrorMeters; -using osuTK; using osuTK.Graphics; namespace osu.Game.Skinning From b803ec543f3423afa408c340d88bb63fe7cab43e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Jul 2022 13:50:03 +0900 Subject: [PATCH 04/27] Remove unused `combo` DI retrieval --- osu.Game/Skinning/LegacySkin.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 66258fb4a2..15d4965a1d 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -337,7 +337,6 @@ namespace osu.Game.Skinning { var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); - var combo = container.OfType().FirstOrDefault(); if (score != null && accuracy != null) { From 62ca3aada6bb969de3185065df1001834aa2034c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Jul 2022 13:53:25 +0900 Subject: [PATCH 05/27] Transfer TODO comment across to copy-pasted implmentation --- osu.Game/Skinning/LegacySongProgress.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index 57280fff07..4d8838e031 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -73,6 +73,7 @@ namespace osu.Game.Skinning firstEventTime = beatmap?.Value.Storyboard.EarliestEventTime ?? 0; firstHitTime = drawableRuleset.Objects.First().StartTime; + //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). lastHitTime = drawableRuleset.Objects.Last().GetEndTime() + 1; } From d8e605d8aaa8e8119bdd3397fcaa7d3685942824 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Jul 2022 13:58:01 +0900 Subject: [PATCH 06/27] Fix broken tests due to badly reimplemented copy-pasted code --- osu.Game/Skinning/LegacySongProgress.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index 4d8838e031..2fd4180139 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -72,9 +72,13 @@ namespace osu.Game.Skinning }; firstEventTime = beatmap?.Value.Storyboard.EarliestEventTime ?? 0; - firstHitTime = drawableRuleset.Objects.First().StartTime; - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - lastHitTime = drawableRuleset.Objects.Last().GetEndTime() + 1; + + if (drawableRuleset != null) + { + firstHitTime = drawableRuleset.Objects.First().StartTime; + //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). + lastHitTime = drawableRuleset.Objects.Last().GetEndTime() + 1; + } } protected override void Update() From a12676c25de8486987374068b3636d4c4fe5a882 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Wed, 27 Jul 2022 14:35:18 +0800 Subject: [PATCH 07/27] scale down graph from bleeding through border --- osu.Game/Skinning/LegacySongProgress.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index 2fd4180139..a141a5f91e 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -44,10 +44,16 @@ namespace osu.Game.Skinning InternalChildren = new Drawable[] { - pie = new CircularProgress + new Container { + Size = new Vector2(0.95f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Alpha = 0.5f, + Child = pie = new CircularProgress + { + RelativeSizeAxes = Axes.Both, + }, }, new CircularContainer { @@ -91,14 +97,14 @@ namespace osu.Game.Skinning { pie.Scale = new Vector2(-1, 1); pie.Anchor = Anchor.TopRight; - pie.Colour = Colour4.LimeGreen; + pie.Colour = new Colour4(199, 255, 47, 153); pie.Current.Value = 1 - Math.Clamp((gameplayTime - firstEventTime) / (firstHitTime - firstEventTime), 0, 1); } else { pie.Scale = new Vector2(1); pie.Anchor = Anchor.TopLeft; - pie.Colour = Colour4.White; + pie.Colour = new Colour4(255, 255, 255, 153); pie.Current.Value = Math.Clamp((gameplayTime - firstHitTime) / (lastHitTime - firstHitTime), 0, 1); } } From 37e642b0bd41f30265e239b737faafecabc6bac9 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Wed, 27 Jul 2022 15:19:21 +0800 Subject: [PATCH 08/27] make `SongProgress` abstract - move unrelated logic to `DefaultSongProgress` - make `LegacySongProgress` inherit `SongProgress` --- .../Visual/Gameplay/TestSceneSongProgress.cs | 4 +- .../DefaultSongProgress.cs} | 67 ++++----------- osu.Game/Screens/Play/HUD/SongProgress.cs | 85 +++++++++++++++++++ .../Screens/Play/{ => HUD}/SongProgressBar.cs | 2 +- .../Play/{ => HUD}/SongProgressGraph.cs | 2 +- .../Play/{ => HUD}/SongProgressInfo.cs | 2 +- osu.Game/Skinning/DefaultSkin.cs | 2 +- osu.Game/Skinning/LegacySongProgress.cs | 53 ++++-------- 8 files changed, 126 insertions(+), 91 deletions(-) rename osu.Game/Screens/Play/{SongProgress.cs => HUD/DefaultSongProgress.cs} (82%) create mode 100644 osu.Game/Screens/Play/HUD/SongProgress.cs rename osu.Game/Screens/Play/{ => HUD}/SongProgressBar.cs (99%) rename osu.Game/Screens/Play/{ => HUD}/SongProgressGraph.cs (97%) rename osu.Game/Screens/Play/{ => HUD}/SongProgressInfo.cs (98%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index 07efb25b46..6f2853b095 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneSongProgress : OsuTestScene { - private SongProgress progress; + private DefaultSongProgress progress; private TestSongProgressGraph graph; private readonly Container progressContainer; @@ -62,7 +62,7 @@ namespace osu.Game.Tests.Visual.Gameplay progress = null; } - progressContainer.Add(progress = new SongProgress + progressContainer.Add(progress = new DefaultSongProgress { RelativeSizeAxes = Axes.X, Anchor = Anchor.BottomLeft, diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs similarity index 82% rename from osu.Game/Screens/Play/SongProgress.cs rename to osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index d1510d10c2..7c2d8a9de2 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -5,12 +5,9 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Timing; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Rulesets.Objects; @@ -18,9 +15,9 @@ using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osuTK; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { - public class SongProgress : OverlayContainer, ISkinnableDrawable + public class DefaultSongProgress : SongProgress { public const float MAX_HEIGHT = info_height + bottom_bar_height + graph_height + handle_height; @@ -52,41 +49,13 @@ namespace osu.Game.Screens.Play protected override bool BlockScrollInput => false; - private double firstHitTime => objects.First().StartTime; - - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - private double lastHitTime => objects.Last().GetEndTime() + 1; - - private IEnumerable objects; - - public IEnumerable Objects - { - set - { - graph.Objects = objects = value; - - info.StartTime = firstHitTime; - info.EndTime = lastHitTime; - - bar.StartTime = firstHitTime; - bar.EndTime = lastHitTime; - } - } - [Resolved(canBeNull: true)] private Player player { get; set; } - [Resolved] - private GameplayClock gameplayClock { get; set; } - [Resolved(canBeNull: true)] private DrawableRuleset drawableRuleset { get; set; } - private IClock referenceClock; - - public bool UsesFixedAnchor { get; set; } - - public SongProgress() + public DefaultSongProgress() { RelativeSizeAxes = Axes.X; Anchor = Anchor.BottomRight; @@ -127,9 +96,6 @@ namespace osu.Game.Screens.Play { if (player?.Configuration.AllowUserInteraction == true) ((IBindable)AllowSeeking).BindTo(drawableRuleset.HasReplayLoaded); - - referenceClock = drawableRuleset.FrameStableClock; - Objects = drawableRuleset.Objects; } graph.FillColour = bar.FillColour = colours.BlueLighter; @@ -203,21 +169,24 @@ namespace osu.Game.Screens.Play this.FadeOut(100); } + protected override void UpdateObjects(IEnumerable objects) + { + graph.Objects = objects; + info.StartTime = FirstHitTime; + info.EndTime = LastHitTime; + bar.StartTime = FirstHitTime; + bar.EndTime = LastHitTime; + } + + protected override void UpdateProgress(double progress, double time, bool isIntro) + { + bar.CurrentTime = time; + graph.Progress = (int)(graph.ColumnCount * progress); + } + protected override void Update() { base.Update(); - - if (objects == null) - return; - - double gameplayTime = gameplayClock?.CurrentTime ?? Time.Current; - double frameStableTime = referenceClock?.CurrentTime ?? gameplayTime; - - double progress = Math.Min(1, (frameStableTime - firstHitTime) / (lastHitTime - firstHitTime)); - - bar.CurrentTime = gameplayTime; - graph.Progress = (int)(graph.ColumnCount * progress); - Height = bottom_bar_height + graph_height + handle_size.Y + info_height - graph.Y; } diff --git a/osu.Game/Screens/Play/HUD/SongProgress.cs b/osu.Game/Screens/Play/HUD/SongProgress.cs new file mode 100644 index 0000000000..c245a47554 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/SongProgress.cs @@ -0,0 +1,85 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Skinning; + +namespace osu.Game.Screens.Play.HUD +{ + public abstract class SongProgress : OverlayContainer, ISkinnableDrawable + { + public bool UsesFixedAnchor { get; set; } + + [Resolved] + private GameplayClock gameplayClock { get; set; } + + [Resolved(canBeNull: true)] + private DrawableRuleset drawableRuleset { get; set; } + + [Resolved(canBeNull: true)] + private IBindable beatmap { get; set; } + + private IClock referenceClock; + private IEnumerable objects; + + public IEnumerable Objects + { + set => UpdateObjects(objects = value); + } + + protected double FirstHitTime => objects.FirstOrDefault()?.StartTime ?? 0; + + //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). + protected double LastHitTime => objects.LastOrDefault()?.GetEndTime() ?? 0; + + protected double FirstEventTime { get; private set; } + + protected abstract void UpdateProgress(double progress, double time, bool isIntro); + protected abstract void UpdateObjects(IEnumerable objects); + + [BackgroundDependencyLoader] + private void load() + { + if (drawableRuleset != null) + { + Objects = drawableRuleset.Objects; + referenceClock = drawableRuleset.FrameStableClock; + } + + if (beatmap != null) + { + FirstEventTime = beatmap.Value.Storyboard.EarliestEventTime ?? 0; + } + } + + protected override void Update() + { + base.Update(); + + if (objects == null) + return; + + double gameplayTime = gameplayClock?.CurrentTime ?? Time.Current; + double frameStableTime = referenceClock?.CurrentTime ?? gameplayTime; + + if (frameStableTime < FirstHitTime) + { + UpdateProgress((frameStableTime - FirstEventTime) / (FirstHitTime - FirstEventTime), gameplayTime, true); + } + else + { + UpdateProgress((frameStableTime - FirstHitTime) / (LastHitTime - FirstHitTime), gameplayTime, false); + } + } + } +} diff --git a/osu.Game/Screens/Play/SongProgressBar.cs b/osu.Game/Screens/Play/HUD/SongProgressBar.cs similarity index 99% rename from osu.Game/Screens/Play/SongProgressBar.cs rename to osu.Game/Screens/Play/HUD/SongProgressBar.cs index 67923f4b6a..db4e200724 100644 --- a/osu.Game/Screens/Play/SongProgressBar.cs +++ b/osu.Game/Screens/Play/HUD/SongProgressBar.cs @@ -13,7 +13,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Framework.Threading; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public class SongProgressBar : SliderBar { diff --git a/osu.Game/Screens/Play/SongProgressGraph.cs b/osu.Game/Screens/Play/HUD/SongProgressGraph.cs similarity index 97% rename from osu.Game/Screens/Play/SongProgressGraph.cs rename to osu.Game/Screens/Play/HUD/SongProgressGraph.cs index c742df67ce..f234b45922 100644 --- a/osu.Game/Screens/Play/SongProgressGraph.cs +++ b/osu.Game/Screens/Play/HUD/SongProgressGraph.cs @@ -8,7 +8,7 @@ using System.Collections.Generic; using System.Diagnostics; using osu.Game.Rulesets.Objects; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public class SongProgressGraph : SquareGraph { diff --git a/osu.Game/Screens/Play/SongProgressInfo.cs b/osu.Game/Screens/Play/HUD/SongProgressInfo.cs similarity index 98% rename from osu.Game/Screens/Play/SongProgressInfo.cs rename to osu.Game/Screens/Play/HUD/SongProgressInfo.cs index 40759c3a3b..8f10e84509 100644 --- a/osu.Game/Screens/Play/SongProgressInfo.cs +++ b/osu.Game/Screens/Play/HUD/SongProgressInfo.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using System; -namespace osu.Game.Screens.Play +namespace osu.Game.Screens.Play.HUD { public class SongProgressInfo : Container { diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 5267861e3e..0848f42360 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -147,7 +147,7 @@ namespace osu.Game.Skinning new DefaultScoreCounter(), new DefaultAccuracyCounter(), new DefaultHealthDisplay(), - new SongProgress(), + new DefaultSongProgress(), new BarHitErrorMeter(), new BarHitErrorMeter(), new PerformancePointsCounter() diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index a141a5f91e..5f27d73761 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -3,38 +3,20 @@ #nullable disable -using System; -using System.Linq; +using System.Collections.Generic; 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.Graphics.UserInterface; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.UI; -using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osuTK; namespace osu.Game.Skinning { - public class LegacySongProgress : CompositeDrawable, ISkinnableDrawable + public class LegacySongProgress : SongProgress { - public bool UsesFixedAnchor { get; set; } - - [Resolved] - private GameplayClock gameplayClock { get; set; } - - [Resolved(canBeNull: true)] - private DrawableRuleset drawableRuleset { get; set; } - - [Resolved(canBeNull: true)] - private IBindable beatmap { get; set; } - - private double lastHitTime; - private double firstHitTime; - private double firstEventTime; private CircularProgress pie; [BackgroundDependencyLoader] @@ -76,36 +58,35 @@ namespace osu.Game.Skinning Size = new Vector2(3), } }; - - firstEventTime = beatmap?.Value.Storyboard.EarliestEventTime ?? 0; - - if (drawableRuleset != null) - { - firstHitTime = drawableRuleset.Objects.First().StartTime; - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - lastHitTime = drawableRuleset.Objects.Last().GetEndTime() + 1; - } } - protected override void Update() + protected override void PopIn() { - base.Update(); + } - double gameplayTime = gameplayClock?.CurrentTime ?? Time.Current; + protected override void PopOut() + { + } - if (gameplayTime < firstHitTime) + protected override void UpdateObjects(IEnumerable objects) + { + } + + protected override void UpdateProgress(double progress, double time, bool isIntro) + { + if (isIntro) { pie.Scale = new Vector2(-1, 1); pie.Anchor = Anchor.TopRight; pie.Colour = new Colour4(199, 255, 47, 153); - pie.Current.Value = 1 - Math.Clamp((gameplayTime - firstEventTime) / (firstHitTime - firstEventTime), 0, 1); + pie.Current.Value = 1 - progress; } else { pie.Scale = new Vector2(1); pie.Anchor = Anchor.TopLeft; pie.Colour = new Colour4(255, 255, 255, 153); - pie.Current.Value = Math.Clamp((gameplayTime - firstHitTime) / (lastHitTime - firstHitTime), 0, 1); + pie.Current.Value = progress; } } } From 1e013bd4e90ce2b22830fff88ca5ab283f0a77bd Mon Sep 17 00:00:00 2001 From: Nitrous Date: Wed, 27 Jul 2022 15:57:23 +0800 Subject: [PATCH 09/27] move song progress graph to its own test scene --- .../Gameplay/TestSceneSongProgressGraph.cs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSongProgressGraph.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgressGraph.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgressGraph.cs new file mode 100644 index 0000000000..2fa3c0c7ec --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgressGraph.cs @@ -0,0 +1,73 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Play.HUD; + +namespace osu.Game.Tests.Visual.Gameplay +{ + [TestFixture] + public class TestSceneSongProgressGraph : OsuTestScene + { + private TestSongProgressGraph graph; + + [SetUpSteps] + public void SetupSteps() + { + AddStep("add new big graph", () => + { + if (graph != null) + { + graph.Expire(); + graph = null; + } + + Add(graph = new TestSongProgressGraph + { + RelativeSizeAxes = Axes.X, + Height = 200, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }); + }); + } + + [Test] + public void TestGraphRecreation() + { + AddAssert("ensure not created", () => graph.CreationCount == 0); + AddStep("display values", displayRandomValues); + AddUntilStep("wait for creation count", () => graph.CreationCount == 1); + AddRepeatStep("new values", displayRandomValues, 5); + AddWaitStep("wait some", 5); + AddAssert("ensure recreation debounced", () => graph.CreationCount == 2); + } + + private void displayRandomValues() + { + var objects = new List(); + for (double i = 0; i < 5000; i += RNG.NextDouble() * 10 + i / 1000) + objects.Add(new HitObject { StartTime = i }); + + graph.Objects = objects; + } + + private class TestSongProgressGraph : SongProgressGraph + { + public int CreationCount { get; private set; } + + protected override void RecreateGraph() + { + base.RecreateGraph(); + CreationCount++; + } + } + } +} From 6af6f03e293b22b763dc6b55c833fff2414fc36a Mon Sep 17 00:00:00 2001 From: Nitrous Date: Wed, 27 Jul 2022 15:57:47 +0800 Subject: [PATCH 10/27] refactor song progress test scene --- .../Visual/Gameplay/TestSceneSongProgress.cs | 115 +++++------------- 1 file changed, 30 insertions(+), 85 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index 6f2853b095..f32a7e7cab 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -7,23 +7,20 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; -using osu.Framework.Utils; using osu.Framework.Timing; -using osu.Game.Graphics; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; +using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { [TestFixture] - public class TestSceneSongProgress : OsuTestScene + public class TestSceneSongProgress : SkinnableHUDComponentTestScene { private DefaultSongProgress progress; - private TestSongProgressGraph graph; - private readonly Container progressContainer; + private readonly List progresses = new List(); private readonly StopwatchClock clock; private readonly FramedClock framedClock; @@ -35,77 +32,18 @@ namespace osu.Game.Tests.Visual.Gameplay { clock = new StopwatchClock(); gameplayClock = new GameplayClock(framedClock = new FramedClock(clock)); - - Add(progressContainer = new Container - { - RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Height = 100, - Y = -100, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(1), - } - }); } [SetUpSteps] public void SetupSteps() { - AddStep("add new song progress", () => - { - if (progress != null) - { - progress.Expire(); - progress = null; - } - - progressContainer.Add(progress = new DefaultSongProgress - { - RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - }); - }); - - AddStep("add new big graph", () => - { - if (graph != null) - { - graph.Expire(); - graph = null; - } - - Add(graph = new TestSongProgressGraph - { - RelativeSizeAxes = Axes.X, - Height = 200, - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - }); - }); - AddStep("reset clock", clock.Reset); } - [Test] - public void TestGraphRecreation() - { - AddAssert("ensure not created", () => graph.CreationCount == 0); - AddStep("display values", displayRandomValues); - AddUntilStep("wait for creation count", () => graph.CreationCount == 1); - AddRepeatStep("new values", displayRandomValues, 5); - AddWaitStep("wait some", 5); - AddAssert("ensure recreation debounced", () => graph.CreationCount == 2); - } - [Test] public void TestDisplay() { AddStep("display max values", displayMaxValues); - AddUntilStep("wait for graph", () => graph.CreationCount == 1); AddStep("start", clock.Start); AddStep("allow seeking", () => progress.AllowSeeking.Value = true); AddStep("hide graph", () => progress.ShowGraph.Value = false); @@ -115,15 +53,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("stop", clock.Stop); } - private void displayRandomValues() - { - var objects = new List(); - for (double i = 0; i < 5000; i += RNG.NextDouble() * 10 + i / 1000) - objects.Add(new HitObject { StartTime = i }); - - replaceObjects(objects); - } - private void displayMaxValues() { var objects = new List(); @@ -135,10 +64,12 @@ namespace osu.Game.Tests.Visual.Gameplay private void replaceObjects(List objects) { - progress.Objects = objects; - graph.Objects = objects; - progress.RequestSeek = pos => clock.Seek(pos); + + foreach (var progress in progresses) + { + progress.Objects = objects; + } } protected override void Update() @@ -147,15 +78,29 @@ namespace osu.Game.Tests.Visual.Gameplay framedClock.ProcessFrame(); } - private class TestSongProgressGraph : SongProgressGraph + protected override Drawable CreateDefaultImplementation() { - public int CreationCount { get; private set; } - - protected override void RecreateGraph() + progress = new DefaultSongProgress { - base.RecreateGraph(); - CreationCount++; - } + RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }; + + progresses.Add(progress); + return progress; + } + + protected override Drawable CreateLegacyImplementation() + { + var progress = new LegacySongProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + progresses.Add(progress); + return progress; } } } From a222278710568f240b75b69dc4600941ecb395f4 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Wed, 27 Jul 2022 16:01:35 +0800 Subject: [PATCH 11/27] remove unused using --- osu.Game/Skinning/DefaultSkin.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 0848f42360..7d217dd956 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -16,7 +16,6 @@ using osu.Game.Audio; using osu.Game.Beatmaps.Formats; using osu.Game.Extensions; using osu.Game.IO; -using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD.HitErrorMeters; using osuTK; From 45c11f2b7b2765c42d19069949fa5b3a14c35c6c Mon Sep 17 00:00:00 2001 From: Nitrous Date: Thu, 28 Jul 2022 08:01:38 +0800 Subject: [PATCH 12/27] account for gameplay start time --- osu.Game/Screens/Play/HUD/SongProgress.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SongProgress.cs b/osu.Game/Screens/Play/HUD/SongProgress.cs index c245a47554..5c7c7d28c6 100644 --- a/osu.Game/Screens/Play/HUD/SongProgress.cs +++ b/osu.Game/Screens/Play/HUD/SongProgress.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -20,9 +21,12 @@ namespace osu.Game.Screens.Play.HUD { public bool UsesFixedAnchor { get; set; } - [Resolved] + [Resolved(canBeNull: true)] private GameplayClock gameplayClock { get; set; } + [Resolved(canBeNull: true)] + private GameplayClockContainer gameplayClockContainer { get; set; } + [Resolved(canBeNull: true)] private DrawableRuleset drawableRuleset { get; set; } @@ -69,12 +73,13 @@ namespace osu.Game.Screens.Play.HUD if (objects == null) return; - double gameplayTime = gameplayClock?.CurrentTime ?? Time.Current; + double gameplayTime = gameplayClockContainer?.GameplayClock.CurrentTime ?? gameplayClock?.CurrentTime ?? Time.Current; double frameStableTime = referenceClock?.CurrentTime ?? gameplayTime; if (frameStableTime < FirstHitTime) { - UpdateProgress((frameStableTime - FirstEventTime) / (FirstHitTime - FirstEventTime), gameplayTime, true); + double earliest = Math.Min(FirstEventTime, gameplayClockContainer?.StartTime ?? 0); + UpdateProgress((frameStableTime - earliest) / (FirstHitTime - earliest), gameplayTime, true); } else { From 0913cf013cbe8555a5450924a7ebfd3c2a8d8820 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jul 2022 13:24:14 +0900 Subject: [PATCH 13/27] Split out tests and fix variable conflict --- .../Visual/Gameplay/TestSceneSongProgress.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index f32a7e7cab..897284ed80 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -19,7 +19,8 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneSongProgress : SkinnableHUDComponentTestScene { - private DefaultSongProgress progress; + private DefaultSongProgress defaultProgress; + private readonly List progresses = new List(); private readonly StopwatchClock clock; @@ -45,14 +46,19 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("display max values", displayMaxValues); AddStep("start", clock.Start); - AddStep("allow seeking", () => progress.AllowSeeking.Value = true); - AddStep("hide graph", () => progress.ShowGraph.Value = false); - AddStep("disallow seeking", () => progress.AllowSeeking.Value = false); - AddStep("allow seeking", () => progress.AllowSeeking.Value = true); - AddStep("show graph", () => progress.ShowGraph.Value = true); AddStep("stop", clock.Stop); } + [Test] + public void TestToggleSeeking() + { + AddStep("allow seeking", () => defaultProgress.AllowSeeking.Value = true); + AddStep("hide graph", () => defaultProgress.ShowGraph.Value = false); + AddStep("disallow seeking", () => defaultProgress.AllowSeeking.Value = false); + AddStep("allow seeking", () => defaultProgress.AllowSeeking.Value = true); + AddStep("show graph", () => defaultProgress.ShowGraph.Value = true); + } + private void displayMaxValues() { var objects = new List(); @@ -64,11 +70,11 @@ namespace osu.Game.Tests.Visual.Gameplay private void replaceObjects(List objects) { - progress.RequestSeek = pos => clock.Seek(pos); + defaultProgress.RequestSeek = pos => clock.Seek(pos); - foreach (var progress in progresses) + foreach (var p in progresses) { - progress.Objects = objects; + p.Objects = objects; } } @@ -80,15 +86,15 @@ namespace osu.Game.Tests.Visual.Gameplay protected override Drawable CreateDefaultImplementation() { - progress = new DefaultSongProgress + defaultProgress = new DefaultSongProgress { RelativeSizeAxes = Axes.X, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, }; - progresses.Add(progress); - return progress; + progresses.Add(defaultProgress); + return defaultProgress; } protected override Drawable CreateLegacyImplementation() From db62d4be3a006d8782de23ad39d50bf93da7cb04 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Thu, 28 Jul 2022 15:12:49 +0800 Subject: [PATCH 14/27] apply suggestions - refactor `SongProgress` - make`UpdateProgress` more readable - enable NRT on new classes - refactor `TestSceneSongProgress` to use `GameplayClockContainer` --- .../Visual/Gameplay/TestSceneSongProgress.cs | 89 +++++++------------ .../Screens/Play/HUD/DefaultSongProgress.cs | 11 ++- osu.Game/Screens/Play/HUD/SongProgress.cs | 47 ++++------ osu.Game/Skinning/LegacySongProgress.cs | 9 +- 4 files changed, 63 insertions(+), 93 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index 897284ed80..911b9bb7ec 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -4,11 +4,12 @@ #nullable disable using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Testing; -using osu.Framework.Timing; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; @@ -19,44 +20,43 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneSongProgress : SkinnableHUDComponentTestScene { - private DefaultSongProgress defaultProgress; + private DefaultSongProgress progress => this.ChildrenOfType().Single(); + private GameplayClockContainer gameplayClockContainer; + private const double gameplay_start_time = -2000; - private readonly List progresses = new List(); - - private readonly StopwatchClock clock; - private readonly FramedClock framedClock; - - [Cached] - private readonly GameplayClock gameplayClock; - - public TestSceneSongProgress() + [BackgroundDependencyLoader] + private void load() { - clock = new StopwatchClock(); - gameplayClock = new GameplayClock(framedClock = new FramedClock(clock)); + var working = CreateWorkingBeatmap(Ruleset.Value); + working.LoadTrack(); + Add(gameplayClockContainer = new MasterGameplayClockContainer(working, gameplay_start_time)); + Dependencies.CacheAs(gameplayClockContainer); + Dependencies.CacheAs(gameplayClockContainer.GameplayClock); } [SetUpSteps] public void SetupSteps() { - AddStep("reset clock", clock.Reset); + AddStep("reset clock", () => gameplayClockContainer.Reset(false)); } [Test] public void TestDisplay() { AddStep("display max values", displayMaxValues); - AddStep("start", clock.Start); - AddStep("stop", clock.Stop); + AddStep("seek to intro", () => gameplayClockContainer.Seek(gameplay_start_time)); + AddStep("start", gameplayClockContainer.Start); + AddStep("stop", gameplayClockContainer.Stop); } [Test] public void TestToggleSeeking() { - AddStep("allow seeking", () => defaultProgress.AllowSeeking.Value = true); - AddStep("hide graph", () => defaultProgress.ShowGraph.Value = false); - AddStep("disallow seeking", () => defaultProgress.AllowSeeking.Value = false); - AddStep("allow seeking", () => defaultProgress.AllowSeeking.Value = true); - AddStep("show graph", () => defaultProgress.ShowGraph.Value = true); + AddStep("allow seeking", () => progress.AllowSeeking.Value = true); + AddStep("hide graph", () => progress.ShowGraph.Value = false); + AddStep("disallow seeking", () => progress.AllowSeeking.Value = false); + AddStep("allow seeking", () => progress.AllowSeeking.Value = true); + AddStep("show graph", () => progress.ShowGraph.Value = true); } private void displayMaxValues() @@ -65,48 +65,25 @@ namespace osu.Game.Tests.Visual.Gameplay for (double i = 0; i < 5000; i++) objects.Add(new HitObject { StartTime = i }); - replaceObjects(objects); + setObjects(objects); } - private void replaceObjects(List objects) + private void setObjects(List objects) { - defaultProgress.RequestSeek = pos => clock.Seek(pos); - - foreach (var p in progresses) - { - p.Objects = objects; - } + this.ChildrenOfType().ForEach(progress => progress.Objects = objects); } - protected override void Update() + protected override Drawable CreateDefaultImplementation() => new DefaultSongProgress { - base.Update(); - framedClock.ProcessFrame(); - } + RelativeSizeAxes = Axes.X, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }; - protected override Drawable CreateDefaultImplementation() + protected override Drawable CreateLegacyImplementation() => new LegacySongProgress { - defaultProgress = new DefaultSongProgress - { - RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - }; - - progresses.Add(defaultProgress); - return defaultProgress; - } - - protected override Drawable CreateLegacyImplementation() - { - var progress = new LegacySongProgress - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; - - progresses.Add(progress); - return progress; - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; } } diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index 7c2d8a9de2..347bd797ac 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -49,6 +49,9 @@ namespace osu.Game.Screens.Play.HUD protected override bool BlockScrollInput => false; + [Resolved(canBeNull: true)] + private GameplayClock gameplayClock { get; set; } + [Resolved(canBeNull: true)] private Player player { get; set; } @@ -178,10 +181,12 @@ namespace osu.Game.Screens.Play.HUD bar.EndTime = LastHitTime; } - protected override void UpdateProgress(double progress, double time, bool isIntro) + protected override void UpdateProgress(double progress, bool isIntro) { - bar.CurrentTime = time; - graph.Progress = (int)(graph.ColumnCount * progress); + bar.CurrentTime = gameplayClock?.CurrentTime ?? Time.Current; + + if (!isIntro) + graph.Progress = (int)(graph.ColumnCount * progress); } protected override void Update() diff --git a/osu.Game/Screens/Play/HUD/SongProgress.cs b/osu.Game/Screens/Play/HUD/SongProgress.cs index 5c7c7d28c6..e0663b42ea 100644 --- a/osu.Game/Screens/Play/HUD/SongProgress.cs +++ b/osu.Game/Screens/Play/HUD/SongProgress.cs @@ -1,16 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Skinning; @@ -21,20 +16,14 @@ namespace osu.Game.Screens.Play.HUD { public bool UsesFixedAnchor { get; set; } - [Resolved(canBeNull: true)] - private GameplayClock gameplayClock { get; set; } + [Resolved] + private GameplayClockContainer? gameplayClockContainer { get; set; } [Resolved(canBeNull: true)] - private GameplayClockContainer gameplayClockContainer { get; set; } + private DrawableRuleset? drawableRuleset { get; set; } - [Resolved(canBeNull: true)] - private DrawableRuleset drawableRuleset { get; set; } - - [Resolved(canBeNull: true)] - private IBindable beatmap { get; set; } - - private IClock referenceClock; - private IEnumerable objects; + private IClock? referenceClock; + private IEnumerable? objects; public IEnumerable Objects { @@ -46,9 +35,7 @@ namespace osu.Game.Screens.Play.HUD //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). protected double LastHitTime => objects.LastOrDefault()?.GetEndTime() ?? 0; - protected double FirstEventTime { get; private set; } - - protected abstract void UpdateProgress(double progress, double time, bool isIntro); + protected abstract void UpdateProgress(double progress, bool isIntro); protected abstract void UpdateObjects(IEnumerable objects); [BackgroundDependencyLoader] @@ -59,11 +46,6 @@ namespace osu.Game.Screens.Play.HUD Objects = drawableRuleset.Objects; referenceClock = drawableRuleset.FrameStableClock; } - - if (beatmap != null) - { - FirstEventTime = beatmap.Value.Storyboard.EarliestEventTime ?? 0; - } } protected override void Update() @@ -73,17 +55,22 @@ namespace osu.Game.Screens.Play.HUD if (objects == null) return; - double gameplayTime = gameplayClockContainer?.GameplayClock.CurrentTime ?? gameplayClock?.CurrentTime ?? Time.Current; - double frameStableTime = referenceClock?.CurrentTime ?? gameplayTime; + // The reference clock is used to accurately tell the playfield's time. This is obtained from the drawable ruleset. + // However, if no drawable ruleset is available (i.e. used in tests), we fall back to either the gameplay clock container or this drawable's own clock. + double gameplayTime = referenceClock?.CurrentTime ?? gameplayClockContainer?.GameplayClock.CurrentTime ?? Time.Current; - if (frameStableTime < FirstHitTime) + if (gameplayTime < FirstHitTime) { - double earliest = Math.Min(FirstEventTime, gameplayClockContainer?.StartTime ?? 0); - UpdateProgress((frameStableTime - earliest) / (FirstHitTime - earliest), gameplayTime, true); + double earliest = gameplayClockContainer?.StartTime ?? 0; + double introDuration = FirstHitTime - earliest; + double currentIntroTime = gameplayTime - earliest; + UpdateProgress(currentIntroTime / introDuration, true); } else { - UpdateProgress((frameStableTime - FirstHitTime) / (LastHitTime - FirstHitTime), gameplayTime, false); + double duration = LastHitTime - FirstHitTime; + double currentTime = gameplayTime - FirstHitTime; + UpdateProgress(currentTime / duration, false); } } } diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index 5f27d73761..ee071ad3ed 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -17,7 +15,7 @@ namespace osu.Game.Skinning { public class LegacySongProgress : SongProgress { - private CircularProgress pie; + private CircularProgress? pie; [BackgroundDependencyLoader] private void load() @@ -72,8 +70,11 @@ namespace osu.Game.Skinning { } - protected override void UpdateProgress(double progress, double time, bool isIntro) + protected override void UpdateProgress(double progress, bool isIntro) { + if (pie == null) + return; + if (isIntro) { pie.Scale = new Vector2(-1, 1); From 70ed347b0624502761040067fd7e8fe18158bb6a Mon Sep 17 00:00:00 2001 From: Nitrous Date: Thu, 28 Jul 2022 15:19:35 +0800 Subject: [PATCH 15/27] simplify helper methods --- osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index 911b9bb7ec..4a786f2ebe 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -38,12 +38,12 @@ namespace osu.Game.Tests.Visual.Gameplay public void SetupSteps() { AddStep("reset clock", () => gameplayClockContainer.Reset(false)); + AddStep("set hit objects", setHitObjects); } [Test] public void TestDisplay() { - AddStep("display max values", displayMaxValues); AddStep("seek to intro", () => gameplayClockContainer.Seek(gameplay_start_time)); AddStep("start", gameplayClockContainer.Start); AddStep("stop", gameplayClockContainer.Stop); @@ -59,17 +59,12 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("show graph", () => progress.ShowGraph.Value = true); } - private void displayMaxValues() + private void setHitObjects() { var objects = new List(); for (double i = 0; i < 5000; i++) objects.Add(new HitObject { StartTime = i }); - setObjects(objects); - } - - private void setObjects(List objects) - { this.ChildrenOfType().ForEach(progress => progress.Objects = objects); } From bca3994d918a344cf9001dd1637564dfc94ba76f Mon Sep 17 00:00:00 2001 From: Nitrous Date: Thu, 28 Jul 2022 15:25:12 +0800 Subject: [PATCH 16/27] set `FirstHitTime` and `LastHitTime` once --- osu.Game/Screens/Play/HUD/SongProgress.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SongProgress.cs b/osu.Game/Screens/Play/HUD/SongProgress.cs index e0663b42ea..55c85a1d91 100644 --- a/osu.Game/Screens/Play/HUD/SongProgress.cs +++ b/osu.Game/Screens/Play/HUD/SongProgress.cs @@ -27,13 +27,19 @@ namespace osu.Game.Screens.Play.HUD public IEnumerable Objects { - set => UpdateObjects(objects = value); + set + { + objects = value; + FirstHitTime = objects.FirstOrDefault()?.StartTime ?? 0; + LastHitTime = objects.LastOrDefault()?.GetEndTime() ?? 0; + UpdateObjects(objects); + } } - protected double FirstHitTime => objects.FirstOrDefault()?.StartTime ?? 0; + protected double FirstHitTime { get; private set; } //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - protected double LastHitTime => objects.LastOrDefault()?.GetEndTime() ?? 0; + protected double LastHitTime { get; private set; } protected abstract void UpdateProgress(double progress, bool isIntro); protected abstract void UpdateObjects(IEnumerable objects); From 0d36907cad09b4d45305c1f2c3e342e570872252 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Thu, 28 Jul 2022 15:30:45 +0800 Subject: [PATCH 17/27] apply code quality fixes --- osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs | 2 +- osu.Game/Screens/Play/HUD/DefaultSongProgress.cs | 4 +++- osu.Game/Screens/Play/HUD/SongProgress.cs | 6 +++--- osu.Game/Skinning/LegacySongProgress.cs | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index 4a786f2ebe..3f81757fcd 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Gameplay [SetUpSteps] public void SetupSteps() { - AddStep("reset clock", () => gameplayClockContainer.Reset(false)); + AddStep("reset clock", () => gameplayClockContainer.Reset()); AddStep("set hit objects", setHitObjects); } diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index 347bd797ac..654884c0d5 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -174,7 +174,9 @@ namespace osu.Game.Screens.Play.HUD protected override void UpdateObjects(IEnumerable objects) { - graph.Objects = objects; + if (objects != null) + graph.Objects = objects; + info.StartTime = FirstHitTime; info.EndTime = LastHitTime; bar.StartTime = FirstHitTime; diff --git a/osu.Game/Screens/Play/HUD/SongProgress.cs b/osu.Game/Screens/Play/HUD/SongProgress.cs index 55c85a1d91..78f0142dba 100644 --- a/osu.Game/Screens/Play/HUD/SongProgress.cs +++ b/osu.Game/Screens/Play/HUD/SongProgress.cs @@ -30,8 +30,8 @@ namespace osu.Game.Screens.Play.HUD set { objects = value; - FirstHitTime = objects.FirstOrDefault()?.StartTime ?? 0; - LastHitTime = objects.LastOrDefault()?.GetEndTime() ?? 0; + FirstHitTime = objects?.FirstOrDefault()?.StartTime ?? 0; + LastHitTime = objects?.LastOrDefault()?.GetEndTime() ?? 0; UpdateObjects(objects); } } @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Play.HUD protected double LastHitTime { get; private set; } protected abstract void UpdateProgress(double progress, bool isIntro); - protected abstract void UpdateObjects(IEnumerable objects); + protected abstract void UpdateObjects(IEnumerable? objects); [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index ee071ad3ed..0d1110df47 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -66,7 +66,7 @@ namespace osu.Game.Skinning { } - protected override void UpdateObjects(IEnumerable objects) + protected override void UpdateObjects(IEnumerable? objects) { } From 71085538833a34e5eeea9b8087a531008394608a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jul 2022 18:20:08 +0900 Subject: [PATCH 18/27] Tidy up various things everywhere --- .../Visual/Gameplay/TestSceneSongProgress.cs | 35 ++++++++++--------- .../Screens/Play/HUD/DefaultSongProgress.cs | 2 -- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index 3f81757fcd..5e9cf8839d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -11,6 +9,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; @@ -20,18 +19,20 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneSongProgress : SkinnableHUDComponentTestScene { - private DefaultSongProgress progress => this.ChildrenOfType().Single(); - private GameplayClockContainer gameplayClockContainer; - private const double gameplay_start_time = -2000; + private GameplayClockContainer gameplayClockContainer = null!; + + private const double skip_target_time = -2000; [BackgroundDependencyLoader] private void load() { - var working = CreateWorkingBeatmap(Ruleset.Value); - working.LoadTrack(); - Add(gameplayClockContainer = new MasterGameplayClockContainer(working, gameplay_start_time)); - Dependencies.CacheAs(gameplayClockContainer); - Dependencies.CacheAs(gameplayClockContainer.GameplayClock); + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + Beatmap.Value.LoadTrack(); + + Add(gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, skip_target_time)); + + Dependencies.CacheAs(gameplayClockContainer); // required for StartTime + Dependencies.CacheAs(gameplayClockContainer.GameplayClock); // required for everything else } [SetUpSteps] @@ -44,7 +45,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestDisplay() { - AddStep("seek to intro", () => gameplayClockContainer.Seek(gameplay_start_time)); + AddStep("seek to intro", () => gameplayClockContainer.Seek(skip_target_time)); AddStep("start", gameplayClockContainer.Start); AddStep("stop", gameplayClockContainer.Stop); } @@ -52,11 +53,13 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestToggleSeeking() { - AddStep("allow seeking", () => progress.AllowSeeking.Value = true); - AddStep("hide graph", () => progress.ShowGraph.Value = false); - AddStep("disallow seeking", () => progress.AllowSeeking.Value = false); - AddStep("allow seeking", () => progress.AllowSeeking.Value = true); - AddStep("show graph", () => progress.ShowGraph.Value = true); + DefaultSongProgress getDefaultProgress() => this.ChildrenOfType().Single(); + + AddStep("allow seeking", () => getDefaultProgress().AllowSeeking.Value = true); + AddStep("hide graph", () => getDefaultProgress().ShowGraph.Value = false); + AddStep("disallow seeking", () => getDefaultProgress().AllowSeeking.Value = false); + AddStep("allow seeking", () => getDefaultProgress().AllowSeeking.Value = true); + AddStep("show graph", () => getDefaultProgress().ShowGraph.Value = true); } private void setHitObjects() diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index 654884c0d5..ac184c6407 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -19,8 +19,6 @@ namespace osu.Game.Screens.Play.HUD { public class DefaultSongProgress : SongProgress { - public const float MAX_HEIGHT = info_height + bottom_bar_height + graph_height + handle_height; - private const float info_height = 20; private const float bottom_bar_height = 5; private const float graph_height = SquareGraph.Column.WIDTH * 6; From d5e5761892138216f2d55ff0e6b6bc6dcaec8a59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jul 2022 18:25:24 +0900 Subject: [PATCH 19/27] Fix `DefaultSongProgress` graph not resetting if time is in intro --- osu.Game/Screens/Play/HUD/DefaultSongProgress.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index ac184c6407..36b172cb44 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -185,7 +185,9 @@ namespace osu.Game.Screens.Play.HUD { bar.CurrentTime = gameplayClock?.CurrentTime ?? Time.Current; - if (!isIntro) + if (isIntro) + graph.Progress = 0; + else graph.Progress = (int)(graph.ColumnCount * progress); } From bfa026879c342fa1c44c46ac6cae6d5a83551252 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jul 2022 18:28:03 +0900 Subject: [PATCH 20/27] Remove pointless null check --- osu.Game/Skinning/LegacySongProgress.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index 0d1110df47..963209d4ce 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -15,7 +15,7 @@ namespace osu.Game.Skinning { public class LegacySongProgress : SongProgress { - private CircularProgress? pie; + private CircularProgress pie = null!; [BackgroundDependencyLoader] private void load() @@ -72,9 +72,6 @@ namespace osu.Game.Skinning protected override void UpdateProgress(double progress, bool isIntro) { - if (pie == null) - return; - if (isIntro) { pie.Scale = new Vector2(-1, 1); From ea027eda46ebdb8ed8b13674fcbbdca3b96360c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jul 2022 18:29:49 +0900 Subject: [PATCH 21/27] Move initial show to base implementation and add transition for legacy version --- osu.Game/Screens/Play/HUD/DefaultSongProgress.cs | 2 -- osu.Game/Screens/Play/HUD/SongProgress.cs | 9 ++++++++- osu.Game/Skinning/LegacySongProgress.cs | 8 ++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index 36b172cb44..b4eade0709 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -104,8 +104,6 @@ namespace osu.Game.Screens.Play.HUD protected override void LoadComplete() { - Show(); - AllowSeeking.BindValueChanged(_ => updateBarVisibility(), true); ShowGraph.BindValueChanged(_ => updateGraphVisibility(), true); diff --git a/osu.Game/Screens/Play/HUD/SongProgress.cs b/osu.Game/Screens/Play/HUD/SongProgress.cs index 78f0142dba..35847b4b16 100644 --- a/osu.Game/Screens/Play/HUD/SongProgress.cs +++ b/osu.Game/Screens/Play/HUD/SongProgress.cs @@ -36,13 +36,20 @@ namespace osu.Game.Screens.Play.HUD } } + protected override void LoadComplete() + { + base.LoadComplete(); + + Show(); + } + protected double FirstHitTime { get; private set; } //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). protected double LastHitTime { get; private set; } protected abstract void UpdateProgress(double progress, bool isIntro); - protected abstract void UpdateObjects(IEnumerable? objects); + protected virtual void UpdateObjects(IEnumerable? objects) { } [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index 963209d4ce..3fba0e5abe 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -1,13 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Game.Rulesets.Objects; using osu.Game.Screens.Play.HUD; using osuTK; @@ -60,14 +58,12 @@ namespace osu.Game.Skinning protected override void PopIn() { + this.FadeIn(500, Easing.OutQuint); } protected override void PopOut() { - } - - protected override void UpdateObjects(IEnumerable? objects) - { + this.FadeOut(100); } protected override void UpdateProgress(double progress, bool isIntro) From 86c2b7e449ed0dd670480e9826f46b695fa4113d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jul 2022 18:37:17 +0900 Subject: [PATCH 22/27] Apply nullability to `DefaultSongProgress` and clean up more stuff --- .../Screens/Play/HUD/DefaultSongProgress.cs | 65 ++++++++----------- osu.Game/Screens/Play/HUD/SongProgress.cs | 31 +++++---- 2 files changed, 46 insertions(+), 50 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index b4eade0709..7b9453f2ed 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -1,9 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -32,8 +29,6 @@ namespace osu.Game.Screens.Play.HUD private readonly SongProgressGraph graph; private readonly SongProgressInfo info; - public Action RequestSeek; - /// /// Whether seeking is allowed and the progress bar should be shown. /// @@ -47,14 +42,20 @@ namespace osu.Game.Screens.Play.HUD protected override bool BlockScrollInput => false; - [Resolved(canBeNull: true)] - private GameplayClock gameplayClock { get; set; } + [Resolved] + private GameplayClock? gameplayClock { get; set; } - [Resolved(canBeNull: true)] - private Player player { get; set; } + [Resolved] + private Player? player { get; set; } - [Resolved(canBeNull: true)] - private DrawableRuleset drawableRuleset { get; set; } + [Resolved] + private DrawableRuleset? drawableRuleset { get; set; } + + [Resolved] + private OsuConfigManager config { get; set; } = null!; + + [Resolved] + private SkinManager skinManager { get; set; } = null!; public DefaultSongProgress() { @@ -110,12 +111,6 @@ namespace osu.Game.Screens.Play.HUD migrateSettingFromConfig(); } - [Resolved] - private OsuConfigManager config { get; set; } - - [Resolved] - private SkinManager skinManager { get; set; } - /// /// This setting has been migrated to a per-component level. /// Only take the value from the config if it is in a non-default state (then reset it to default so it only applies once). @@ -131,29 +126,26 @@ namespace osu.Game.Screens.Play.HUD ShowGraph.Value = configShowGraph.Value; // This is pretty ugly, but the only way to make this stick... - if (skinManager != null) + var skinnableTarget = this.FindClosestParent(); + + if (skinnableTarget != null) { - var skinnableTarget = this.FindClosestParent(); + // If the skin is not mutable, a mutable instance will be created, causing this migration logic to run again on the correct skin. + // Therefore we want to avoid resetting the config value on this invocation. + if (skinManager.EnsureMutableSkin()) + return; - if (skinnableTarget != null) + // If `EnsureMutableSkin` actually changed the skin, default layout may take a frame to apply. + // See `SkinnableTargetComponentsContainer`'s use of ScheduleAfterChildren. + ScheduleAfterChildren(() => { - // If the skin is not mutable, a mutable instance will be created, causing this migration logic to run again on the correct skin. - // Therefore we want to avoid resetting the config value on this invocation. - if (skinManager.EnsureMutableSkin()) - return; + var skin = skinManager.CurrentSkin.Value; + skin.UpdateDrawableTarget(skinnableTarget); - // If `EnsureMutableSkin` actually changed the skin, default layout may take a frame to apply. - // See `SkinnableTargetComponentsContainer`'s use of ScheduleAfterChildren. - ScheduleAfterChildren(() => - { - var skin = skinManager.CurrentSkin.Value; - skin.UpdateDrawableTarget(skinnableTarget); + skinManager.Save(skin); + }); - skinManager.Save(skin); - }); - - configShowGraph.SetDefault(); - } + configShowGraph.SetDefault(); } } } @@ -170,8 +162,7 @@ namespace osu.Game.Screens.Play.HUD protected override void UpdateObjects(IEnumerable objects) { - if (objects != null) - graph.Objects = objects; + graph.Objects = objects; info.StartTime = FirstHitTime; info.EndTime = LastHitTime; diff --git a/osu.Game/Screens/Play/HUD/SongProgress.cs b/osu.Game/Screens/Play/HUD/SongProgress.cs index 35847b4b16..aaef141349 100644 --- a/osu.Game/Screens/Play/HUD/SongProgress.cs +++ b/osu.Game/Screens/Play/HUD/SongProgress.cs @@ -30,8 +30,9 @@ namespace osu.Game.Screens.Play.HUD set { objects = value; - FirstHitTime = objects?.FirstOrDefault()?.StartTime ?? 0; - LastHitTime = objects?.LastOrDefault()?.GetEndTime() ?? 0; + FirstHitTime = objects.FirstOrDefault()?.StartTime ?? 0; + //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). + LastHitTime = objects.LastOrDefault()?.GetEndTime() ?? 0; UpdateObjects(objects); } } @@ -45,11 +46,10 @@ namespace osu.Game.Screens.Play.HUD protected double FirstHitTime { get; private set; } - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). protected double LastHitTime { get; private set; } protected abstract void UpdateProgress(double progress, bool isIntro); - protected virtual void UpdateObjects(IEnumerable? objects) { } + protected virtual void UpdateObjects(IEnumerable objects) { } [BackgroundDependencyLoader] private void load() @@ -70,20 +70,25 @@ namespace osu.Game.Screens.Play.HUD // The reference clock is used to accurately tell the playfield's time. This is obtained from the drawable ruleset. // However, if no drawable ruleset is available (i.e. used in tests), we fall back to either the gameplay clock container or this drawable's own clock. - double gameplayTime = referenceClock?.CurrentTime ?? gameplayClockContainer?.GameplayClock.CurrentTime ?? Time.Current; + double currentTime = referenceClock?.CurrentTime ?? gameplayClockContainer?.GameplayClock.CurrentTime ?? Time.Current; - if (gameplayTime < FirstHitTime) + bool isInIntro = currentTime < FirstHitTime; + + if (isInIntro) { - double earliest = gameplayClockContainer?.StartTime ?? 0; - double introDuration = FirstHitTime - earliest; - double currentIntroTime = gameplayTime - earliest; - UpdateProgress(currentIntroTime / introDuration, true); + double introStartTime = gameplayClockContainer?.StartTime ?? 0; + + double introOffsetCurrent = currentTime - introStartTime; + double introDuration = FirstHitTime - introStartTime; + + UpdateProgress(introOffsetCurrent / introDuration, true); } else { - double duration = LastHitTime - FirstHitTime; - double currentTime = gameplayTime - FirstHitTime; - UpdateProgress(currentTime / duration, false); + double objectOffsetCurrent = currentTime - FirstHitTime; + + double objectDuration = LastHitTime - FirstHitTime; + UpdateProgress(objectOffsetCurrent / objectDuration, false); } } } From 4b140e1f5a3fbcccc816eb91c8a3b51f6219342d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jul 2022 18:49:37 +0900 Subject: [PATCH 23/27] Adjust metrics --- osu.Game/Skinning/LegacySongProgress.cs | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Skinning/LegacySongProgress.cs b/osu.Game/Skinning/LegacySongProgress.cs index 3fba0e5abe..f828e301f2 100644 --- a/osu.Game/Skinning/LegacySongProgress.cs +++ b/osu.Game/Skinning/LegacySongProgress.cs @@ -13,22 +13,22 @@ namespace osu.Game.Skinning { public class LegacySongProgress : SongProgress { - private CircularProgress pie = null!; + private CircularProgress circularProgress = null!; [BackgroundDependencyLoader] private void load() { - Size = new Vector2(35); + Size = new Vector2(33); InternalChildren = new Drawable[] { new Container { - Size = new Vector2(0.95f), Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Child = pie = new CircularProgress + Size = new Vector2(0.92f), + Child = circularProgress = new CircularProgress { RelativeSizeAxes = Axes.Both, }, @@ -51,7 +51,7 @@ namespace osu.Game.Skinning Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = Colour4.White, - Size = new Vector2(3), + Size = new Vector2(4), } }; } @@ -70,17 +70,17 @@ namespace osu.Game.Skinning { if (isIntro) { - pie.Scale = new Vector2(-1, 1); - pie.Anchor = Anchor.TopRight; - pie.Colour = new Colour4(199, 255, 47, 153); - pie.Current.Value = 1 - progress; + circularProgress.Scale = new Vector2(-1, 1); + circularProgress.Anchor = Anchor.TopRight; + circularProgress.Colour = new Colour4(199, 255, 47, 153); + circularProgress.Current.Value = 1 - progress; } else { - pie.Scale = new Vector2(1); - pie.Anchor = Anchor.TopLeft; - pie.Colour = new Colour4(255, 255, 255, 153); - pie.Current.Value = progress; + circularProgress.Scale = new Vector2(1); + circularProgress.Anchor = Anchor.TopLeft; + circularProgress.Colour = new Colour4(255, 255, 255, 153); + circularProgress.Current.Value = progress; } } } From e664690fe2051d529861347183784f2e3f2ba550 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jul 2022 22:19:46 +0900 Subject: [PATCH 24/27] Remove unnecessary `LoadTrack` call --- osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index 5e9cf8839d..428c056fc6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -27,7 +27,6 @@ namespace osu.Game.Tests.Visual.Gameplay private void load() { Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - Beatmap.Value.LoadTrack(); Add(gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, skip_target_time)); From 3f72e76348955667bd9e61a26fe5587a14516f9a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Jul 2022 16:12:52 +0300 Subject: [PATCH 25/27] Expose `StartTime` from gameplay clock --- osu.Game/Screens/Play/GameplayClock.cs | 9 +++++++++ osu.Game/Screens/Play/GameplayClockContainer.cs | 16 +++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayClock.cs b/osu.Game/Screens/Play/GameplayClock.cs index e6248014c5..6af795cfd8 100644 --- a/osu.Game/Screens/Play/GameplayClock.cs +++ b/osu.Game/Screens/Play/GameplayClock.cs @@ -35,6 +35,15 @@ namespace osu.Game.Screens.Play UnderlyingClock = underlyingClock; } + /// + /// The time from which the clock should start. Will be seeked to on calling . + /// + /// + /// If not set, a value of zero will be used. + /// Importantly, the value will be inferred from the current ruleset in unless specified. + /// + public double? StartTime { get; internal set; } + public double CurrentTime => UnderlyingClock.CurrentTime; public double Rate => UnderlyingClock.Rate; diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 9396b3311f..7b75decb51 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -44,6 +44,8 @@ namespace osu.Game.Screens.Play /// public event Action OnSeek; + private double? startTime; + /// /// The time from which the clock should start. Will be seeked to on calling . /// @@ -51,7 +53,17 @@ namespace osu.Game.Screens.Play /// If not set, a value of zero will be used. /// Importantly, the value will be inferred from the current ruleset in unless specified. /// - public double? StartTime { get; set; } + public double? StartTime + { + get => startTime; + set + { + startTime = value; + + if (GameplayClock != null) + GameplayClock.StartTime = value; + } + } /// /// Creates a new . @@ -72,6 +84,8 @@ namespace osu.Game.Screens.Play var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs(GameplayClock = CreateGameplayClock(AdjustableSource)); + + GameplayClock.StartTime = StartTime; GameplayClock.IsPaused.BindTo(IsPaused); return dependencies; From 905bbdc8eebe51dacd9a63535996edd7253df358 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Jul 2022 16:17:25 +0300 Subject: [PATCH 26/27] Remove caching of `GameplayClockContainer` in favour of `GameplayClock` Also fixes `SongProgress` being displayed in skin editor on non-gameplay screens, due to `GameplayClock` not marked as a required dependency. --- osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs | 3 +-- osu.Game/Screens/Play/GameplayClockContainer.cs | 1 - osu.Game/Screens/Play/HUD/DefaultSongProgress.cs | 5 +---- osu.Game/Screens/Play/HUD/SongProgress.cs | 8 ++++---- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index 428c056fc6..efd3fae147 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -30,8 +30,7 @@ namespace osu.Game.Tests.Visual.Gameplay Add(gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, skip_target_time)); - Dependencies.CacheAs(gameplayClockContainer); // required for StartTime - Dependencies.CacheAs(gameplayClockContainer.GameplayClock); // required for everything else + Dependencies.CacheAs(gameplayClockContainer.GameplayClock); } [SetUpSteps] diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 7b75decb51..b37d15e06c 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -16,7 +16,6 @@ namespace osu.Game.Screens.Play /// /// Encapsulates gameplay timing logic and provides a via DI for gameplay components to use. /// - [Cached] public abstract class GameplayClockContainer : Container, IAdjustableClock { /// diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index 7b9453f2ed..96a6c56860 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -42,9 +42,6 @@ namespace osu.Game.Screens.Play.HUD protected override bool BlockScrollInput => false; - [Resolved] - private GameplayClock? gameplayClock { get; set; } - [Resolved] private Player? player { get; set; } @@ -172,7 +169,7 @@ namespace osu.Game.Screens.Play.HUD protected override void UpdateProgress(double progress, bool isIntro) { - bar.CurrentTime = gameplayClock?.CurrentTime ?? Time.Current; + bar.CurrentTime = GameplayClock.CurrentTime; if (isIntro) graph.Progress = 0; diff --git a/osu.Game/Screens/Play/HUD/SongProgress.cs b/osu.Game/Screens/Play/HUD/SongProgress.cs index aaef141349..702d2f7c6f 100644 --- a/osu.Game/Screens/Play/HUD/SongProgress.cs +++ b/osu.Game/Screens/Play/HUD/SongProgress.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Play.HUD public bool UsesFixedAnchor { get; set; } [Resolved] - private GameplayClockContainer? gameplayClockContainer { get; set; } + protected GameplayClock GameplayClock { get; private set; } = null!; [Resolved(canBeNull: true)] private DrawableRuleset? drawableRuleset { get; set; } @@ -69,14 +69,14 @@ namespace osu.Game.Screens.Play.HUD return; // The reference clock is used to accurately tell the playfield's time. This is obtained from the drawable ruleset. - // However, if no drawable ruleset is available (i.e. used in tests), we fall back to either the gameplay clock container or this drawable's own clock. - double currentTime = referenceClock?.CurrentTime ?? gameplayClockContainer?.GameplayClock.CurrentTime ?? Time.Current; + // However, if no drawable ruleset is available (i.e. used in tests), we fall back to the gameplay clock. + double currentTime = referenceClock?.CurrentTime ?? GameplayClock.CurrentTime; bool isInIntro = currentTime < FirstHitTime; if (isInIntro) { - double introStartTime = gameplayClockContainer?.StartTime ?? 0; + double introStartTime = GameplayClock.StartTime ?? 0; double introOffsetCurrent = currentTime - introStartTime; double introDuration = FirstHitTime - introStartTime; From 3b1a76b1906d0b99f8690ed8cda037a94e870f53 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Jul 2022 15:40:56 +0300 Subject: [PATCH 27/27] Remove redundant/overwritten specifications --- .../Visual/Gameplay/TestSceneSongProgress.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index efd3fae147..9eb71b9cf7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -69,17 +69,8 @@ namespace osu.Game.Tests.Visual.Gameplay this.ChildrenOfType().ForEach(progress => progress.Objects = objects); } - protected override Drawable CreateDefaultImplementation() => new DefaultSongProgress - { - RelativeSizeAxes = Axes.X, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - }; + protected override Drawable CreateDefaultImplementation() => new DefaultSongProgress(); - protected override Drawable CreateLegacyImplementation() => new LegacySongProgress - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; + protected override Drawable CreateLegacyImplementation() => new LegacySongProgress(); } }