1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-28 03:07:19 +08:00

Merge pull request #12845 from peppy/hit-error-skinnable

This commit is contained in:
Bartłomiej Dach 2021-05-19 19:55:27 +02:00 committed by GitHub
commit 73234b8b27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 153 additions and 244 deletions

View File

@ -1,6 +1,9 @@
// 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.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -11,29 +14,35 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Rulesets.Taiko.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Screens.Play.HUD.HitErrorMeters; using osu.Game.Screens.Play.HUD.HitErrorMeters;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestSceneHitErrorMeter : OsuTestScene public class TestSceneHitErrorMeter : OsuTestScene
{ {
private HitWindows hitWindows;
[Cached] [Cached]
private ScoreProcessor scoreProcessor = new ScoreProcessor(); private ScoreProcessor scoreProcessor = new ScoreProcessor();
[Cached(typeof(DrawableRuleset))]
private TestDrawableRuleset drawableRuleset = new TestDrawableRuleset();
public TestSceneHitErrorMeter() public TestSceneHitErrorMeter()
{ {
recreateDisplay(new OsuHitWindows(), 5); recreateDisplay(new OsuHitWindows(), 5);
AddRepeatStep("New random judgement", () => newJudgement(), 40); AddRepeatStep("New random judgement", () => newJudgement(), 40);
AddRepeatStep("New max negative", () => newJudgement(-hitWindows.WindowFor(HitResult.Meh)), 20); AddRepeatStep("New max negative", () => newJudgement(-drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20);
AddRepeatStep("New max positive", () => newJudgement(hitWindows.WindowFor(HitResult.Meh)), 20); AddRepeatStep("New max positive", () => newJudgement(drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20);
AddStep("New fixed judgement (50ms)", () => newJudgement(50)); AddStep("New fixed judgement (50ms)", () => newJudgement(50));
AddStep("Judgement barrage", () => AddStep("Judgement barrage", () =>
@ -83,10 +92,10 @@ namespace osu.Game.Tests.Visual.Gameplay
private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) private void recreateDisplay(HitWindows hitWindows, float overallDifficulty)
{ {
this.hitWindows = hitWindows;
hitWindows?.SetDifficulty(overallDifficulty); hitWindows?.SetDifficulty(overallDifficulty);
drawableRuleset.HitWindows = hitWindows;
Clear(); Clear();
Add(new FillFlowContainer Add(new FillFlowContainer
@ -103,40 +112,40 @@ namespace osu.Game.Tests.Visual.Gameplay
} }
}); });
Add(new BarHitErrorMeter(hitWindows, true) Add(new BarHitErrorMeter
{ {
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
}); });
Add(new BarHitErrorMeter(hitWindows, false) Add(new BarHitErrorMeter
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
}); });
Add(new BarHitErrorMeter(hitWindows, true) Add(new BarHitErrorMeter
{ {
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Rotation = 270, Rotation = 270,
}); });
Add(new ColourHitErrorMeter(hitWindows) Add(new ColourHitErrorMeter
{ {
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
Margin = new MarginPadding { Right = 50 } Margin = new MarginPadding { Right = 50 }
}); });
Add(new ColourHitErrorMeter(hitWindows) Add(new ColourHitErrorMeter
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Margin = new MarginPadding { Left = 50 } Margin = new MarginPadding { Left = 50 }
}); });
Add(new ColourHitErrorMeter(hitWindows) Add(new ColourHitErrorMeter
{ {
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
@ -147,11 +156,47 @@ namespace osu.Game.Tests.Visual.Gameplay
private void newJudgement(double offset = 0) private void newJudgement(double offset = 0)
{ {
scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = hitWindows }, new Judgement()) scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = drawableRuleset.HitWindows }, new Judgement())
{ {
TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset, TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset,
Type = HitResult.Perfect, Type = HitResult.Perfect,
}); });
} }
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
private class TestDrawableRuleset : DrawableRuleset
{
public HitWindows HitWindows;
public override IEnumerable<HitObject> Objects => new[] { new HitCircle { HitWindows = HitWindows } };
public override event Action<JudgementResult> NewResult;
public override event Action<JudgementResult> RevertResult;
public override Playfield Playfield { get; }
public override Container Overlays { get; }
public override Container FrameStableComponents { get; }
public override IFrameStableClock FrameStableClock { get; }
public override IReadOnlyList<Mod> Mods { get; }
public override double GameplayStartTime { get; }
public override GameplayCursorContainer Cursor { get; }
public TestDrawableRuleset()
: base(new OsuRuleset())
{
// won't compile without this.
NewResult?.Invoke(null);
RevertResult?.Invoke(null);
}
public override void SetReplayScore(Score replayScore) => throw new NotImplementedException();
public override void SetRecordTarget(Score score) => throw new NotImplementedException();
public override void RequestResume(Action continueResume) => throw new NotImplementedException();
public override void CancelResume() => throw new NotImplementedException();
}
} }
} }

