1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-16 21:42:55 +08:00

Merge pull request #684 from peppy/song-select-performance

Song select performance tweaks
This commit is contained in:
Dan Balasescu 2017-05-01 11:43:38 +09:00 committed by GitHub
commit 8df4459b1f
6 changed files with 229 additions and 195 deletions

View File

@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps.Drawables
/// <summary> /// <summary>
/// Fires when one of our difficulties was selected. Will fire on first expand. /// Fires when one of our difficulties was selected. Will fire on first expand.
/// </summary> /// </summary>
public Action<BeatmapGroup, BeatmapInfo> SelectionChanged; public Action<BeatmapGroup, BeatmapPanel> SelectionChanged;
/// <summary> /// <summary>
/// Fires when one of our difficulties is clicked when already selected. Should start playing the map. /// Fires when one of our difficulties is clicked when already selected. Should start playing the map.
@ -89,8 +89,6 @@ namespace osu.Game.Beatmaps.Drawables
//we want to make sure one of our children is selected in the case none have been selected yet. //we want to make sure one of our children is selected in the case none have been selected yet.
if (SelectedPanel == null) if (SelectedPanel == null)
BeatmapPanels.First().State = PanelSelectedState.Selected; BeatmapPanels.First().State = PanelSelectedState.Selected;
else
SelectionChanged?.Invoke(this, SelectedPanel.Beatmap);
} }
private void panelGainedSelection(BeatmapPanel panel) private void panelGainedSelection(BeatmapPanel panel)
@ -106,7 +104,7 @@ namespace osu.Game.Beatmaps.Drawables
finally finally
{ {
State = BeatmapGroupState.Expanded; State = BeatmapGroupState.Expanded;
SelectionChanged?.Invoke(this, panel.Beatmap); SelectionChanged?.Invoke(this, SelectedPanel);
} }
} }
} }

View File

@ -94,6 +94,9 @@ namespace osu.Game.Beatmaps
{ {
if (track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) if (track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
other.track = track; other.track = track;
if (background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo))
other.background = background;
} }
public virtual void Dispose() public virtual void Dispose()

View File

@ -93,5 +93,9 @@ namespace osu.Game.Database
public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
BeatmapSet.Path == other.BeatmapSet.Path && BeatmapSet.Path == other.BeatmapSet.Path &&
(Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile; (Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile;
public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
BeatmapSet.Path == other.BeatmapSet.Path &&
(Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile;
} }
} }

View File

@ -120,7 +120,7 @@ namespace osu.Game.Screens.Select
public void RemoveBeatmap(BeatmapSetInfo info) => removeGroup(groups.Find(b => b.BeatmapSet.ID == info.ID)); public void RemoveBeatmap(BeatmapSetInfo info) => removeGroup(groups.Find(b => b.BeatmapSet.ID == info.ID));
public Action<BeatmapGroup, BeatmapInfo> SelectionChanged; public Action<BeatmapInfo> SelectionChanged;
public Action StartRequested; public Action StartRequested;
@ -230,7 +230,7 @@ namespace osu.Game.Screens.Select
return new BeatmapGroup(beatmapSet, database) return new BeatmapGroup(beatmapSet, database)
{ {
SelectionChanged = SelectionChanged, SelectionChanged = (g, p) => selectGroup(g, p),
StartRequested = b => StartRequested?.Invoke(), StartRequested = b => StartRequested?.Invoke(),
State = BeatmapGroupState.Collapsed State = BeatmapGroupState.Collapsed
}; };
@ -328,21 +328,31 @@ namespace osu.Game.Screens.Select
private void selectGroup(BeatmapGroup group, BeatmapPanel panel = null, bool animated = true) private void selectGroup(BeatmapGroup group, BeatmapPanel panel = null, bool animated = true)
{ {
if (panel == null) try
panel = group.BeatmapPanels.First(); {
if (panel == null)
panel = group.BeatmapPanels.First();
Trace.Assert(group.BeatmapPanels.Contains(panel), @"Selected panel must be in provided group"); if (selectedPanel == panel) return;
if (selectedGroup != null && selectedGroup != group && selectedGroup.State != BeatmapGroupState.Hidden) Trace.Assert(group.BeatmapPanels.Contains(panel), @"Selected panel must be in provided group");
selectedGroup.State = BeatmapGroupState.Collapsed;
group.State = BeatmapGroupState.Expanded; if (selectedGroup != null && selectedGroup != group && selectedGroup.State != BeatmapGroupState.Hidden)
selectedGroup = group; selectedGroup.State = BeatmapGroupState.Collapsed;
panel.State = PanelSelectedState.Selected;
selectedPanel = panel;
float selectedY = computeYPositions(animated); group.State = BeatmapGroupState.Expanded;
ScrollTo(selectedY, animated); panel.State = PanelSelectedState.Selected;
selectedPanel = panel;
selectedGroup = group;
SelectionChanged?.Invoke(panel.Beatmap);
}
finally
{
float selectedY = computeYPositions(animated);
ScrollTo(selectedY, animated);
}
} }
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)

