1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-28 14:12:55 +08:00

Implement middle panel contents

This commit is contained in:
smoogipoo 2020-03-17 17:25:24 +09:00
parent 3792d3645f
commit 1521f25c96
8 changed files with 661 additions and 0 deletions

View File

@ -0,0 +1,80 @@
// 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;
using System.Collections.Generic;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osu.Game.Screens.Ranking.Expanded.Statistics;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneExpandedPanelMiddleContent : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ExpandedPanelMiddleContent),
typeof(AccuracyCircle),
typeof(AccuracyStatistic),
typeof(ComboStatistic),
typeof(CounterStatistic),
typeof(StarRatingDisplay),
typeof(StatisticDisplay),
typeof(TotalScoreCounter)
};
public TestSceneExpandedPanelMiddleContent()
{
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 700),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#444"),
},
new ExpandedPanelMiddleContent(createTestScore())
}
};
}
private ScoreInfo createTestScore() => new ScoreInfo
{
User = new User
{
Id = 2,
Username = "peppy",
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
TotalScore = 999999,
Accuracy = 0.95,
MaxCombo = 999,
Rank = ScoreRank.S,
Date = DateTimeOffset.Now,
Statistics =
{
{ HitResult.Miss, 1 },
{ HitResult.Meh, 50 },
{ HitResult.Good, 100 },
{ HitResult.Great, 300 },
}
};
}
}

View File

@ -1,15 +1,219 @@
// 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.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Scoring;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osu.Game.Screens.Ranking.Expanded.Statistics;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded
{
public class ExpandedPanelMiddleContent : CompositeDrawable
{
private readonly ScoreInfo score;
private readonly List<StatisticDisplay> statisticDisplays = new List<StatisticDisplay>();
private RollingCounter<long> scoreCounter;
public ExpandedPanelMiddleContent(ScoreInfo score)
{
this.score = score;
RelativeSizeAxes = Axes.Both;
Masking = true;
Padding = new MarginPadding { Vertical = 10, Horizontal = 10 };
}
[BackgroundDependencyLoader]
private void load()
{
var topStatistics = new List<StatisticDisplay>
{
new AccuracyStatistic(score.Accuracy),
new ComboStatistic(score.MaxCombo, true),
new CounterStatistic("pp", (int)(score.PP ?? 0)),
};
var bottomStatistics = new List<StatisticDisplay>();
foreach (var stat in score.SortedStatistics)
bottomStatistics.Add(new CounterStatistic(stat.Key.GetDescription(), stat.Value));
statisticDisplays.AddRange(topStatistics);
statisticDisplays.AddRange(bottomStatistics);
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(20),
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = new LocalisedString((score.Beatmap.Metadata.Title, score.Beatmap.Metadata.TitleUnicode)),
Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold),
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = new LocalisedString((score.Beatmap.Metadata.Artist, score.Beatmap.Metadata.ArtistUnicode)),
Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold)
},
new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Top = 40 },
RelativeSizeAxes = Axes.X,
Height = 230,
Child = new AccuracyCircle(score)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
}
},
scoreCounter = new TotalScoreCounter
{
Margin = new MarginPadding { Top = 0, Bottom = 5 },
Current = { Value = 0 },
Alpha = 0,
AlwaysPresent = true
},
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new StarRatingDisplay(score.Beatmap)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
},
new ModDisplay
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
DisplayUnrankedText = false,
ExpansionMode = ExpansionMode.AlwaysExpanded,
Scale = new Vector2(0.5f),
Current = { Value = score.Mods }
}
}
},
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = score.Beatmap.Version,
Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold),
},
new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12))
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
}.With(t =>
{
t.AddText("mapped by ");
t.AddText(score.UserString, s => s.Font = s.Font.With(weight: FontWeight.SemiBold));
})
}
},
}
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Content = new[] { topStatistics.Cast<Drawable>().ToArray() },
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
}
},
new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Content = new[] { bottomStatistics.Cast<Drawable>().ToArray() },
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
}
}
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
// Score counter value setting must be scheduled so it isn't transferred instantaneously
ScheduleAfterChildren(() =>
{
using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY, true))
{
scoreCounter.FadeIn();
scoreCounter.Current.Value = score.TotalScore;
double delay = 0;
foreach (var stat in statisticDisplays)
{
using (BeginDelayedSequence(delay, true))
stat.Appear();
delay += 200;
}
}
});
}
}
}

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.Globalization;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Ranking.Expanded
{
public class StarRatingDisplay : CompositeDrawable
{
private readonly BeatmapInfo beatmap;
public StarRatingDisplay(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
var starRatingParts = beatmap.StarDifficulty.ToString("0.00", CultureInfo.InvariantCulture).Split('.');
string wholePart = starRatingParts[0];
string fractionPart = starRatingParts[1];
string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
InternalChildren = new Drawable[]
{
new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.ForDifficultyRating(beatmap.DifficultyRating)
},
}
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 8, Vertical = 4 },
Direction = FillDirection.Horizontal,
Spacing = new Vector2(2, 0),
Children = new Drawable[]
{
new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(7),
Icon = FontAwesome.Solid.Star,
Colour = Color4.Black
},
new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black))
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
TextAnchor = Anchor.BottomLeft,
}.With(t =>
{
t.AddText($"{wholePart}", s =>
{
s.Colour = Color4.Black;
s.Font = s.Font.With(size: 14);
s.UseFullGlyphHeight = false;
});
t.AddText($"{separator}{fractionPart}", s =>
{
s.Colour = Color4.Black;
s.Font = s.Font.With(size: 7);
s.UseFullGlyphHeight = false;
});
})
}
}
};
}
}
}

