diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs
index df970c1c46..d596def98a 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs
@@ -6,6 +6,7 @@ using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Testing;
+using osu.Framework.Utils;
using osu.Game.Screens.Play.HUD;
using osu.Game.Users;
using osuTK;
@@ -24,9 +25,8 @@ namespace osu.Game.Tests.Visual.Gameplay
Add(leaderboard = new TestGameplayLeaderboard
{
Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
+ Origin = Anchor.TopCentre,
Scale = new Vector2(2),
- RelativeSizeAxes = Axes.X,
});
}
@@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay
playerScore.Value = 1222333;
});
- AddStep("add player user", () => leaderboard.AddPlayer(playerScore, new User { Username = "You" }));
+ AddStep("add local player", () => leaderboard.Add(createLeaderboardScore(playerScore, "You", true)));
AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v);
}
@@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay
var player2Score = new BindableDouble(1234567);
var player3Score = new BindableDouble(1111111);
- AddStep("add player 2", () => leaderboard.AddPlayer(player2Score, new User { Username = "Player 2" }));
- AddStep("add player 3", () => leaderboard.AddPlayer(player3Score, new User { Username = "Player 3" }));
+ AddStep("add player 2", () => leaderboard.Add(createLeaderboardScore(player2Score, "Player 2")));
+ AddStep("add player 3", () => leaderboard.Add(createLeaderboardScore(player3Score, "Player 3")));
AddAssert("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1));
AddAssert("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2));
@@ -67,6 +67,21 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("is player 2 position #3", () => leaderboard.CheckPositionByUsername("Player 2", 3));
}
+ [Test]
+ public void TestRandomScores()
+ {
+ int playerNumber = 1;
+ AddRepeatStep("add player with random score", () => leaderboard.Add(createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), $"Player {playerNumber++}")), 10);
+ }
+
+ private static GameplayLeaderboardScore createLeaderboardScore(BindableDouble score, string username, bool localOrReplayPlayer = false)
+ {
+ return new GameplayLeaderboardScore(new User { Username = username }, localOrReplayPlayer)
+ {
+ TotalScore = { BindTarget = score },
+ };
+ }
+
private class TestGameplayLeaderboard : GameplayLeaderboard
{
public bool CheckPositionByUsername(string username, int? expectedPosition)
diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs
index 3934a99221..573bf54b14 100644
--- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs
+++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs
@@ -2,11 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
-using JetBrains.Annotations;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Users;
using osuTK;
namespace osu.Game.Screens.Play.HUD
@@ -15,8 +12,7 @@ namespace osu.Game.Screens.Play.HUD
{
public GameplayLeaderboard()
{
- RelativeSizeAxes = Axes.X;
- AutoSizeAxes = Axes.Y;
+ AutoSizeAxes = Axes.Both;
Direction = FillDirection.Vertical;
@@ -26,35 +22,15 @@ namespace osu.Game.Screens.Play.HUD
LayoutEasing = Easing.OutQuint;
}
- ///
- /// Adds a player to the leaderboard.
- ///
- /// The bindable current score of the player.
- /// The player.
- public void AddPlayer([NotNull] IBindableNumber currentScore, [NotNull] User user)
+ public override void Add(GameplayLeaderboardScore drawable)
{
- var scoreItem = addScore(currentScore.Value, user);
- currentScore.ValueChanged += s => scoreItem.TotalScore = s.NewValue;
- }
-
- private GameplayLeaderboardScore addScore(double totalScore, User user)
- {
- var scoreItem = new GameplayLeaderboardScore
- {
- User = user,
- TotalScore = totalScore,
- OnScoreChange = updateScores,
- };
-
- Add(scoreItem);
- updateScores();
-
- return scoreItem;
+ base.Add(drawable);
+ drawable?.TotalScore.BindValueChanged(_ => updateScores(), true);
}
private void updateScores()
{
- var orderedByScore = this.OrderByDescending(i => i.TotalScore).ToList();
+ var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList();
for (int i = 0; i < Count; i++)
{
diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs
index 4c75f422c9..439210c944 100644
--- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs
+++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs
@@ -1,25 +1,34 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
using Humanizer;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
+using osu.Game.Utils;
using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Screens.Play.HUD
{
public class GameplayLeaderboardScore : CompositeDrawable
{
- private readonly OsuSpriteText positionText, positionSymbol, userString;
- private readonly GlowingSpriteText scoreText;
+ private const float regular_width = 215f;
+ private const float extended_width = 235f;
- public Action OnScoreChange;
+ private const float panel_height = 35f;
+
+ private OsuSpriteText positionText, scoreText, accuracyText, comboText, usernameText;
+
+ public readonly BindableDouble TotalScore = new BindableDouble(1000000);
+ public readonly BindableDouble Accuracy = new BindableDouble(1);
+ public readonly BindableInt Combo = new BindableInt();
private int? scorePosition;
@@ -34,103 +43,211 @@ namespace osu.Game.Screens.Play.HUD
positionText.Text = $"#{scorePosition.Value.ToMetric(decimals: scorePosition < 100000 ? 1 : 0)}";
positionText.FadeTo(scorePosition.HasValue ? 1 : 0);
- positionSymbol.FadeTo(scorePosition.HasValue ? 1 : 0);
+ updateColour();
}
}
- private double totalScore;
+ public User User { get; }
- public double TotalScore
+ private readonly bool trackedPlayer;
+
+ private Container mainFillContainer;
+ private Box centralFill;
+
+ ///
+ /// Creates a new .
+ ///
+ /// The score's player.
+ /// Whether the player is the local user or a replay player.
+ public GameplayLeaderboardScore(User user, bool trackedPlayer)
+ {
+ User = user;
+ this.trackedPlayer = trackedPlayer;
+
+ AutoSizeAxes = Axes.X;
+ Height = panel_height;
+
+ Anchor = Anchor.TopRight;
+ Origin = Anchor.TopRight;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ updateColour();
+ FinishTransforms(true);
+ }
+
+ private void updateColour()
+ {
+ if (scorePosition == 1)
+ {
+ mainFillContainer.ResizeWidthTo(extended_width, 200, Easing.OutQuint);
+ panelColour = Color4Extensions.FromHex("7fcc33");
+ textColour = Color4.White;
+ }
+ else if (trackedPlayer)
+ {
+ mainFillContainer.ResizeWidthTo(extended_width, 200, Easing.OutQuint);
+ panelColour = Color4Extensions.FromHex("ffd966");
+ textColour = Color4Extensions.FromHex("2e576b");
+ }
+ else
+ {
+ mainFillContainer.ResizeWidthTo(regular_width, 200, Easing.OutQuint);
+ panelColour = Color4Extensions.FromHex("3399cc");
+ textColour = Color4.White;
+ }
+ }
+
+ private Color4 panelColour
{
- get => totalScore;
set
{
- totalScore = value;
- scoreText.Text = totalScore.ToString("N0");
-
- OnScoreChange?.Invoke();
+ mainFillContainer.FadeColour(value, 200, Easing.OutQuint);
+ centralFill.FadeColour(value, 200, Easing.OutQuint);
}
}
- private User user;
-
- public User User
+ private Color4 textColour
{
- get => user;
set
{
- user = value;
- userString.Text = user?.Username;
+ scoreText.FadeColour(value, 200, Easing.OutQuint);
+ accuracyText.FadeColour(value, 200, Easing.OutQuint);
+ comboText.FadeColour(value, 200, Easing.OutQuint);
+ usernameText.FadeColour(value, 200, Easing.OutQuint);
+ positionText.FadeColour(value, 200, Easing.OutQuint);
}
}
- public GameplayLeaderboardScore()
- {
- RelativeSizeAxes = Axes.X;
- AutoSizeAxes = Axes.Y;
-
- InternalChild = new Container
- {
- Masking = true,
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- AutoSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- new FillFlowContainer
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopRight,
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Margin = new MarginPadding { Right = 2.5f },
- Spacing = new Vector2(2.5f),
- Children = new[]
- {
- positionText = new OsuSpriteText
- {
- Alpha = 0,
- Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
- },
- positionSymbol = new OsuSpriteText
- {
- Alpha = 0,
- Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold),
- Text = ">",
- },
- }
- },
- new FillFlowContainer
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopLeft,
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Margin = new MarginPadding { Left = 2.5f },
- Spacing = new Vector2(2.5f),
- Children = new Drawable[]
- {
- userString = new OsuSpriteText
- {
- Size = new Vector2(80, 16),
- Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
- },
- scoreText = new GlowingSpriteText
- {
- GlowColour = Color4Extensions.FromHex(@"83ccfa"),
- Font = OsuFont.Numeric.With(size: 14),
- }
- }
- },
- },
- };
- }
-
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load()
{
- positionText.Colour = colours.YellowLight;
- positionSymbol.Colour = colours.Yellow;
+ const float panel_shear = 0.15f;
+ const float shear_width = panel_height * panel_shear;
+
+ InternalChildren = new Drawable[]
+ {
+ mainFillContainer = new Container
+ {
+ Width = regular_width,
+ RelativeSizeAxes = Axes.Y,
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Masking = true,
+ CornerRadius = 5f,
+ Shear = new Vector2(panel_shear, 0f),
+ Child = new Box
+ {
+ Alpha = 0.5f,
+ RelativeSizeAxes = Axes.Both,
+ }
+ },
+ new GridContainer
+ {
+ Width = regular_width,
+ RelativeSizeAxes = Axes.Y,
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ ColumnDimensions = new[]
+ {
+ new Dimension(GridSizeMode.Absolute, 35f),
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 85f),
+ },
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ positionText = new OsuSpriteText
+ {
+ Padding = new MarginPadding { Right = shear_width / 2 },
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Colour = Color4.White,
+ Font = OsuFont.Torus.With(size: 14, weight: FontWeight.Bold),
+ Shadow = false,
+ },
+ new Container
+ {
+ Padding = new MarginPadding { Horizontal = shear_width / 3 },
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Masking = true,
+ CornerRadius = 5f,
+ Shear = new Vector2(panel_shear, 0f),
+ RelativeSizeAxes = Axes.Both,
+ Children = new[]
+ {
+ centralFill = new Box
+ {
+ Alpha = 0.5f,
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4Extensions.FromHex("3399cc"),
+ },
+ }
+ },
+ usernameText = new OsuSpriteText
+ {
+ Padding = new MarginPadding { Left = shear_width },
+ RelativeSizeAxes = Axes.X,
+ Width = 0.8f,
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Colour = Color4.White,
+ Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold),
+ Text = User.Username,
+ Truncate = true,
+ Shadow = false,
+ }
+ }
+ },
+ new Container
+ {
+ Padding = new MarginPadding { Top = 2f, Right = 17.5f, Bottom = 5f },
+ RelativeSizeAxes = Axes.Both,
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Colour = Color4.White,
+ Children = new Drawable[]
+ {
+ scoreText = new OsuSpriteText
+ {
+ Spacing = new Vector2(-1f, 0f),
+ Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold, fixedWidth: true),
+ Shadow = false,
+ },
+ accuracyText = new OsuSpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold, fixedWidth: true),
+ Spacing = new Vector2(-1f, 0f),
+ Shadow = false,
+ },
+ comboText = new OsuSpriteText
+ {
+ Anchor = Anchor.BottomRight,
+ Origin = Anchor.BottomRight,
+ Spacing = new Vector2(-1f, 0f),
+ Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold, fixedWidth: true),
+ Shadow = false,
+ },
+ },
+ }
+ }
+ }
+ }
+ };
+
+ TotalScore.BindValueChanged(v => scoreText.Text = v.NewValue.ToString("N0"), true);
+ Accuracy.BindValueChanged(v => accuracyText.Text = v.NewValue.FormatAccuracy(), true);
+ Combo.BindValueChanged(v => comboText.Text = $"{v.NewValue}x", true);
}
}
}