View File

@ -74,156 +74,21 @@ namespace osu.Game.Screens.Select
} }
State = Visibility.Visible; State = Visibility.Visible;
var lastContainer = beatmapInfoContainer;
float newDepth = lastContainer?.Depth + 1 ?? 0;
BeatmapInfo beatmapInfo = beatmap.BeatmapInfo;
BeatmapMetadata metadata = beatmap.BeatmapInfo?.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
List<InfoLabel> labels = new List<InfoLabel>();
if (beatmap.Beatmap != null)
{
HitObject lastObject = beatmap.Beatmap.HitObjects.LastOrDefault();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "Length",
Icon = FontAwesome.fa_clock_o,
Content = beatmap.Beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
}));
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "BPM",
Icon = FontAwesome.fa_circle,
Content = getBPMRange(beatmap.Beatmap),
}));
//get statistics fromt he current ruleset.
labels.AddRange(beatmap.BeatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(beatmap).Select(s => new InfoLabel(s)));
}
AlwaysPresent = true; AlwaysPresent = true;
var lastContainer = beatmapInfoContainer;
float newDepth = lastContainer?.Depth + 1 ?? 0;
Add(beatmapInfoContainer = new AsyncLoadWrapper( Add(beatmapInfoContainer = new AsyncLoadWrapper(
new BufferedContainer new BufferedWedgeInfo(beatmap)
{ {
Shear = -Shear,
OnLoadComplete = d => OnLoadComplete = d =>
{ {
FadeIn(250); FadeIn(250);
lastContainer?.FadeOut(250); lastContainer?.FadeOut(250);
lastContainer?.Expire(); lastContainer?.Expire();
},
PixelSnapping = true,
CacheDrawnFrameBuffer = true,
Shear = -Shear,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
// We will create the white-to-black gradient by modulating transparency and having
// a black backdrop. This results in an sRGB-space gradient and not linear space,
// transitioning from white to black more perceptually uniformly.
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
// We use a container, such that we can set the colour gradient to go across the
// vertices of the masked container instead of the vertices of the (larger) sprite.
new Container
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)),
Children = new[]
{
// Zoomed-in and cropped beatmap background
new BeatmapBackgroundSprite(beatmap)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
},
},
},
new DifficultyColourBar(beatmap.BeatmapInfo)
{
RelativeSizeAxes = Axes.Y,
Width = 20,
},
new FillFlowContainer
{
Name = "Top-aligned metadata",
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 10, Left = 25, Right = 10, Bottom = 20 },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = beatmapInfo.Version,
TextSize = 24,
},
}
},
new FillFlowContainer
{
Name = "Bottom-aligned metadata",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 15, Left = 25, Right = 10, Bottom = 20 },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = !string.IsNullOrEmpty(metadata.Source) ? metadata.Source + " — " + metadata.Title : metadata.Title,
TextSize = 28,
},
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = metadata.Artist,
TextSize = 17,
},
new FillFlowContainer
{
Margin = new MarginPadding { Top = 10 },
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Children = new[]
{
new OsuSpriteText
{
Font = @"Exo2.0-Medium",
Text = "mapped by ",
TextSize = 15,
},
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = metadata.Author,
TextSize = 15,
},
}
},
new FillFlowContainer
{
Margin = new MarginPadding { Top = 20, Left = 10 },
Spacing = new Vector2(40, 0),
AutoSizeAxes = Axes.Both,
Children = labels
},
}
},
} }
}) })
{ {
@ -231,23 +96,164 @@ namespace osu.Game.Screens.Select
}); });
} }
private string getBPMRange(Beatmap beatmap) public class BufferedWedgeInfo : BufferedContainer
{ {
double bpmMax = beatmap.TimingInfo.BPMMaximum; public BufferedWedgeInfo(WorkingBeatmap beatmap)
double bpmMin = beatmap.TimingInfo.BPMMinimum;
if (Precision.AlmostEquals(bpmMin, bpmMax)) return Math.Round(bpmMin) + "bpm";
return Math.Round(bpmMin) + "-" + Math.Round(bpmMax) + "bpm (mostly " + Math.Round(beatmap.TimingInfo.BPMMode) + "bpm)";
}
public class InfoLabel : Container
{
public InfoLabel(BeatmapStatistic statistic)
{ {
AutoSizeAxes = Axes.Both; BeatmapInfo beatmapInfo = beatmap.BeatmapInfo;
BeatmapMetadata metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
List<InfoLabel> labels = new List<InfoLabel>();
if (beatmap.Beatmap != null)
{
HitObject lastObject = beatmap.Beatmap.HitObjects.LastOrDefault();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "Length",
Icon = FontAwesome.fa_clock_o,
Content = beatmap.Beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
}));
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "BPM",
Icon = FontAwesome.fa_circle,
Content = getBPMRange(beatmap.Beatmap),
}));
//get statistics fromt he current ruleset.
labels.AddRange(beatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(beatmap).Select(s => new InfoLabel(s)));
}
PixelSnapping = true;
CacheDrawnFrameBuffer = true;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[] Children = new Drawable[]
{ {
// We will create the white-to-black gradient by modulating transparency and having
// a black backdrop. This results in an sRGB-space gradient and not linear space,
// transitioning from white to black more perceptually uniformly.
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
// We use a container, such that we can set the colour gradient to go across the
// vertices of the masked container instead of the vertices of the (larger) sprite.
new Container
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)),
Children = new[]
{
// Zoomed-in and cropped beatmap background
new BeatmapBackgroundSprite(beatmap)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
},
},
},
new DifficultyColourBar(beatmap.BeatmapInfo)
{
RelativeSizeAxes = Axes.Y,
Width = 20,
},
new FillFlowContainer
{
Name = "Top-aligned metadata",
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 10, Left = 25, Right = 10, Bottom = 20 },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = beatmapInfo.Version,
TextSize = 24,
},
}
},
new FillFlowContainer
{
Name = "Bottom-aligned metadata",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 15, Left = 25, Right = 10, Bottom = 20 },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = !string.IsNullOrEmpty(metadata.Source) ? metadata.Source + " — " + metadata.Title : metadata.Title,
TextSize = 28,
},
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = metadata.Artist,
TextSize = 17,
},
new FillFlowContainer
{
Margin = new MarginPadding { Top = 10 },
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Children = new[]
{
new OsuSpriteText
{
Font = @"Exo2.0-Medium",
Text = "mapped by ",
TextSize = 15,
},
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = metadata.Author,
TextSize = 15,
},
}
},
new FillFlowContainer
{
Margin = new MarginPadding { Top = 20, Left = 10 },
Spacing = new Vector2(40, 0),
AutoSizeAxes = Axes.Both,
Children = labels
},
}
},
};
}
private string getBPMRange(Beatmap beatmap)
{
double bpmMax = beatmap.TimingInfo.BPMMaximum;
double bpmMin = beatmap.TimingInfo.BPMMinimum;
if (Precision.AlmostEquals(bpmMin, bpmMax)) return Math.Round(bpmMin) + "bpm";
return Math.Round(bpmMin) + "-" + Math.Round(bpmMax) + "bpm (mostly " + Math.Round(beatmap.TimingInfo.BPMMode) + "bpm)";
}
public class InfoLabel : Container
{
public InfoLabel(BeatmapStatistic statistic)
{
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new TextAwesome new TextAwesome
{ {
Icon = FontAwesome.fa_square, Icon = FontAwesome.fa_square,
@ -273,23 +279,23 @@ namespace osu.Game.Screens.Select
TextSize = 17, TextSize = 17,
Origin = Anchor.CentreLeft Origin = Anchor.CentreLeft
}, },
}; };
} }
}
private class DifficultyColourBar : DifficultyColouredContainer
{
public DifficultyColourBar(BeatmapInfo beatmap) : base(beatmap)
{
} }
[BackgroundDependencyLoader] private class DifficultyColourBar : DifficultyColouredContainer
private void load()
{ {
const float full_opacity_ratio = 0.7f; public DifficultyColourBar(BeatmapInfo beatmap) : base(beatmap)
Children = new Drawable[]
{ {
}
[BackgroundDependencyLoader]
private void load()
{
const float full_opacity_ratio = 0.7f;
Children = new Drawable[]
{
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -305,7 +311,8 @@ namespace osu.Game.Screens.Select
X = full_opacity_ratio, X = full_opacity_ratio,
Width = 1 - full_opacity_ratio, Width = 1 - full_opacity_ratio,
} }
}; };
}
} }
} }
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using System.Threading; using System.Threading;
using OpenTK; using OpenTK;
using OpenTK.Input; using OpenTK.Input;
@ -15,8 +14,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
@ -197,6 +196,12 @@ namespace osu.Game.Screens.Select
private void raiseSelect() private void raiseSelect()
{ {
var pendingSelection = selectionChangedDebounce;
selectionChangedDebounce = null;
pendingSelection?.RunTask();
pendingSelection?.Cancel(); // cancel the already scheduled task.
if (Beatmap == null) return; if (Beatmap == null) return;
OnSelected(); OnSelected();
@ -297,25 +302,37 @@ namespace osu.Game.Screens.Select
carousel.SelectBeatmap(beatmap?.BeatmapInfo); carousel.SelectBeatmap(beatmap?.BeatmapInfo);
} }
private ScheduledDelegate selectionChangedDebounce;
// We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds.
private BeatmapInfo selectionChangeNoBounce;
/// <summary> /// <summary>
/// selection has been changed as the result of interaction with the carousel. /// selection has been changed as the result of interaction with the carousel.
/// </summary> /// </summary>
private void selectionChanged(BeatmapGroup group, BeatmapInfo beatmap) private void selectionChanged(BeatmapInfo beatmap)
{ {
bool beatmapSetChange = false; bool beatmapSetChange = false;
if (!beatmap.Equals(Beatmap?.BeatmapInfo)) if (!beatmap.Equals(Beatmap?.BeatmapInfo))
{ {
if (beatmap.BeatmapSetInfoID == Beatmap?.BeatmapInfo.BeatmapSetInfoID) if (beatmap.BeatmapSetInfoID == selectionChangeNoBounce?.BeatmapSetInfoID)
sampleChangeDifficulty.Play(); sampleChangeDifficulty.Play();
else else
{ {
sampleChangeBeatmap.Play(); sampleChangeBeatmap.Play();
beatmapSetChange = true; beatmapSetChange = true;
} }
Beatmap = database.GetWorkingBeatmap(beatmap, Beatmap);
} }
ensurePlayingSelected(beatmapSetChange);
selectionChangeNoBounce = beatmap;
selectionChangedDebounce?.Cancel();
selectionChangedDebounce = Scheduler.AddDelayed(delegate
{
Beatmap = database.GetWorkingBeatmap(beatmap, Beatmap);
ensurePlayingSelected(beatmapSetChange);
}, 100);
} }
private void ensurePlayingSelected(bool preview = false) private void ensurePlayingSelected(bool preview = false)
@ -331,11 +348,6 @@ namespace osu.Game.Screens.Select
} }
} }
private void selectBeatmap(BeatmapSetInfo beatmapSet = null)
{
carousel.SelectBeatmap(beatmapSet != null ? beatmapSet.Beatmaps.First() : Beatmap?.BeatmapInfo);
}
private void removeBeatmapSet(BeatmapSetInfo beatmapSet) private void removeBeatmapSet(BeatmapSetInfo beatmapSet)
{ {
carousel.RemoveBeatmap(beatmapSet); carousel.RemoveBeatmap(beatmapSet);