Merge branch 'master' into beatmap-skin

This commit is contained in:
Salman Ahmed
2021-05-19 22:39:32 +03:00
32 changed files with 361 additions and 391 deletions

View File

@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Width = 0.5f, Width = 0.5f,
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 0), _ => new DefaultColumnBackground()) Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
} }
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Width = 0.5f, Width = 0.5f,
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 1), _ => new DefaultColumnBackground()) Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
} }

View File

@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Width = 0.5f, Width = 0.5f,
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, 0), _ => new DefaultKeyArea()) Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}, },
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Width = 0.5f, Width = 0.5f,
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, 1), _ => new DefaultKeyArea()) Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}, },

View File

@ -4,6 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Screens;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Replays; using osu.Game.Replays;
@ -408,6 +409,9 @@ namespace osu.Game.Rulesets.Mania.Tests
judgementResults = new List<JudgementResult>(); judgementResults = new List<JudgementResult>();
}); });
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor?.HasCompleted.Value == true); AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor?.HasCompleted.Value == true);
} }

View File

@ -58,8 +58,9 @@ namespace osu.Game.Rulesets.Mania.Edit
EditorBeatmap.PerformOnSelection(h => EditorBeatmap.PerformOnSelection(h =>
{ {
if (h is ManiaHitObject maniaObj) maniaPlayfield.Remove(h);
maniaObj.Column += columnDelta; ((ManiaHitObject)h).Column += columnDelta;
maniaPlayfield.Add(h);
}); });
} }
} }

View File

