diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs new file mode 100644 index 0000000000..9c0673b527 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs @@ -0,0 +1,77 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.MathUtils; +using osu.Framework.Screens.Testing; +using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Taiko.Objects; +using osu.Game.Modes.Taiko.UI; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseTaikoPlayfield : TestCase + { + public override string Description => "Taiko playfield"; + + private TaikoPlayfield playfield; + + public override void Reset() + { + base.Reset(); + + AddButton("Hit!", addHitJudgement); + AddButton("Miss :(", addMissJudgement); + + Add(playfield = new TaikoPlayfield + { + Y = 200 + }); + } + + private void addHitJudgement() + { + TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great; + + playfield.OnJudgement(new DrawableTestHit(new TaikoHitObject()) + { + X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f), + Judgement = new TaikoJudgementInfo + { + Result = HitResult.Hit, + TaikoResult = hitResult, + TimeOffset = 0, + ComboAtHit = 1, + SecondHit = RNG.Next(10) == 0 + } + }); + } + + private void addMissJudgement() + { + playfield.OnJudgement(new DrawableTestHit(new TaikoHitObject()) + { + Judgement = new TaikoJudgementInfo + { + Result = HitResult.Miss, + TimeOffset = 0, + ComboAtHit = 0 + } + }); + } + + private class DrawableTestHit : DrawableHitObject + { + public DrawableTestHit(TaikoHitObject hitObject) + : base(hitObject) + { + } + + protected override TaikoJudgementInfo CreateJudgementInfo() => new TaikoJudgementInfo(); + + protected override void UpdateState(ArmedState state) + { + } + } + } +} diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index b67b4c4bb3..da5d8dba43 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -23,7 +23,7 @@ false LocalIntranet v4.5 - true + true publish\ true Disk @@ -194,6 +194,7 @@ + diff --git a/osu.Game.Modes.Catch/Judgements/CatchJudgementInfo.cs b/osu.Game.Modes.Catch/Judgements/CatchJudgementInfo.cs index 33e84d2f97..53e0c6c0bf 100644 --- a/osu.Game.Modes.Catch/Judgements/CatchJudgementInfo.cs +++ b/osu.Game.Modes.Catch/Judgements/CatchJudgementInfo.cs @@ -7,5 +7,8 @@ namespace osu.Game.Modes.Catch.Judgements { public class CatchJudgementInfo : JudgementInfo { + public override string ScoreString => string.Empty; + + public override string MaxScoreString => string.Empty; } } diff --git a/osu.Game.Modes.Mania/Judgements/ManiaJudgementInfo.cs b/osu.Game.Modes.Mania/Judgements/ManiaJudgementInfo.cs index a75f95abe7..c65bd87b6b 100644 --- a/osu.Game.Modes.Mania/Judgements/ManiaJudgementInfo.cs +++ b/osu.Game.Modes.Mania/Judgements/ManiaJudgementInfo.cs @@ -7,5 +7,8 @@ namespace osu.Game.Modes.Mania.Judgements { public class ManiaJudgementInfo : JudgementInfo { + public override string ScoreString => string.Empty; + + public override string MaxScoreString => string.Empty; } } diff --git a/osu.Game.Modes.Osu/Judgements/OsuJudgementInfo.cs b/osu.Game.Modes.Osu/Judgements/OsuJudgementInfo.cs index 20d36efe55..b945bad8a1 100644 --- a/osu.Game.Modes.Osu/Judgements/OsuJudgementInfo.cs +++ b/osu.Game.Modes.Osu/Judgements/OsuJudgementInfo.cs @@ -4,6 +4,7 @@ using OpenTK; using osu.Game.Modes.Judgements; using osu.Game.Modes.Osu.Objects.Drawables; +using osu.Framework.Extensions; namespace osu.Game.Modes.Osu.Judgements { @@ -24,6 +25,10 @@ namespace osu.Game.Modes.Osu.Judgements /// public OsuScoreResult MaxScore = OsuScoreResult.Hit300; + public override string ScoreString => Score.GetDescription(); + + public override string MaxScoreString => MaxScore.GetDescription(); + public int ScoreValue => scoreToInt(Score); public int MaxScoreValue => scoreToInt(MaxScore); diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuJudgementInfo.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuJudgementInfo.cs new file mode 100644 index 0000000000..15832bcb75 --- /dev/null +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuJudgementInfo.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Transforms; +using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Osu.Judgements; +using OpenTK; +using osu.Game.Modes.Judgements; + +namespace osu.Game.Modes.Osu.Objects.Drawables +{ + public class DrawableOsuJudgementInfo : DrawableJudgementInfo + { + public DrawableOsuJudgementInfo(OsuJudgementInfo judgement) : base(judgement) + { + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + if (Judgement.Result != HitResult.Miss) + { + JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint); + FadeOut(500); + } + + Expire(); + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs b/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs deleted file mode 100644 index f2ccf554d9..0000000000 --- a/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Transforms; -using osu.Game.Graphics.Sprites; -using osu.Game.Modes.Objects.Drawables; -using osu.Game.Modes.Osu.Judgements; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Modes.Osu.Objects.Drawables -{ - public class HitExplosion : FillFlowContainer - { - private readonly OsuJudgementInfo judgement; - private readonly SpriteText line1; - private readonly SpriteText line2; - - public HitExplosion(OsuJudgementInfo judgement, OsuHitObject h = null) - { - this.judgement = judgement; - AutoSizeAxes = Axes.Both; - Origin = Anchor.Centre; - - Direction = FillDirection.Vertical; - Spacing = new Vector2(0, 2); - Position = (h?.StackedEndPosition ?? Vector2.Zero) + judgement.PositionOffset; - - Children = new Drawable[] - { - line1 = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = judgement.Score.GetDescription(), - Font = @"Venera", - TextSize = 16, - }, - line2 = new OsuSpriteText - { - Text = judgement.Combo.GetDescription(), - Font = @"Venera", - TextSize = 11, - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - if (judgement.Result == HitResult.Miss) - { - FadeInFromZero(60); - - ScaleTo(1.6f); - ScaleTo(1, 100, EasingTypes.In); - - MoveToOffset(new Vector2(0, 100), 800, EasingTypes.InQuint); - RotateTo(40, 800, EasingTypes.InQuint); - - Delay(600); - FadeOut(200); - } - else - { - line1.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint); - line2.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint); - FadeOut(500); - } - - switch (judgement.Result) - { - case HitResult.Miss: - Colour = Color4.Red; - break; - } - - Expire(); - } - } -} \ No newline at end of file diff --git a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs index 87c80f94bc..8924fe71e7 100644 --- a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs @@ -85,7 +85,11 @@ namespace osu.Game.Modes.Osu.UI public override void OnJudgement(DrawableHitObject judgedObject) { - HitExplosion explosion = new HitExplosion(judgedObject.Judgement, judgedObject.HitObject); + DrawableOsuJudgementInfo explosion = new DrawableOsuJudgementInfo(judgedObject.Judgement) + { + Origin = Anchor.Centre, + Position = judgedObject.HitObject.StackedEndPosition + judgedObject.Judgement.PositionOffset + }; judgementLayer.Add(explosion); } diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index 12135a38fb..1c1add8b94 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -58,7 +58,7 @@ - + diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs b/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs index d425616b66..cbc3919c4f 100644 --- a/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs +++ b/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs @@ -1,11 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel; + namespace osu.Game.Modes.Taiko.Judgements { public enum TaikoHitResult { + [Description("GOOD")] Good, + [Description("GREAT")] Great } } diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoJudgementInfo.cs b/osu.Game.Modes.Taiko/Judgements/TaikoJudgementInfo.cs index d9e81d4d77..3312661e2a 100644 --- a/osu.Game.Modes.Taiko/Judgements/TaikoJudgementInfo.cs +++ b/osu.Game.Modes.Taiko/Judgements/TaikoJudgementInfo.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Modes.Judgements; +using osu.Framework.Extensions; namespace osu.Game.Modes.Taiko.Judgements { @@ -37,6 +38,10 @@ namespace osu.Game.Modes.Taiko.Judgements /// public int MaxAccuracyScoreValue => NumericResultForAccuracy(MAX_HIT_RESULT); + public override string ScoreString => TaikoResult.GetDescription(); + + public override string MaxScoreString => MAX_HIT_RESULT.GetDescription(); + /// /// Whether this Judgement has a secondary hit in the case of finishers. /// diff --git a/osu.Game.Modes.Taiko/UI/DrawableTaikoJudgementInfo.cs b/osu.Game.Modes.Taiko/UI/DrawableTaikoJudgementInfo.cs new file mode 100644 index 0000000000..87f321d557 --- /dev/null +++ b/osu.Game.Modes.Taiko/UI/DrawableTaikoJudgementInfo.cs @@ -0,0 +1,57 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Objects.Drawables; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Modes.Judgements; + +namespace osu.Game.Modes.Taiko.UI +{ + /// + /// Text that is shown as judgement when a hit object is hit or missed. + /// + public class DrawableTaikoJudgementInfo : DrawableJudgementInfo + { + /// + /// Creates a new judgement text. + /// + /// The judgement to visualise. + public DrawableTaikoJudgementInfo(TaikoJudgementInfo judgement) + : base(judgement) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + switch (Judgement.Result) + { + case HitResult.Hit: + switch (Judgement.TaikoResult) + { + case TaikoHitResult.Good: + Colour = colours.GreenLight; + break; + case TaikoHitResult.Great: + Colour = colours.BlueLight; + break; + } + break; + } + } + + protected override void LoadComplete() + { + switch (Judgement.Result) + { + case HitResult.Hit: + MoveToY(-100, 500); + break; + } + + base.LoadComplete(); + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Taiko/UI/HitExplosion.cs b/osu.Game.Modes.Taiko/UI/HitExplosion.cs new file mode 100644 index 0000000000..3aa7977617 --- /dev/null +++ b/osu.Game.Modes.Taiko/UI/HitExplosion.cs @@ -0,0 +1,78 @@ +// 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.Graphics.Transforms; +using osu.Game.Graphics; +using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Taiko.Objects; + +namespace osu.Game.Modes.Taiko.UI +{ + /// + /// A circle explodes from the hit target to indicate a hitobject has been hit. + /// + internal class HitExplosion : CircularContainer + { + private readonly TaikoJudgementInfo judgement; + private readonly Box innerFill; + + public HitExplosion(TaikoJudgementInfo judgement) + { + this.judgement = judgement; + + Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativePositionAxes = Axes.Both; + + BorderColour = Color4.White; + BorderThickness = 1; + + Alpha = 0.15f; + Masking = true; + + Children = new[] + { + innerFill = new Box + { + RelativeSizeAxes = Axes.Both, + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (judgement.SecondHit) + Size *= 1.5f; + + switch (judgement.TaikoResult) + { + case TaikoHitResult.Good: + innerFill.Colour = colours.Green; + break; + case TaikoHitResult.Great: + innerFill.Colour = colours.Blue; + break; + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + ScaleTo(5f, 1000, EasingTypes.OutQuint); + FadeOut(500); + + Expire(); + } + } +} diff --git a/osu.Game.Modes.Taiko/UI/HitTarget.cs b/osu.Game.Modes.Taiko/UI/HitTarget.cs new file mode 100644 index 0000000000..d38af3390e --- /dev/null +++ b/osu.Game.Modes.Taiko/UI/HitTarget.cs @@ -0,0 +1,105 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Modes.Taiko.Objects; + +namespace osu.Game.Modes.Taiko.UI +{ + /// + /// A component that is displayed at the hit position in the taiko playfield. + /// + internal class HitTarget : Container + { + /// + /// Diameter of normal hit object circles. + /// + private const float normal_diameter = TaikoHitObject.CIRCLE_RADIUS * 2 * TaikoPlayfield.PLAYFIELD_SCALE; + + /// + /// Diameter of finisher hit object circles. + /// + private const float finisher_diameter = normal_diameter * 1.5f; + + /// + /// The 1px inner border of the taiko playfield. + /// + private const float border_offset = 1; + + /// + /// Thickness of all drawn line pieces. + /// + private const float border_thickness = 2.5f; + + public HitTarget() + { + RelativeSizeAxes = Axes.Y; + + Children = new Drawable[] + { + new Box + { + Name = "Bar Upper", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Y = border_offset, + Size = new Vector2(border_thickness, (TaikoPlayfield.PlayfieldHeight - finisher_diameter) / 2f - border_offset), + Alpha = 0.1f + }, + new CircularContainer + { + Name = "Finisher Ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(finisher_diameter), + Masking = true, + BorderColour = Color4.White, + BorderThickness = border_thickness, + Alpha = 0.1f, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + new CircularContainer + { + Name = "Normal Ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(normal_diameter), + Masking = true, + BorderColour = Color4.White, + BorderThickness = border_thickness, + Alpha = 0.5f, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + new Box + { + Name = "Bar Lower", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -border_offset, + Size = new Vector2(border_thickness, (TaikoPlayfield.PlayfieldHeight - finisher_diameter) / 2f - border_offset), + Alpha = 0.1f + }, + }; + } + } +} diff --git a/osu.Game.Modes.Taiko/UI/InputDrum.cs b/osu.Game.Modes.Taiko/UI/InputDrum.cs new file mode 100644 index 0000000000..1787670c7a --- /dev/null +++ b/osu.Game.Modes.Taiko/UI/InputDrum.cs @@ -0,0 +1,149 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Input; +using osu.Game.Graphics; + +namespace osu.Game.Modes.Taiko.UI +{ + /// + /// A component of the playfield that captures input and displays input as a drum. + /// + internal class InputDrum : Container + { + public InputDrum() + { + Size = new Vector2(TaikoPlayfield.PlayfieldHeight); + + const float middle_split = 10; + + Children = new Drawable[] + { + new TaikoHalfDrum(false) + { + Name = "Left Half", + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + X = -middle_split / 2, + RimKey = Key.D, + CentreKey = Key.F + }, + new TaikoHalfDrum(true) + { + Name = "Right Half", + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + X = middle_split / 2, + Position = new Vector2(-1f, 0), + RimKey = Key.K, + CentreKey = Key.J + } + }; + } + + /// + /// A half-drum. Contains one centre and one rim hit. + /// + private class TaikoHalfDrum : Container + { + /// + /// The key to be used for the rim of the half-drum. + /// + public Key RimKey; + + /// + /// The key to be used for the centre of the half-drum. + /// + public Key CentreKey; + + private readonly Sprite rim; + private readonly Sprite rimHit; + private readonly Sprite centre; + private readonly Sprite centreHit; + + public TaikoHalfDrum(bool flipped) + { + Masking = true; + + Children = new Drawable[] + { + rim = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both + }, + rimHit = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Alpha = 0, + BlendingMode = BlendingMode.Additive, + }, + centre = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.7f) + }, + centreHit = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.7f), + Alpha = 0, + BlendingMode = BlendingMode.Additive + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures, OsuColour colours) + { + rim.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer"); + rimHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer-hit"); + centre.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner"); + centreHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner-hit"); + + rimHit.Colour = colours.Blue; + centreHit.Colour = colours.Pink; + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat) + return false; + + Drawable target = null; + + if (args.Key == CentreKey) + target = centreHit; + else if (args.Key == RimKey) + target = rimHit; + + if (target != null) + { + target.FadeTo(Math.Min(target.Alpha + 0.4f, 1), 40, EasingTypes.OutQuint); + target.Delay(40); + target.FadeOut(600, EasingTypes.OutQuint); + } + + return false; + } + } + } +} diff --git a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs index f3ae600501..b322e167df 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs @@ -4,38 +4,192 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.UI; using OpenTK; using OpenTK.Graphics; using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Objects.Drawables; +using osu.Game.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Primitives; namespace osu.Game.Modes.Taiko.UI { public class TaikoPlayfield : Playfield { + /// + /// The default play field height. + /// + public const float PLAYFIELD_BASE_HEIGHT = 242; + + /// + /// The play field height scale. + /// + public const float PLAYFIELD_SCALE = 0.65f; + + /// + /// The play field height after scaling. + /// + public static float PlayfieldHeight => PLAYFIELD_BASE_HEIGHT * PLAYFIELD_SCALE; + + /// + /// The offset from which the center of the hit target lies at. + /// + private const float hit_target_offset = 80; + + /// + /// The size of the left area of the playfield. This area contains the input drum. + /// + private const float left_area_size = 240; + + protected override Container Content => hitObjectContainer; + + private readonly Container hitExplosionContainer; + //private Container barLineContainer; + private readonly Container judgementContainer; + + private readonly Container hitObjectContainer; + //private Container topLevelHitContainer; + private readonly Container leftBackgroundContainer; + private readonly Container rightBackgroundContainer; + private readonly Box leftBackground; + private readonly Box rightBackground; + public TaikoPlayfield() { RelativeSizeAxes = Axes.X; - Size = new Vector2(1, 100); - Anchor = Anchor.Centre; - Origin = Anchor.Centre; + Height = PlayfieldHeight; + + AddInternal(new Drawable[] + { + rightBackgroundContainer = new Container + { + RelativeSizeAxes = Axes.Both, + BorderThickness = 2, + Masking = true, + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Radius = 5, + }, + Children = new Drawable[] + { + rightBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.6f + }, + } + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = left_area_size }, + Children = new Drawable[] + { + new Container + { + Padding = new MarginPadding { Left = hit_target_offset }, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + hitExplosionContainer = new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), + Scale = new Vector2(PLAYFIELD_SCALE), + BlendingMode = BlendingMode.Additive + }, + //barLineContainer = new Container + //{ + // RelativeSizeAxes = Axes.Both, + //}, + new HitTarget + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + }, + hitObjectContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + judgementContainer = new Container + { + RelativeSizeAxes = Axes.Both, + BlendingMode = BlendingMode.Additive + }, + }, + }, + } + }, + leftBackgroundContainer = new Container + { + Size = new Vector2(left_area_size, PlayfieldHeight), + BorderThickness = 1, + Children = new Drawable[] + { + leftBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new InputDrum + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.X, + Position = new Vector2(0.10f, 0), + Scale = new Vector2(0.9f) + }, + new Box + { + Anchor = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = 10, + ColourInfo = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), + }, + } + }, + //topLevelHitContainer = new Container + //{ + // RelativeSizeAxes = Axes.Both, + //} + }); } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(OsuColour colours) { - Add(new Box { RelativeSizeAxes = Axes.Both, Alpha = 0.5f }); + leftBackgroundContainer.BorderColour = colours.Gray0; + leftBackground.Colour = colours.Gray1; - Add(new Sprite + rightBackgroundContainer.BorderColour = colours.Gray1; + rightBackground.Colour = colours.Gray0; + } + + public override void Add(DrawableHitObject h) + { + h.Depth = (float)h.HitObject.StartTime; + + base.Add(h); + } + + public override void OnJudgement(DrawableHitObject judgedObject) + { + bool wasHit = judgedObject.Judgement.Result == HitResult.Hit; + + if (wasHit) + hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement)); + + judgementContainer.Add(new DrawableTaikoJudgementInfo(judgedObject.Judgement) { - Texture = textures.Get(@"Menu/logo"), - Origin = Anchor.Centre, - Scale = new Vector2(0.2f), - RelativePositionAxes = Axes.Both, - Position = new Vector2(0.1f, 0.5f), - Colour = Color4.Gray + Anchor = wasHit ? Anchor.TopLeft : Anchor.CentreLeft, + Origin = wasHit ? Anchor.BottomCentre : Anchor.Centre, + RelativePositionAxes = Axes.X, + X = wasHit ? judgedObject.Position.X : 0, }); } } diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index de6444dd5f..f7bc6883e1 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -60,6 +60,10 @@ + + + + @@ -77,10 +81,6 @@ {C76BF5B3-985E-4D39-95FE-97C9C879B83A} osu.Framework - - {C92A607B-1FDD-4954-9F92-03FF547D9080} - osu.Game.Modes.Osu - {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D} osu.Game diff --git a/osu.Game/Modes/Judgements/DrawableJudgementInfo.cs b/osu.Game/Modes/Judgements/DrawableJudgementInfo.cs new file mode 100644 index 0000000000..85b357a995 --- /dev/null +++ b/osu.Game/Modes/Judgements/DrawableJudgementInfo.cs @@ -0,0 +1,94 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Modes.Objects.Drawables; + +namespace osu.Game.Modes.Judgements +{ + /// + /// A drawable object which visualises the hit result of a . + /// + /// The type of judgement to visualise. + public class DrawableJudgementInfo : Container + where TJudgement : JudgementInfo + { + protected readonly TJudgement Judgement; + + protected readonly SpriteText JudgementText; + + /// + /// Creates a drawable which visualises a . + /// + /// The judgement to visualise. + public DrawableJudgementInfo(TJudgement judgement) + { + Judgement = judgement; + + AutoSizeAxes = Axes.Both; + + string scoreString = judgement.Result == HitResult.Hit ? judgement.ScoreString : judgement.Result.GetDescription(); + + Children = new[] + { + JudgementText = new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Text = scoreString.ToUpper(), + Font = @"Venera", + TextSize = 16 + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + switch (Judgement.Result) + { + case HitResult.Miss: + Colour = colours.Red; + break; + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + FadeInFromZero(100, EasingTypes.OutQuint); + + switch (Judgement.Result) + { + case HitResult.Miss: + ScaleTo(1.6f); + ScaleTo(1, 100, EasingTypes.In); + + MoveToOffset(new Vector2(0, 100), 800, EasingTypes.InQuint); + RotateTo(40, 800, EasingTypes.InQuint); + + Delay(600); + FadeOut(200); + break; + case HitResult.Hit: + ScaleTo(0.9f); + ScaleTo(1, 500, EasingTypes.OutElastic); + + Delay(250); + FadeOut(250, EasingTypes.OutQuint); + break; + } + + Expire(); + } + } +} diff --git a/osu.Game/Modes/Judgements/JudgementInfo.cs b/osu.Game/Modes/Judgements/JudgementInfo.cs index 8e7539134e..a3cb9ba51f 100644 --- a/osu.Game/Modes/Judgements/JudgementInfo.cs +++ b/osu.Game/Modes/Judgements/JudgementInfo.cs @@ -5,10 +5,31 @@ using osu.Game.Modes.Objects.Drawables; namespace osu.Game.Modes.Judgements { - public class JudgementInfo + public abstract class JudgementInfo { - public ulong? ComboAtHit; + /// + /// Whether this judgement is the result of a hit or a miss. + /// public HitResult? Result; + + /// + /// The offset at which this judgement occurred. + /// public double TimeOffset; + + /// + /// The combo after this judgement was processed. + /// + public ulong? ComboAtHit; + + /// + /// The string representation for the score achieved. + /// + public abstract string ScoreString { get; } + + /// + /// The string representation for the max score achievable. + /// + public abstract string MaxScoreString { get; } } } \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index bfb787cd51..164c8c7ec1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -96,6 +96,7 @@ +