Merge branch 'master' into fix-summary-kiai-ranges

This commit is contained in:
Salman Ahmed
2022-07-29 19:38:28 +03:00
committed by GitHub
12 changed files with 370 additions and 216 deletions

View File

@ -1,161 +1,76 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Framework.Timing;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[TestFixture] [TestFixture]
public class TestSceneSongProgress : OsuTestScene public class TestSceneSongProgress : SkinnableHUDComponentTestScene
{ {
private SongProgress progress; private GameplayClockContainer gameplayClockContainer = null!;
private TestSongProgressGraph graph;
private readonly Container progressContainer;
private readonly StopwatchClock clock; private const double skip_target_time = -2000;
private readonly FramedClock framedClock;
[Cached] [BackgroundDependencyLoader]
private readonly GameplayClock gameplayClock; private void load()
public TestSceneSongProgress()
{ {
clock = new StopwatchClock(); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
gameplayClock = new GameplayClock(framedClock = new FramedClock(clock));
Add(progressContainer = new Container Add(gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, skip_target_time));
{
RelativeSizeAxes = Axes.X, Dependencies.CacheAs(gameplayClockContainer.GameplayClock);
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Height = 100,
Y = -100,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(1),
}
});
} }
[SetUpSteps] [SetUpSteps]
public void SetupSteps() public void SetupSteps()
{ {
AddStep("add new song progress", () => AddStep("reset clock", () => gameplayClockContainer.Reset());
{ AddStep("set hit objects", setHitObjects);
if (progress != null)
{
progress.Expire();
progress = null;
}
progressContainer.Add(progress = new SongProgress
{
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] [Test]
public void TestDisplay() public void TestDisplay()
{ {
AddStep("display max values", displayMaxValues); AddStep("seek to intro", () => gameplayClockContainer.Seek(skip_target_time));
AddUntilStep("wait for graph", () => graph.CreationCount == 1); AddStep("start", gameplayClockContainer.Start);
AddStep("start", clock.Start); AddStep("stop", gameplayClockContainer.Stop);
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);
} }
private void displayRandomValues() [Test]
public void TestToggleSeeking()
{ {
var objects = new List<HitObject>(); DefaultSongProgress getDefaultProgress() => this.ChildrenOfType<DefaultSongProgress>().Single();
for (double i = 0; i < 5000; i += RNG.NextDouble() * 10 + i / 1000)
objects.Add(new HitObject { StartTime = i });
replaceObjects(objects); 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 displayMaxValues() private void setHitObjects()
{ {
var objects = new List<HitObject>(); var objects = new List<HitObject>();
for (double i = 0; i < 5000; i++) for (double i = 0; i < 5000; i++)
objects.Add(new HitObject { StartTime = i }); objects.Add(new HitObject { StartTime = i });
replaceObjects(objects); this.ChildrenOfType<SongProgress>().ForEach(progress => progress.Objects = objects);
} }
private void replaceObjects(List<HitObject> objects) protected override Drawable CreateDefaultImplementation() => new DefaultSongProgress();
{
progress.Objects = objects;
graph.Objects = objects;
progress.RequestSeek = pos => clock.Seek(pos); protected override Drawable CreateLegacyImplementation() => new LegacySongProgress();
}
protected override void Update()
{
base.Update();
framedClock.ProcessFrame();
}
private class TestSongProgressGraph : SongProgressGraph
{
public int CreationCount { get; private set; }
protected override void RecreateGraph()
{
base.RecreateGraph();
CreationCount++;
}
}
} }
} }

View File

@ -0,0 +1,73 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#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<HitObject>();
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++;
}
}
}
}

View File

@ -35,6 +35,15 @@ namespace osu.Game.Screens.Play
UnderlyingClock = underlyingClock; UnderlyingClock = underlyingClock;
} }
/// <summary>
/// The time from which the clock should start. Will be seeked to on calling <see cref="GameplayClockContainer.Reset"/>.
/// </summary>
/// <remarks>
/// If not set, a value of zero will be used.
/// Importantly, the value will be inferred from the current ruleset in <see cref="MasterGameplayClockContainer"/> unless specified.
/// </remarks>
public double? StartTime { get; internal set; }
public double CurrentTime => UnderlyingClock.CurrentTime; public double CurrentTime => UnderlyingClock.CurrentTime;
public double Rate => UnderlyingClock.Rate; public double Rate => UnderlyingClock.Rate;