@ -9,12 +9,6 @@ namespace osu.Game.Rulesets.Mania
{ {
public class ManiaSkinComponent : GameplaySkinComponent<ManiaSkinComponents> public class ManiaSkinComponent : GameplaySkinComponent<ManiaSkinComponents>
{ {
/// <summary>
/// The intended <see cref="Column"/> index for this component.
/// May be null if the component does not exist in a <see cref="Column"/>.
/// </summary>
public readonly int? TargetColumn;
/// <summary> /// <summary>
/// The intended <see cref="StageDefinition"/> for this component. /// The intended <see cref="StageDefinition"/> for this component.
/// May be null if the component is not a direct member of a <see cref="Stage"/>. /// May be null if the component is not a direct member of a <see cref="Stage"/>.
@ -25,12 +19,10 @@ namespace osu.Game.Rulesets.Mania
/// Creates a new <see cref="ManiaSkinComponent"/>. /// Creates a new <see cref="ManiaSkinComponent"/>.
/// </summary> /// </summary>
/// <param name="component">The component.</param> /// <param name="component">The component.</param>
/// <param name="targetColumn">The intended <see cref="Column"/> index for this component. May be null if the component does not exist in a <see cref="Column"/>.</param>
/// <param name="stageDefinition">The intended <see cref="StageDefinition"/> for this component. May be null if the component is not a direct member of a <see cref="Stage"/>.</param> /// <param name="stageDefinition">The intended <see cref="StageDefinition"/> for this component. May be null if the component is not a direct member of a <see cref="Stage"/>.</param>
public ManiaSkinComponent(ManiaSkinComponents component, int? targetColumn = null, StageDefinition? stageDefinition = null) public ManiaSkinComponent(ManiaSkinComponents component, StageDefinition? stageDefinition = null)
: base(component) : base(component)
{ {
TargetColumn = targetColumn;
StageDefinition = stageDefinition; StageDefinition = stageDefinition;
} }

View File

@ -2,6 +2,7 @@
// 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;
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.Graphics.Containers;
@ -12,6 +13,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Mania.Objects.Drawables namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
@ -29,21 +31,21 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public DrawableHoldNoteHead Head => headContainer.Child; public DrawableHoldNoteHead Head => headContainer.Child;
public DrawableHoldNoteTail Tail => tailContainer.Child; public DrawableHoldNoteTail Tail => tailContainer.Child;
private readonly Container<DrawableHoldNoteHead> headContainer; private Container<DrawableHoldNoteHead> headContainer;
private readonly Container<DrawableHoldNoteTail> tailContainer; private Container<DrawableHoldNoteTail> tailContainer;
private readonly Container<DrawableHoldNoteTick> tickContainer; private Container<DrawableHoldNoteTick> tickContainer;
/// <summary> /// <summary>
/// Contains the size of the hold note covering the whole head/tail bounds. The size of this container changes as the hold note is being pressed. /// Contains the size of the hold note covering the whole head/tail bounds. The size of this container changes as the hold note is being pressed.
/// </summary> /// </summary>
private readonly Container sizingContainer; private Container sizingContainer;
/// <summary> /// <summary>
/// Contains the contents of the hold note that should be masked as the hold note is being pressed. Follows changes in the size of <see cref="sizingContainer"/>. /// Contains the contents of the hold note that should be masked as the hold note is being pressed. Follows changes in the size of <see cref="sizingContainer"/>.
/// </summary> /// </summary>
private readonly Container maskingContainer; private Container maskingContainer;
private readonly SkinnableDrawable bodyPiece; private SkinnableDrawable bodyPiece;
/// <summary> /// <summary>
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note. /// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
@ -60,11 +62,19 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary> /// </summary>
private double? releaseTime; private double? releaseTime;
public DrawableHoldNote()
: this(null)
{
}
public DrawableHoldNote(HoldNote hitObject) public DrawableHoldNote(HoldNote hitObject)
: base(hitObject) : base(hitObject)
{ {
RelativeSizeAxes = Axes.X; }
[BackgroundDependencyLoader]
private void load()
{
Container maskedContents; Container maskedContents;
AddRangeInternal(new Drawable[] AddRangeInternal(new Drawable[]
@ -86,7 +96,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
headContainer = new Container<DrawableHoldNoteHead> { RelativeSizeAxes = Axes.Both } headContainer = new Container<DrawableHoldNoteHead> { RelativeSizeAxes = Axes.Both }
} }
}, },
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody), _ => new DefaultBodyPiece
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}) })
@ -105,6 +115,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}); });
} }
protected override void OnApply()
{
base.OnApply();
sizingContainer.Size = Vector2.One;
HoldStartTime = null;
HoldBrokenTime = null;
releaseTime = null;
}
protected override void AddNestedHitObject(DrawableHitObject hitObject) protected override void AddNestedHitObject(DrawableHitObject hitObject)
{ {
base.AddNestedHitObject(hitObject); base.AddNestedHitObject(hitObject);
@ -128,37 +148,23 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override void ClearNestedHitObjects() protected override void ClearNestedHitObjects()
{ {
base.ClearNestedHitObjects(); base.ClearNestedHitObjects();
headContainer.Clear(); headContainer.Clear(false);
tailContainer.Clear(); tailContainer.Clear(false);
tickContainer.Clear(); tickContainer.Clear(false);
} }
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
{ {
switch (hitObject) switch (hitObject)
{ {
case TailNote _: case TailNote tail:
return new DrawableHoldNoteTail(this) return new DrawableHoldNoteTail(tail);
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = { BindTarget = AccentColour }
};
case Note _: case HeadNote head:
return new DrawableHoldNoteHead(this) return new DrawableHoldNoteHead(head);
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = { BindTarget = AccentColour }
};
case HoldNoteTick tick: case HoldNoteTick tick:
return new DrawableHoldNoteTick(tick) return new DrawableHoldNoteTick(tick);
{
HoldStartTime = () => HoldStartTime,
AccentColour = { BindTarget = AccentColour }
};
} }
return base.CreateNestedHitObject(hitObject); return base.CreateNestedHitObject(hitObject);

View File

@ -1,6 +1,7 @@
// 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 osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables namespace osu.Game.Rulesets.Mania.Objects.Drawables
@ -12,11 +13,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteHead; protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteHead;
public DrawableHoldNoteHead(DrawableHoldNote holdNote) public DrawableHoldNoteHead()
: base(holdNote.HitObject.Head) : this(null)
{ {
} }
public DrawableHoldNoteHead(HeadNote headNote)
: base(headNote)
{
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
}
public void UpdateResult() => base.UpdateResult(true); public void UpdateResult() => base.UpdateResult(true);
protected override void UpdateInitialTransforms() protected override void UpdateInitialTransforms()

View File

@ -2,6 +2,7 @@
// 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.Diagnostics; using System.Diagnostics;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables namespace osu.Game.Rulesets.Mania.Objects.Drawables
@ -20,12 +21,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteTail; protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteTail;
private readonly DrawableHoldNote holdNote; protected DrawableHoldNote HoldNote => (DrawableHoldNote)ParentHitObject;
public DrawableHoldNoteTail(DrawableHoldNote holdNote) public DrawableHoldNoteTail()
: base(holdNote.HitObject.Tail) : this(null)
{ {
this.holdNote = holdNote; }
public DrawableHoldNoteTail(TailNote tailNote)
: base(tailNote)
{
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
} }
public void UpdateResult() => base.UpdateResult(true); public void UpdateResult() => base.UpdateResult(true);
@ -54,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
ApplyResult(r => ApplyResult(r =>
{ {
// If the head wasn't hit or the hold note was broken, cap the max score to Meh. // If the head wasn't hit or the hold note was broken, cap the max score to Meh.
if (result > HitResult.Meh && (!holdNote.Head.IsHit || holdNote.HoldBrokenTime != null)) if (result > HitResult.Meh && (!HoldNote.Head.IsHit || HoldNote.HoldBrokenTime != null))
result = HitResult.Meh; result = HitResult.Meh;
r.Type = result; r.Type = result;

View File

@ -2,7 +2,8 @@
// 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;
using osuTK; using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -19,22 +20,28 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// <summary> /// <summary>
/// References the time at which the user started holding the hold note. /// References the time at which the user started holding the hold note.
/// </summary> /// </summary>
public Func<double?> HoldStartTime; private Func<double?> holdStartTime;
private Container glowContainer;
public DrawableHoldNoteTick()
: this(null)
{
}
public DrawableHoldNoteTick(HoldNoteTick hitObject) public DrawableHoldNoteTick(HoldNoteTick hitObject)
: base(hitObject) : base(hitObject)
{ {
Container glowContainer;
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Size = new Vector2(1); }
AddRangeInternal(new[] [BackgroundDependencyLoader]
private void load()
{ {
glowContainer = new CircularContainer AddInternal(glowContainer = new CircularContainer
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
@ -49,8 +56,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
AlwaysPresent = true AlwaysPresent = true
} }
} }
}
}); });
}
protected override void LoadComplete()
{
base.LoadComplete();
AccentColour.BindValueChanged(colour => AccentColour.BindValueChanged(colour =>
{ {
@ -64,12 +75,29 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}, true); }, true);
} }
protected override void OnApply()
{
base.OnApply();
Debug.Assert(ParentHitObject != null);
var holdNote = (DrawableHoldNote)ParentHitObject;
holdStartTime = () => holdNote.HoldStartTime;
}
protected override void OnFree()
{
base.OnFree();
holdStartTime = null;
}
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (Time.Current < HitObject.StartTime) if (Time.Current < HitObject.StartTime)
return; return;
var startTime = HoldStartTime?.Invoke(); var startTime = holdStartTime?.Invoke();
if (startTime == null || startTime > HitObject.StartTime) if (startTime == null || startTime > HitObject.StartTime)
ApplyResult(r => r.Type = r.Judgement.MinResult); ApplyResult(r => r.Type = r.Judgement.MinResult);

