diff --git a/osu-resources b/osu-resources index f85c594c18..2d8a6c1699 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit f85c594c182db2b01233e29ca52639b7baa00402 +Subproject commit 2d8a6c1699ff1acd3915fc28e8906dabf1b145a3 diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs new file mode 100644 index 0000000000..edd9c74485 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs @@ -0,0 +1,212 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Screens.Testing; +using osu.Game.Graphics; +using osu.Game.Modes.Taiko.Objects; +using osu.Game.Modes.Taiko.Objects.Drawable.Pieces; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseTaikoHitObjects : TestCase + { + public override string Description => "Taiko hit objects"; + + private bool kiai; + + public override void Reset() + { + base.Reset(); + + AddToggle("Kiai", b => + { + kiai = !kiai; + Reset(); + }); + + Add(new CentreHitCircle(new CirclePiece() + { + KiaiMode = kiai + }) + { + Position = new Vector2(100, 100) + }); + + Add(new CentreHitCircle(new AccentedCirclePiece() + { + KiaiMode = kiai + }) + { + Position = new Vector2(350, 100) + }); + + Add(new RimHitCircle(new CirclePiece() + { + KiaiMode = kiai + }) + { + Position = new Vector2(100, 300) + }); + + Add(new RimHitCircle(new AccentedCirclePiece() + { + KiaiMode = kiai + }) + { + Position = new Vector2(350, 300) + }); + + Add(new SwellCircle(new CirclePiece() + { + KiaiMode = kiai + }) + { + Position = new Vector2(100, 500) + }); + + Add(new SwellCircle(new AccentedCirclePiece() + { + KiaiMode = kiai + }) + { + Position = new Vector2(350, 500) + }); + + Add(new DrumRollCircle(new CirclePiece() + { + KiaiMode = kiai + }) + { + Width = 250, + Position = new Vector2(575, 100) + }); + + Add(new DrumRollCircle(new AccentedCirclePiece() + { + KiaiMode = kiai + }) + { + Width = 250, + Position = new Vector2(575, 300) + }); + } + + private class SwellCircle : BaseCircle + { + public SwellCircle(CirclePiece piece) + : base(piece) + { + Piece.Add(new TextAwesome + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + TextSize = SYMBOL_INNER_SIZE, + Icon = FontAwesome.fa_asterisk, + Shadow = false + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Piece.AccentColour = colours.YellowDark; + } + } + + private class DrumRollCircle : BaseCircle + { + public DrumRollCircle(CirclePiece piece) + : base(piece) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Piece.AccentColour = colours.YellowDark; + } + } + + private class CentreHitCircle : BaseCircle + { + public CentreHitCircle(CirclePiece piece) + : base(piece) + { + Piece.Add(new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(SYMBOL_INNER_SIZE), + Masking = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both + } + } + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Piece.AccentColour = colours.PinkDarker; + } + } + + private class RimHitCircle : BaseCircle + { + public RimHitCircle(CirclePiece piece) + : base(piece) + { + Piece.Add(new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(SYMBOL_SIZE), + BorderThickness = SYMBOL_BORDER, + BorderColour = Color4.White, + Masking = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Piece.AccentColour = colours.BlueDarker; + } + } + + private abstract class BaseCircle : Container + { + protected const float SYMBOL_SIZE = TaikoHitObject.CIRCLE_RADIUS * 2f * 0.45f; + protected const float SYMBOL_BORDER = 8; + protected const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER; + + protected readonly CirclePiece Piece; + + protected BaseCircle(CirclePiece piece) + { + Piece = piece; + + Add(Piece); + } + } + } +} diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index e63306300d..f1b4a99510 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -194,6 +194,7 @@ + diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs index c77c7762e3..e165f40442 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Taiko.Objects.Drawable.Pieces; namespace osu.Game.Modes.Taiko.Objects.Drawable { @@ -16,6 +17,11 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable Origin = Anchor.Centre; RelativePositionAxes = Axes.X; + + Children = new[] + { + CreateCircle() + }; } protected override void LoadComplete() @@ -42,5 +48,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable { UpdateScrollPosition(Time.Current); } + + protected abstract CirclePiece CreateCircle(); } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/AccentedCirclePiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/AccentedCirclePiece.cs new file mode 100644 index 0000000000..c02cbc572a --- /dev/null +++ b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/AccentedCirclePiece.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces +{ + /// + /// A type of circle piece which is drawn at a higher scale as an "accent". + /// + public class AccentedCirclePiece : CirclePiece + { + /// + /// The amount to scale up the base circle to show it as an "accented" piece. + /// + private const float accent_scale = 1.5f; + + public AccentedCirclePiece() + { + SymbolContainer.Scale = new Vector2(accent_scale); + } + + public override Vector2 Size => new Vector2(base.Size.X, base.Size.Y * accent_scale); + } +} diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/CirclePiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/CirclePiece.cs new file mode 100644 index 0000000000..453ab7a05d --- /dev/null +++ b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/CirclePiece.cs @@ -0,0 +1,156 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.Backgrounds; +using OpenTK.Graphics; +using System; + +namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces +{ + /// + /// A circle piece which is used uniformly through osu!taiko to visualise hitobjects. + /// + /// The body of this piece will overshoot its parent by to form + /// a rounded (_[-Width-]_) figure such that a regular "circle" is the result of a parent with Width = 0. + /// + /// + public class CirclePiece : Container + { + private Color4 accentColour; + /// + /// The colour of the inner circle and outer glows. + /// + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + + innerBackground.Colour = AccentColour; + + triangles.ColourLight = AccentColour; + triangles.ColourDark = AccentColour.Darken(0.1f); + + resetEdgeEffects(); + } + } + + private bool kiaiMode; + /// + /// Whether Kiai mode effects are enabled for this circle piece. + /// + public bool KiaiMode + { + get { return kiaiMode; } + set + { + kiaiMode = value; + + resetEdgeEffects(); + } + } + + public override Anchor Origin + { + get { return Anchor.CentreLeft; } + set { throw new InvalidOperationException($"{nameof(CirclePiece)} must always use CentreLeft origin."); } + } + + protected override Container Content => SymbolContainer; + protected readonly Container SymbolContainer; + + private readonly Container innerLayer; + private readonly Container innerCircleContainer; + private readonly Box innerBackground; + private readonly Triangles triangles; + + public CirclePiece() + { + RelativeSizeAxes = Axes.X; + Height = TaikoHitObject.CIRCLE_RADIUS * 2; + + // The "inner layer" is the body of the CirclePiece that overshoots it by Height/2 px on both sides + AddInternal(innerLayer = new Container + { + Name = "Inner Layer", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Children = new Framework.Graphics.Drawable[] + { + innerCircleContainer = new CircularContainer + { + Name = "Inner Circle", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Framework.Graphics.Drawable[] + { + innerBackground = new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + }, + triangles = new Triangles + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + } + } + }, + new CircularContainer + { + Name = "Ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + BorderThickness = 8, + BorderColour = Color4.White, + Masking = true, + Children = new[] + { + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + SymbolContainer = new Container + { + Name = "Symbol", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }); + } + + protected override void Update() + { + // Add the overshoot to compensate for corner radius + innerLayer.Width = DrawWidth + DrawHeight; + } + + private void resetEdgeEffects() + { + innerCircleContainer.EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Glow, + Colour = AccentColour, + Radius = KiaiMode ? 50 : 8 + }; + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index 937c5a5e51..25c3052583 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -54,9 +54,11 @@ + + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 989e605c16..32dd814fdc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -160,7 +160,7 @@ - + @@ -320,7 +320,7 @@ - + @@ -396,4 +396,4 @@ --> - \ No newline at end of file +