View File

@ -16,7 +16,6 @@ namespace osu.Game.Screens.Play
/// <summary> /// <summary>
/// Encapsulates gameplay timing logic and provides a <see cref="GameplayClock"/> via DI for gameplay components to use. /// Encapsulates gameplay timing logic and provides a <see cref="GameplayClock"/> via DI for gameplay components to use.
/// </summary> /// </summary>
[Cached]
public abstract class GameplayClockContainer : Container, IAdjustableClock public abstract class GameplayClockContainer : Container, IAdjustableClock
{ {
/// <summary> /// <summary>
@ -44,6 +43,8 @@ namespace osu.Game.Screens.Play
/// </summary> /// </summary>
public event Action OnSeek; public event Action OnSeek;
private double? startTime;
/// <summary> /// <summary>
/// The time from which the clock should start. Will be seeked to on calling <see cref="Reset"/>. /// The time from which the clock should start. Will be seeked to on calling <see cref="Reset"/>.
/// </summary> /// </summary>
@ -51,7 +52,17 @@ namespace osu.Game.Screens.Play
/// If not set, a value of zero will be used. /// If not set, a value of zero will be used.
/// Importantly, the value will be inferred from the current ruleset in <see cref="MasterGameplayClockContainer"/> unless specified. /// Importantly, the value will be inferred from the current ruleset in <see cref="MasterGameplayClockContainer"/> unless specified.
/// </remarks> /// </remarks>
public double? StartTime { get; set; } public double? StartTime
{
get => startTime;
set
{
startTime = value;
if (GameplayClock != null)
GameplayClock.StartTime = value;
}
}
/// <summary> /// <summary>
/// Creates a new <see cref="GameplayClockContainer"/>. /// Creates a new <see cref="GameplayClockContainer"/>.
@ -72,6 +83,8 @@ namespace osu.Game.Screens.Play
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs(GameplayClock = CreateGameplayClock(AdjustableSource)); dependencies.CacheAs(GameplayClock = CreateGameplayClock(AdjustableSource));
GameplayClock.StartTime = StartTime;
GameplayClock.IsPaused.BindTo(IsPaused); GameplayClock.IsPaused.BindTo(IsPaused);
return dependencies; return dependencies;

View File

@ -1,16 +1,10 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -18,12 +12,10 @@ using osu.Game.Rulesets.UI;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; 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;
private const float info_height = 20; private const float info_height = 20;
private const float bottom_bar_height = 5; private const float bottom_bar_height = 5;
private const float graph_height = SquareGraph.Column.WIDTH * 6; private const float graph_height = SquareGraph.Column.WIDTH * 6;
@ -37,8 +29,6 @@ namespace osu.Game.Screens.Play
private readonly SongProgressGraph graph; private readonly SongProgressGraph graph;
private readonly SongProgressInfo info; private readonly SongProgressInfo info;
public Action<double> RequestSeek;
/// <summary> /// <summary>
/// Whether seeking is allowed and the progress bar should be shown. /// Whether seeking is allowed and the progress bar should be shown.
/// </summary> /// </summary>
@ -52,41 +42,19 @@ namespace osu.Game.Screens.Play
protected override bool BlockScrollInput => false; protected override bool BlockScrollInput => false;
private double firstHitTime => objects.First().StartTime; [Resolved]
private Player? player { get; set; }
//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<HitObject> objects;
public IEnumerable<HitObject> 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] [Resolved]
private GameplayClock gameplayClock { get; set; } private DrawableRuleset? drawableRuleset { get; set; }
[Resolved(canBeNull: true)] [Resolved]
private DrawableRuleset drawableRuleset { get; set; } private OsuConfigManager config { get; set; } = null!;
private IClock referenceClock; [Resolved]
private SkinManager skinManager { get; set; } = null!;
public bool UsesFixedAnchor { get; set; } public DefaultSongProgress()
public SongProgress()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Anchor = Anchor.BottomRight; Anchor = Anchor.BottomRight;
@ -127,9 +95,6 @@ namespace osu.Game.Screens.Play
{ {
if (player?.Configuration.AllowUserInteraction == true) if (player?.Configuration.AllowUserInteraction == true)
((IBindable<bool>)AllowSeeking).BindTo(drawableRuleset.HasReplayLoaded); ((IBindable<bool>)AllowSeeking).BindTo(drawableRuleset.HasReplayLoaded);
referenceClock = drawableRuleset.FrameStableClock;
Objects = drawableRuleset.Objects;
} }
graph.FillColour = bar.FillColour = colours.BlueLighter; graph.FillColour = bar.FillColour = colours.BlueLighter;
@ -137,20 +102,12 @@ namespace osu.Game.Screens.Play
protected override void LoadComplete() protected override void LoadComplete()
{ {
Show();
AllowSeeking.BindValueChanged(_ => updateBarVisibility(), true); AllowSeeking.BindValueChanged(_ => updateBarVisibility(), true);
ShowGraph.BindValueChanged(_ => updateGraphVisibility(), true); ShowGraph.BindValueChanged(_ => updateGraphVisibility(), true);
migrateSettingFromConfig(); migrateSettingFromConfig();
} }
[Resolved]
private OsuConfigManager config { get; set; }
[Resolved]
private SkinManager skinManager { get; set; }
/// <summary> /// <summary>
/// This setting has been migrated to a per-component level. /// 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). /// 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).
@ -166,8 +123,6 @@ namespace osu.Game.Screens.Play
ShowGraph.Value = configShowGraph.Value; ShowGraph.Value = configShowGraph.Value;
// This is pretty ugly, but the only way to make this stick... // This is pretty ugly, but the only way to make this stick...
if (skinManager != null)
{
var skinnableTarget = this.FindClosestParent<ISkinnableTarget>(); var skinnableTarget = this.FindClosestParent<ISkinnableTarget>();
if (skinnableTarget != null) if (skinnableTarget != null)
@ -191,7 +146,6 @@ namespace osu.Game.Screens.Play
} }
} }
} }
}
protected override void PopIn() protected override void PopIn()
{ {
@ -203,21 +157,29 @@ namespace osu.Game.Screens.Play
this.FadeOut(100); this.FadeOut(100);
} }
protected override void UpdateObjects(IEnumerable<HitObject> objects)
{
graph.Objects = objects;
info.StartTime = FirstHitTime;
info.EndTime = LastHitTime;
bar.StartTime = FirstHitTime;
bar.EndTime = LastHitTime;
}
protected override void UpdateProgress(double progress, bool isIntro)
{
bar.CurrentTime = GameplayClock.CurrentTime;
if (isIntro)
graph.Progress = 0;
else
graph.Progress = (int)(graph.ColumnCount * progress);
}
protected override void Update() protected override void Update()
{ {
base.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; Height = bottom_bar_height + graph_height + handle_size.Y + info_height - graph.Y;
} }