View File

@ -50,6 +50,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected DrawableManiaHitObject(ManiaHitObject hitObject) protected DrawableManiaHitObject(ManiaHitObject hitObject)
: base(hitObject) : base(hitObject)
{ {
RelativeSizeAxes = Axes.X;
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
@ -59,9 +60,31 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Action.BindTo(action); Action.BindTo(action);
Direction.BindTo(scrollingInfo.Direction); Direction.BindTo(scrollingInfo.Direction);
}
protected override void LoadComplete()
{
base.LoadComplete();
Direction.BindValueChanged(OnDirectionChanged, true); Direction.BindValueChanged(OnDirectionChanged, true);
} }
protected override void OnApply()
{
base.OnApply();
if (ParentHitObject != null)
AccentColour.BindTo(ParentHitObject.AccentColour);
}
protected override void OnFree()
{
base.OnFree();
if (ParentHitObject != null)
AccentColour.UnbindFrom(ParentHitObject.AccentColour);
}
private double computedLifetimeStart; private double computedLifetimeStart;
public override double LifetimeStart public override double LifetimeStart
@ -147,12 +170,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public abstract class DrawableManiaHitObject<TObject> : DrawableManiaHitObject public abstract class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
where TObject : ManiaHitObject where TObject : ManiaHitObject
{ {
public new readonly TObject HitObject; public new TObject HitObject => (TObject)base.HitObject;
protected DrawableManiaHitObject(TObject hitObject) protected DrawableManiaHitObject(TObject hitObject)
: base(hitObject) : base(hitObject)
{ {
HitObject = hitObject;
} }
} }
} }