View File

@ -104,7 +104,6 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.KeyOverlay, false); SetDefault(OsuSetting.KeyOverlay, false);
SetDefault(OsuSetting.PositionalHitSounds, true); SetDefault(OsuSetting.PositionalHitSounds, true);
SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true); SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true);
SetDefault(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth);
SetDefault(OsuSetting.FloatingComments, false); SetDefault(OsuSetting.FloatingComments, false);
@ -213,7 +212,6 @@ namespace osu.Game.Configuration
KeyOverlay, KeyOverlay,
PositionalHitSounds, PositionalHitSounds,
AlwaysPlayFirstComboBreak, AlwaysPlayFirstComboBreak,
ScoreMeter,
FloatingComments, FloatingComments,
HUDVisibilityMode, HUDVisibilityMode,
ShowProgressGraph, ShowProgressGraph,

View File

@ -1,37 +0,0 @@
// 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.ComponentModel;
namespace osu.Game.Configuration
{
public enum ScoreMeterType
{
[Description("None")]
None,
[Description("Hit Error (left)")]
HitErrorLeft,
[Description("Hit Error (right)")]
HitErrorRight,
[Description("Hit Error (left+right)")]
HitErrorBoth,
[Description("Hit Error (bottom)")]
HitErrorBottom,
[Description("Colour (left)")]
ColourLeft,
[Description("Colour (right)")]
ColourRight,
[Description("Colour (left+right)")]
ColourBoth,
[Description("Colour (bottom)")]
ColourBottom,
}
}

View File