View File

@ -0,0 +1,95 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
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]
protected GameplayClock GameplayClock { get; private set; } = null!;
[Resolved(canBeNull: true)]
private DrawableRuleset? drawableRuleset { get; set; }
private IClock? referenceClock;
private IEnumerable<HitObject>? objects;
public IEnumerable<HitObject> Objects
{
set
{
objects = value;
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);
}
}
protected override void LoadComplete()
{
base.LoadComplete();
Show();
}
protected double FirstHitTime { get; private set; }
protected double LastHitTime { get; private set; }
protected abstract void UpdateProgress(double progress, bool isIntro);
protected virtual void UpdateObjects(IEnumerable<HitObject> objects) { }
[BackgroundDependencyLoader]
private void load()
{
if (drawableRuleset != null)
{
Objects = drawableRuleset.Objects;
referenceClock = drawableRuleset.FrameStableClock;
}
}
protected override void Update()
{
base.Update();
if (objects == null)
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 the gameplay clock.
double currentTime = referenceClock?.CurrentTime ?? GameplayClock.CurrentTime;
bool isInIntro = currentTime < FirstHitTime;
if (isInIntro)
{
double introStartTime = GameplayClock.StartTime ?? 0;
double introOffsetCurrent = currentTime - introStartTime;
double introDuration = FirstHitTime - introStartTime;
UpdateProgress(introOffsetCurrent / introDuration, true);
}
else
{
double objectOffsetCurrent = currentTime - FirstHitTime;
double objectDuration = LastHitTime - FirstHitTime;
UpdateProgress(objectOffsetCurrent / objectDuration, false);
}
}
}
}

View File