View File

@ -0,0 +1,51 @@
// 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.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osu.Game.Utils;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded.Statistics
{
public class AccuracyStatistic : StatisticDisplay
{
private readonly double accuracy;
private RollingCounter<double> counter;
public AccuracyStatistic(double accuracy)
: base("accuracy")
{
this.accuracy = accuracy;
}
public override void Appear()
{
base.Appear();
counter.Current.Value = accuracy;
}
protected override Drawable CreateContent() => counter = new Counter();
private class Counter : RollingCounter<double>
{
protected override double RollingDuration => AccuracyCircle.ACCURACY_TRANSFORM_DURATION;
protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING;
public Counter()
{
DisplayedCountSpriteText.Font = OsuFont.Torus.With(size: 20, fixedWidth: true);
DisplayedCountSpriteText.Spacing = new Vector2(-2, 0);
}
protected override string FormatCount(double count) => count.FormatAccuracy();
public override void Increment(double amount)
=> Current.Value += amount;
}
}
}

View File

@ -0,0 +1,66 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded.Statistics
{
public class ComboStatistic : CounterStatistic
{
private readonly bool isPerfect;
private Drawable perfectText;
public ComboStatistic(int combo, bool isPerfect)
: base("combo", combo)
{
this.isPerfect = isPerfect;
}
public override void Appear()
{
base.Appear();
if (isPerfect)
{
using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DURATION / 2, true))
perfectText.FadeIn(50);
}
}
protected override Drawable CreateContent()
{
return new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new[]
{
base.CreateContent().With(d =>
{
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
}),
perfectText = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Text = "PERFECT",
Font = OsuFont.Torus.With(size: 11, weight: FontWeight.SemiBold),
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#66FFCC"), Color4Extensions.FromHex("#FF9AD7")),
Alpha = 0,
UseFullGlyphHeight = false,
}
}
};
}
}
}

View File

@ -0,0 +1,48 @@
// 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.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded.Statistics
{
public class CounterStatistic : StatisticDisplay
{
private readonly int count;
private RollingCounter<int> counter;
public CounterStatistic(string header, int count)
: base(header)
{
this.count = count;
}
public override void Appear()
{
base.Appear();
counter.Current.Value = count;
}
protected override Drawable CreateContent() => counter = new Counter();
private class Counter : RollingCounter<int>
{
protected override double RollingDuration => AccuracyCircle.ACCURACY_TRANSFORM_DURATION;
protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING;
public Counter()
{
DisplayedCountSpriteText.Font = OsuFont.Torus.With(size: 20, fixedWidth: true);
DisplayedCountSpriteText.Spacing = new Vector2(-2, 0);
}
public override void Increment(int amount)
=> Current.Value += amount;
}
}
}

View File

@ -0,0 +1,82 @@
// 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.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;
namespace osu.Game.Screens.Ranking.Expanded.Statistics
{
public abstract class StatisticDisplay : CompositeDrawable
{
private readonly string header;
private Drawable content;
protected StatisticDisplay(string header)
{
this.header = header;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new[]
{
new CircularContainer
{
RelativeSizeAxes = Axes.X,
Height = 12,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#222")
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold),
Text = header.ToUpperInvariant(),
}
}
},
new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
Children = new[]
{
content = CreateContent().With(d =>
{
d.Anchor = Anchor.TopCentre;
d.Origin = Anchor.TopCentre;
d.Alpha = 0;
d.AlwaysPresent = true;
}),
}
}
}
};
}
public virtual void Appear() => content.FadeIn(100);
protected abstract Drawable CreateContent();
}
}

View File

@ -0,0 +1,35 @@
// 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.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded
{
public class TotalScoreCounter : RollingCounter<long>
{
protected override double RollingDuration => AccuracyCircle.ACCURACY_TRANSFORM_DURATION;
protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING;
public TotalScoreCounter()
{
// Todo: AutoSize X removed here due to https://github.com/ppy/osu-framework/issues/3369
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
DisplayedCountSpriteText.Anchor = Anchor.TopCentre;
DisplayedCountSpriteText.Origin = Anchor.TopCentre;
DisplayedCountSpriteText.Font = OsuFont.Torus.With(size: 60, weight: FontWeight.Light, fixedWidth: true);
DisplayedCountSpriteText.Spacing = new Vector2(-5, 0);
}
protected override string FormatCount(long count) => count.ToString("N0");
public override void Increment(long amount)
=> Current.Value += amount;
}
}