@ -73,11 +73,6 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
LabelText = "Always play first combo break sound", LabelText = "Always play first combo break sound",
Current = config.GetBindable<bool>(OsuSetting.AlwaysPlayFirstComboBreak) Current = config.GetBindable<bool>(OsuSetting.AlwaysPlayFirstComboBreak)
}, },
new SettingsEnumDropdown<ScoreMeterType>
{
LabelText = "Score meter type",
Current = config.GetBindable<ScoreMeterType>(OsuSetting.ScoreMeter)
},
new SettingsEnumDropdown<ScoringMode> new SettingsEnumDropdown<ScoringMode>
{ {
LabelText = "Score display mode", LabelText = "Score display mode",

View File

@ -1,127 +0,0 @@
// 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.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
namespace osu.Game.Screens.Play.HUD
{
public class HitErrorDisplay : Container<HitErrorMeter>
{
private const int fade_duration = 200;
private const int margin = 10;
private readonly Bindable<ScoreMeterType> type = new Bindable<ScoreMeterType>();
private readonly HitWindows hitWindows;
public HitErrorDisplay(HitWindows hitWindows)
{
this.hitWindows = hitWindows;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
config.BindWith(OsuSetting.ScoreMeter, type);
}
protected override void LoadComplete()
{
base.LoadComplete();
type.BindValueChanged(typeChanged, true);
}
private void typeChanged(ValueChangedEvent<ScoreMeterType> type)
{
Children.ForEach(c => c.FadeOut(fade_duration, Easing.OutQuint));
if (hitWindows == null)
return;
switch (type.NewValue)
{
case ScoreMeterType.HitErrorBoth:
createBar(Anchor.CentreLeft);
createBar(Anchor.CentreRight);
break;
case ScoreMeterType.HitErrorLeft:
createBar(Anchor.CentreLeft);
break;
case ScoreMeterType.HitErrorRight:
createBar(Anchor.CentreRight);
break;
case ScoreMeterType.HitErrorBottom:
createBar(Anchor.BottomCentre);
break;
case ScoreMeterType.ColourBoth:
createColour(Anchor.CentreLeft);
createColour(Anchor.CentreRight);
break;
case ScoreMeterType.ColourLeft:
createColour(Anchor.CentreLeft);
break;
case ScoreMeterType.ColourRight:
createColour(Anchor.CentreRight);
break;
case ScoreMeterType.ColourBottom:
createColour(Anchor.BottomCentre);
break;
}
}
private void createBar(Anchor anchor)
{
bool rightAligned = (anchor & Anchor.x2) > 0;
bool bottomAligned = (anchor & Anchor.y2) > 0;
var display = new BarHitErrorMeter(hitWindows, rightAligned)
{
Margin = new MarginPadding(margin),
Anchor = anchor,
Origin = bottomAligned ? Anchor.CentreLeft : anchor,
Alpha = 0,
Rotation = bottomAligned ? 270 : 0
};
completeDisplayLoading(display);
}
private void createColour(Anchor anchor)
{
bool bottomAligned = (anchor & Anchor.y2) > 0;
var display = new ColourHitErrorMeter(hitWindows)
{
Margin = new MarginPadding(margin),
Anchor = anchor,
Origin = bottomAligned ? Anchor.CentreLeft : anchor,
Alpha = 0,
Rotation = bottomAligned ? 270 : 0
};
completeDisplayLoading(display);
}
private void completeDisplayLoading(HitErrorMeter display)
{
Add(display);
display.FadeInFromZero(fade_duration, Easing.OutQuint);
}
}
}

View File

@ -20,8 +20,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
{ {
public class BarHitErrorMeter : HitErrorMeter public class BarHitErrorMeter : HitErrorMeter
{ {
private readonly Anchor alignment;
private const int arrow_move_duration = 400; private const int arrow_move_duration = 400;
private const int judgement_line_width = 6; private const int judgement_line_width = 6;
@ -43,11 +41,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
private double maxHitWindow; private double maxHitWindow;
public BarHitErrorMeter(HitWindows hitWindows, bool rightAligned = false) public BarHitErrorMeter()
: base(hitWindows)
{ {
alignment = rightAligned ? Anchor.x0 : Anchor.x2;
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
} }
@ -63,33 +58,42 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
Margin = new MarginPadding(2), Margin = new MarginPadding(2),
Children = new Drawable[] Children = new Drawable[]
{ {
judgementsContainer = new Container new Container
{ {
Anchor = Anchor.y1 | alignment, Anchor = Anchor.CentreLeft,
Origin = Anchor.y1 | alignment, Origin = Anchor.CentreLeft,
Width = judgement_line_width, Width = chevron_size,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Child = arrow = new SpriteIcon
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Y,
Y = 0.5f,
Icon = FontAwesome.Solid.ChevronRight,
Size = new Vector2(chevron_size),
}
}, },
colourBars = new Container colourBars = new Container
{ {
Width = bar_width, Width = bar_width,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Anchor = Anchor.y1 | alignment, Anchor = Anchor.CentreLeft,
Origin = Anchor.y1 | alignment, Origin = Anchor.CentreLeft,
Children = new Drawable[] Children = new Drawable[]
{ {
colourBarsEarly = new Container colourBarsEarly = new Container
{ {
Anchor = Anchor.y1 | alignment, Anchor = Anchor.CentreLeft,
Origin = alignment, Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Height = 0.5f, Height = 0.5f,
Scale = new Vector2(1, -1), Scale = new Vector2(1, -1),
}, },
colourBarsLate = new Container colourBarsLate = new Container
{ {
Anchor = Anchor.y1 | alignment, Anchor = Anchor.CentreLeft,
Origin = alignment, Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Height = 0.5f, Height = 0.5f,
}, },
@ -115,21 +119,12 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
} }
} }
}, },
new Container judgementsContainer = new Container
{ {
Anchor = Anchor.y1 | alignment, Anchor = Anchor.CentreLeft,
Origin = Anchor.y1 | alignment, Origin = Anchor.CentreLeft,
Width = chevron_size, Width = judgement_line_width,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Child = arrow = new SpriteIcon
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Y,
Y = 0.5f,
Icon = alignment == Anchor.x2 ? FontAwesome.Solid.ChevronRight : FontAwesome.Solid.ChevronLeft,
Size = new Vector2(chevron_size),
}
}, },
} }
}; };
@ -152,19 +147,22 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
{ {
var windows = HitWindows.GetAllAvailableWindows().ToArray(); var windows = HitWindows.GetAllAvailableWindows().ToArray();
maxHitWindow = windows.First().length; // max to avoid div-by-zero.
maxHitWindow = Math.Max(1, windows.First().length);
for (var i = 0; i < windows.Length; i++) for (var i = 0; i < windows.Length; i++)
{ {
var (result, length) = windows[i]; var (result, length) = windows[i];
colourBarsEarly.Add(createColourBar(result, (float)(length / maxHitWindow), i == 0)); var hitWindow = (float)(length / maxHitWindow);
colourBarsLate.Add(createColourBar(result, (float)(length / maxHitWindow), i == 0));
colourBarsEarly.Add(createColourBar(result, hitWindow, i == 0));
colourBarsLate.Add(createColourBar(result, hitWindow, i == 0));
} }
// a little nub to mark the centre point. // a little nub to mark the centre point.
var centre = createColourBar(windows.Last().result, 0.01f); var centre = createColourBar(windows.Last().result, 0.01f);
centre.Anchor = centre.Origin = Anchor.y1 | (alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2); centre.Anchor = centre.Origin = Anchor.CentreLeft;
centre.Width = 2.5f; centre.Width = 2.5f;
colourBars.Add(centre); colourBars.Add(centre);
@ -236,8 +234,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
judgementsContainer.Add(new JudgementLine judgementsContainer.Add(new JudgementLine
{ {
Y = getRelativeJudgementPosition(judgement.TimeOffset), Y = getRelativeJudgementPosition(judgement.TimeOffset),
Anchor = alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2, Origin = Anchor.CentreLeft,
Origin = Anchor.y1 | (alignment == Anchor.x2 ? Anchor.x0 : Anchor.x2),
}); });
arrow.MoveToY( arrow.MoveToY(

View File

@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -19,8 +18,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
private readonly JudgementFlow judgementsFlow; private readonly JudgementFlow judgementsFlow;
public ColourHitErrorMeter(HitWindows hitWindows) public ColourHitErrorMeter()
: base(hitWindows)
{ {
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
InternalChild = judgementsFlow = new JudgementFlow(); InternalChild = judgementsFlow = new JudgementFlow();

View File

@ -6,13 +6,15 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Screens.Play.HUD.HitErrorMeters namespace osu.Game.Screens.Play.HUD.HitErrorMeters
{ {
public abstract class HitErrorMeter : CompositeDrawable public abstract class HitErrorMeter : CompositeDrawable, ISkinnableDrawable
{ {
protected readonly HitWindows HitWindows; protected HitWindows HitWindows { get; private set; }
[Resolved] [Resolved]
private ScoreProcessor processor { get; set; } private ScoreProcessor processor { get; set; }
@ -20,9 +22,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
[Resolved] [Resolved]
private OsuColour colours { get; set; } private OsuColour colours { get; set; }
protected HitErrorMeter(HitWindows hitWindows) [BackgroundDependencyLoader(true)]
private void load(DrawableRuleset drawableRuleset)
{ {
HitWindows = hitWindows; HitWindows = drawableRuleset?.FirstAvailableHitWindows ?? HitWindows.Empty;
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -87,22 +87,10 @@ namespace osu.Game.Screens.Play
visibilityContainer = new Container visibilityContainer = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[] Child = mainComponents = new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents)
{ {
mainComponents = new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents) RelativeSizeAxes = Axes.Both,
{ },
RelativeSizeAxes = Axes.Both,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
// still need to be migrated; a bit more involved.
new HitErrorDisplay(this.drawableRuleset?.FirstAvailableHitWindows),
}
},
}
}, },
topRightElements = new FillFlowContainer topRightElements = new FillFlowContainer
{ {

View File

@ -20,10 +20,14 @@ namespace osu.Game.Screens.Play
{ {
public class SongProgress : OverlayContainer, ISkinnableDrawable public class SongProgress : OverlayContainer, ISkinnableDrawable
{ {
private const int info_height = 20; public const float MAX_HEIGHT = info_height + bottom_bar_height + graph_height + handle_height;
private const int bottom_bar_height = 5;
private const float info_height = 20;
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;
private static readonly Vector2 handle_size = new Vector2(10, 18); private const float handle_height = 18;
private static readonly Vector2 handle_size = new Vector2(10, handle_height);
private const float transition_duration = 200; private const float transition_duration = 200;

View File

@ -14,6 +14,7 @@ using osu.Game.Extensions;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.Screens.Play; 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 osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -78,6 +79,24 @@ namespace osu.Game.Skinning
combo.Position = new Vector2(accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 + horizontal_padding, vertical_offset + 5); combo.Position = new Vector2(accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 + horizontal_padding, vertical_offset + 5);
combo.Anchor = Anchor.TopCentre; combo.Anchor = Anchor.TopCentre;
} }
var hitError = container.OfType<HitErrorMeter>().FirstOrDefault();
if (hitError != null)
{
hitError.Anchor = Anchor.CentreLeft;
hitError.Origin = Anchor.CentreLeft;
}
var hitError2 = container.OfType<HitErrorMeter>().LastOrDefault();
if (hitError2 != null)
{
hitError2.Anchor = Anchor.CentreRight;
hitError2.Scale = new Vector2(-1, 1);
// origin flipped to match scale above.
hitError2.Origin = Anchor.CentreLeft;
}
} }
}) })
{ {
@ -88,6 +107,8 @@ namespace osu.Game.Skinning
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.BarHitErrorMeter)),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.BarHitErrorMeter)),
} }
}; };
@ -114,6 +135,12 @@ namespace osu.Game.Skinning
case HUDSkinComponents.SongProgress: case HUDSkinComponents.SongProgress:
return new SongProgress(); return new SongProgress();
case HUDSkinComponents.BarHitErrorMeter:
return new BarHitErrorMeter();
case HUDSkinComponents.ColourHitErrorMeter:
return new ColourHitErrorMeter();
} }
break; break;