@ -13,7 +13,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Framework.Threading; using osu.Framework.Threading;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play.HUD
{ {
public class SongProgressBar : SliderBar<double> public class SongProgressBar : SliderBar<double>
{ {

View File

@ -8,7 +8,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play.HUD
{ {
public class SongProgressGraph : SquareGraph public class SongProgressGraph : SquareGraph
{ {

View File

@ -10,7 +10,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using System; using System;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play.HUD
{ {
public class SongProgressInfo : Container public class SongProgressInfo : Container
{ {

View File

@ -16,7 +16,6 @@ using osu.Game.Audio;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Extensions; using osu.Game.Extensions;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.HUD.HitErrorMeters; using osu.Game.Screens.Play.HUD.HitErrorMeters;
using osuTK; using osuTK;
@ -147,7 +146,7 @@ namespace osu.Game.Skinning
new DefaultScoreCounter(), new DefaultScoreCounter(),
new DefaultAccuracyCounter(), new DefaultAccuracyCounter(),
new DefaultHealthDisplay(), new DefaultHealthDisplay(),
new SongProgress(), new DefaultSongProgress(),
new BarHitErrorMeter(), new BarHitErrorMeter(),
new BarHitErrorMeter(), new BarHitErrorMeter(),
new PerformancePointsCounter() new PerformancePointsCounter()

View File

@ -16,10 +16,10 @@ using osu.Framework.IO.Stores;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.HUD.HitErrorMeters; using osu.Game.Screens.Play.HUD.HitErrorMeters;
using osuTK.Graphics; using osuTK.Graphics;
@ -337,14 +337,21 @@ namespace osu.Game.Skinning
{ {
var score = container.OfType<LegacyScoreCounter>().FirstOrDefault(); var score = container.OfType<LegacyScoreCounter>().FirstOrDefault();
var accuracy = container.OfType<GameplayAccuracyCounter>().FirstOrDefault(); var accuracy = container.OfType<GameplayAccuracyCounter>().FirstOrDefault();
var combo = container.OfType<LegacyComboCounter>().FirstOrDefault();
if (score != null && accuracy != null) if (score != null && accuracy != null)
{ {
accuracy.Y = container.ToLocalSpace(score.ScreenSpaceDrawQuad.BottomRight).Y; accuracy.Y = container.ToLocalSpace(score.ScreenSpaceDrawQuad.BottomRight).Y;
} }
var songProgress = container.OfType<SongProgress>().FirstOrDefault(); var songProgress = container.OfType<LegacySongProgress>().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<HitErrorMeter>().FirstOrDefault(); var hitError = container.OfType<HitErrorMeter>().FirstOrDefault();
@ -354,12 +361,6 @@ namespace osu.Game.Skinning
hitError.Origin = Anchor.CentreLeft; hitError.Origin = Anchor.CentreLeft;
hitError.Rotation = -90; 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[] Children = new Drawable[]
@ -368,7 +369,7 @@ namespace osu.Game.Skinning
new LegacyScoreCounter(), new LegacyScoreCounter(),
new LegacyAccuracyCounter(), new LegacyAccuracyCounter(),
new LegacyHealthDisplay(), new LegacyHealthDisplay(),
new SongProgress(), new LegacySongProgress(),
new BarHitErrorMeter(), new BarHitErrorMeter(),
} }
}; };

View File

@ -0,0 +1,87 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Screens.Play.HUD;
using osuTK;
namespace osu.Game.Skinning
{
public class LegacySongProgress : SongProgress
{
private CircularProgress circularProgress = null!;
[BackgroundDependencyLoader]
private void load()
{
Size = new Vector2(33);
InternalChildren = new Drawable[]
{
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.92f),
Child = circularProgress = new CircularProgress
{
RelativeSizeAxes = Axes.Both,
},
},
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(4),
}
};
}
protected override void PopIn()
{
this.FadeIn(500, Easing.OutQuint);
}
protected override void PopOut()
{
this.FadeOut(100);
}
protected override void UpdateProgress(double progress, bool isIntro)
{
if (isIntro)
{
circularProgress.Scale = new Vector2(-1, 1);
circularProgress.Anchor = Anchor.TopRight;
circularProgress.Colour = new Colour4(199, 255, 47, 153);
circularProgress.Current.Value = 1 - progress;
}
else
{
circularProgress.Scale = new Vector2(1);
circularProgress.Anchor = Anchor.TopLeft;
circularProgress.Colour = new Colour4(255, 255, 255, 153);
circularProgress.Current.Value = progress;
}
}
}
}