View File

@ -33,31 +33,37 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected virtual ManiaSkinComponents Component => ManiaSkinComponents.Note; protected virtual ManiaSkinComponents Component => ManiaSkinComponents.Note;
private readonly Drawable headPiece; private Drawable headPiece;
public DrawableNote()
: this(null)
{
}
public DrawableNote(Note hitObject) public DrawableNote(Note hitObject)
: base(hitObject) : base(hitObject)
{ {
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
AddInternal(headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component, hitObject.Column), _ => new DefaultNotePiece())
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
});
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(ManiaRulesetConfigManager rulesetConfig) private void load(ManiaRulesetConfigManager rulesetConfig)
{ {
rulesetConfig?.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring); rulesetConfig?.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring);
AddInternal(headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component), _ => new DefaultNotePiece())
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
});
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
HitObject.StartTimeBindable.BindValueChanged(_ => updateSnapColour()); base.LoadComplete();
configTimingBasedNoteColouring.BindValueChanged(_ => updateSnapColour(), true);
configTimingBasedNoteColouring.BindValueChanged(_ => updateSnapColour());
StartTimeBindable.BindValueChanged(_ => updateSnapColour(), true);
} }
protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e) protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
@ -102,7 +108,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
private void updateSnapColour() private void updateSnapColour()
{ {
if (beatmap == null) return; if (beatmap == null || HitObject == null) return;
int snapDivisor = beatmap.ControlPointInfo.GetClosestBeatDivisor(HitObject.StartTime); int snapDivisor = beatmap.ControlPointInfo.GetClosestBeatDivisor(HitObject.StartTime);

View File

@ -0,0 +1,9 @@
// 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.
namespace osu.Game.Rulesets.Mania.Objects
{
public class HeadNote : Note
{
}
}

View File

@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Mania.Objects
/// <summary> /// <summary>
/// The head note of the hold. /// The head note of the hold.
/// </summary> /// </summary>
public Note Head { get; private set; } public HeadNote Head { get; private set; }
/// <summary> /// <summary>
/// The tail note of the hold. /// The tail note of the hold.
@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Objects
createTicks(cancellationToken); createTicks(cancellationToken);
AddNested(Head = new Note AddNested(Head = new HeadNote
{ {
StartTime = StartTime, StartTime = StartTime,
Column = Column, Column = Column,

View File

@ -17,6 +17,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
@ -55,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Y; RelativeSizeAxes = Axes.Y;
Width = COLUMN_WIDTH; Width = COLUMN_WIDTH;
Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, Index), _ => new DefaultColumnBackground()) Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}; };
@ -66,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.UI
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
background.CreateProxy(), background.CreateProxy(),
HitObjectArea = new ColumnHitObjectArea(Index, HitObjectContainer) { RelativeSizeAxes = Axes.Both }, HitObjectArea = new ColumnHitObjectArea(Index, HitObjectContainer) { RelativeSizeAxes = Axes.Both },
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, Index), _ => new DefaultKeyArea()) new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}, },
@ -83,6 +84,19 @@ namespace osu.Game.Rulesets.Mania.UI
hitPolicy = new OrderedHitPolicy(HitObjectContainer); hitPolicy = new OrderedHitPolicy(HitObjectContainer);
TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy()); TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy());
RegisterPool<Note, DrawableNote>(10, 50);
RegisterPool<HoldNote, DrawableHoldNote>(10, 50);
RegisterPool<HeadNote, DrawableHoldNoteHead>(10, 50);
RegisterPool<TailNote, DrawableHoldNoteTail>(10, 50);
RegisterPool<HoldNoteTick, DrawableHoldNoteTick>(50, 250);
}
protected override void LoadComplete()
{
base.LoadComplete();
NewResult += OnNewResult;
} }
public ColumnType ColumnType { get; set; } public ColumnType ColumnType { get; set; }
@ -98,28 +112,14 @@ namespace osu.Game.Rulesets.Mania.UI
return dependencies; return dependencies;
} }
/// <summary> protected override void OnNewDrawableHitObject(DrawableHitObject drawableHitObject)
/// Adds a DrawableHitObject to this Playfield.
/// </summary>
/// <param name="hitObject">The DrawableHitObject to add.</param>
public override void Add(DrawableHitObject hitObject)
{ {
hitObject.AccentColour.Value = AccentColour; base.OnNewDrawableHitObject(drawableHitObject);
hitObject.OnNewResult += OnNewResult;
DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)hitObject; DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)drawableHitObject;
maniaObject.AccentColour.Value = AccentColour;
maniaObject.CheckHittable = hitPolicy.IsHittable; maniaObject.CheckHittable = hitPolicy.IsHittable;
base.Add(hitObject);
}
public override bool Remove(DrawableHitObject h)
{
if (!base.Remove(h))
return false;
h.OnNewResult -= OnNewResult;
return true;
} }
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Depth = 2, Depth = 2,
}, },
hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget, columnIndex), _ => new DefaultHitTarget()) hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget), _ => new DefaultHitTarget())
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Depth = 1 Depth = 1