View File

@ -10,5 +10,7 @@ namespace osu.Game.Skinning
AccuracyCounter, AccuracyCounter,
HealthDisplay, HealthDisplay,
SongProgress, SongProgress,
BarHitErrorMeter,
ColourHitErrorMeter,
} }
} }

View File

@ -19,6 +19,7 @@ using osu.Game.IO;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play; 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 osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Skinning namespace osu.Game.Skinning
@ -342,6 +343,20 @@ namespace osu.Game.Skinning
{ {
accuracy.Y = container.ToLocalSpace(score.ScreenSpaceDrawQuad.BottomRight).Y; accuracy.Y = container.ToLocalSpace(score.ScreenSpaceDrawQuad.BottomRight).Y;
} }
var songProgress = container.OfType<SongProgress>().FirstOrDefault();
var hitError = container.OfType<HitErrorMeter>().FirstOrDefault();
if (hitError != null)
{
hitError.Anchor = Anchor.BottomCentre;
hitError.Origin = Anchor.CentreLeft;
hitError.Rotation = -90;
if (songProgress != null)
hitError.Y -= SongProgress.MAX_HEIGHT;
}
}) })
{ {
Children = new[] Children = new[]
@ -352,6 +367,7 @@ namespace osu.Game.Skinning
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)) ?? new DefaultAccuracyCounter(), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)) ?? new DefaultAccuracyCounter(),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)) ?? new DefaultHealthDisplay(), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)) ?? new DefaultHealthDisplay(),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)) ?? new SongProgress(), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)) ?? new SongProgress(),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.BarHitErrorMeter)) ?? new BarHitErrorMeter(),
} }
}; };