View File

@ -18,7 +18,6 @@ using osu.Game.Replays;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -134,20 +133,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant); protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
public override DrawableHitObject<ManiaHitObject> CreateDrawableRepresentation(ManiaHitObject h) public override DrawableHitObject<ManiaHitObject> CreateDrawableRepresentation(ManiaHitObject h) => null;
{
switch (h)
{
case HoldNote holdNote:
return new DrawableHoldNote(holdNote);
case Note note:
return new DrawableNote(note);
default:
return null;
}
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay); protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);

View File

@ -9,6 +9,7 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osuTK; using osuTK;
@ -56,6 +57,10 @@ namespace osu.Game.Rulesets.Mania.UI
} }
} }
public override void Add(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Add(hitObject);
public override bool Remove(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Remove(hitObject);
public override void Add(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Add(h); public override void Add(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Add(h);
public override bool Remove(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Remove(h); public override bool Remove(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Remove(h);

View File

@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.UI
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
InternalChild = skinnableExplosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, column.Index), _ => new DefaultHitExplosion()) InternalChild = skinnableExplosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion), _ => new DefaultHitExplosion())
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}; };

View File

@ -11,6 +11,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.Mania.UI.Components;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
@ -132,33 +133,19 @@ namespace osu.Game.Rulesets.Mania.UI
} }
} }
public override void Add(DrawableHitObject h) protected override void LoadComplete()
{ {
var maniaObject = (ManiaHitObject)h.HitObject; base.LoadComplete();
NewResult += OnNewResult;
int columnIndex = -1;
maniaObject.ColumnBindable.BindValueChanged(_ =>
{
if (columnIndex != -1)
Columns.ElementAt(columnIndex).Remove(h);
columnIndex = maniaObject.Column - firstColumnIndex;
Columns.ElementAt(columnIndex).Add(h);
}, true);
h.OnNewResult += OnNewResult;
} }
public override bool Remove(DrawableHitObject h) public override void Add(HitObject hitObject) => Columns.ElementAt(((ManiaHitObject)hitObject).Column - firstColumnIndex).Add(hitObject);
{
var maniaObject = (ManiaHitObject)h.HitObject;
int columnIndex = maniaObject.Column - firstColumnIndex;
Columns.ElementAt(columnIndex).Remove(h);
h.OnNewResult -= OnNewResult; public override bool Remove(HitObject hitObject) => Columns.ElementAt(((ManiaHitObject)hitObject).Column - firstColumnIndex).Remove(hitObject);
return true;
} public override void Add(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Add(h);
public override bool Remove(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Remove(h);
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline)); public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));

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(),
} }
}; };