1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-30 08:52:58 +08:00

Major rsefinements to taiko drawable classes.

This commit is contained in:
Dean Herbert 2017-04-05 10:01:40 +09:00
parent 6dc03c1cc4
commit 028e941ab2
No known key found for this signature in database
GPG Key ID: 46D71BF4958ABB49
21 changed files with 316 additions and 275 deletions

View File

@ -37,7 +37,7 @@ namespace osu.Desktop.VisualTests.Tests
} }
}); });
Add(new StrongCirclePiece Add(new CirclePiece(true)
{ {
Position = new Vector2(350, 100), Position = new Vector2(350, 100),
AccentColour = Color4.DarkRed, AccentColour = Color4.DarkRed,
@ -59,7 +59,7 @@ namespace osu.Desktop.VisualTests.Tests
} }
}); });
Add(new StrongCirclePiece Add(new CirclePiece(true)
{ {
Position = new Vector2(350, 300), Position = new Vector2(350, 300),
AccentColour = Color4.DarkBlue, AccentColour = Color4.DarkBlue,
@ -89,7 +89,7 @@ namespace osu.Desktop.VisualTests.Tests
KiaiMode = kiai, KiaiMode = kiai,
}); });
Add(new StrongCirclePiece Add(new CirclePiece(true)
{ {
Position = new Vector2(575, 300), Position = new Vector2(575, 300),
Width = 0.25f, Width = 0.25f,

View File

@ -108,11 +108,12 @@ namespace osu.Desktop.VisualTests.Tests
var d = new DrumRoll var d = new DrumRoll
{ {
StartTime = Time.Current + 1000, StartTime = Time.Current + 1000,
IsStrong = strong,
Distance = 1000, Distance = 1000,
PreEmpt = 1000, PreEmpt = 1000,
}; };
playfield.Add(strong ? new DrawableStrongDrumRoll(d) : new DrawableDrumRoll(d)); playfield.Add(new DrawableDrumRoll(d));
} }
private void addCentreHit(bool strong) private void addCentreHit(bool strong)

View File

@ -15,13 +15,13 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
public DrawableCentreHit(Hit hit) public DrawableCentreHit(Hit hit)
: base(hit) : base(hit)
{ {
Circle.Add(new CentreHitSymbolPiece()); MainPiece.Add(new CentreHitSymbolPiece());
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
Circle.AccentColour = colours.PinkDarker; MainPiece.AccentColour = colours.PinkDarker;
} }
} }
} }

View File

@ -3,28 +3,23 @@
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableDrumRoll : DrawableTaikoHitObject public class DrawableDrumRoll : DrawableTaikoHitObject<DrumRoll>
{ {
/// <summary> /// <summary>
/// Number of rolling hits required to reach the dark/final accent colour. /// Number of rolling hits required to reach the dark/final accent colour.
/// </summary> /// </summary>
private const int rolling_hits_for_dark_accent = 5; private const int rolling_hits_for_dark_accent = 5;
private readonly DrumRoll drumRoll;
private readonly CirclePiece circle;
private Color4 accentDarkColour; private Color4 accentDarkColour;
/// <summary> /// <summary>
@ -35,14 +30,6 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
public DrawableDrumRoll(DrumRoll drumRoll) public DrawableDrumRoll(DrumRoll drumRoll)
: base(drumRoll) : base(drumRoll)
{ {
this.drumRoll = drumRoll;
RelativeSizeAxes = Axes.X;
Width = (float)(drumRoll.Duration / drumRoll.PreEmpt);
Add(circle = CreateCirclePiece());
circle.KiaiMode = HitObject.Kiai;
foreach (var tick in drumRoll.Ticks) foreach (var tick in drumRoll.Ticks)
{ {
var newTick = new DrawableDrumRollTick(tick) var newTick = new DrawableDrumRollTick(tick)
@ -53,14 +40,22 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
newTick.OnJudgement += onTickJudgement; newTick.OnJudgement += onTickJudgement;
AddNested(newTick); AddNested(newTick);
Add(newTick); MainPiece.Add(newTick);
} }
} }
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = HitObject.IsStrong };
protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(HitObject.IsStrong)
{
Length = (float)(HitObject.Duration / HitObject.PreEmpt),
PlayfieldLengthReference = () => Parent.DrawSize.X
};
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
circle.AccentColour = AccentColour = colours.YellowDark; MainPiece.AccentColour = AccentColour = colours.YellowDark;
accentDarkColour = colours.YellowDarker; accentDarkColour = colours.YellowDarker;
} }
@ -72,7 +67,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
// is further than mid point of the play field, so the time taken to scroll in should always // is further than mid point of the play field, so the time taken to scroll in should always
// be greater than the time taken to scroll out to the left of the screen. // be greater than the time taken to scroll out to the left of the screen.
// Thus, using PreEmpt here is enough for the drum roll to completely scroll out. // Thus, using PreEmpt here is enough for the drum roll to completely scroll out.
LifetimeEnd = drumRoll.EndTime + drumRoll.PreEmpt; LifetimeEnd = HitObject.EndTime + HitObject.PreEmpt;
} }
private void onTickJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> obj) private void onTickJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> obj)
@ -85,7 +80,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_dark_accent); rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_dark_accent);
Color4 newAccent = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_dark_accent, AccentColour, accentDarkColour, 0, 1); Color4 newAccent = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_dark_accent, AccentColour, accentDarkColour, 0, 1);
circle.FadeAccent(newAccent, 100); MainPiece.FadeAccent(newAccent, 100);
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
@ -98,10 +93,10 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit); int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit);
if (countHit > drumRoll.RequiredGoodHits) if (countHit > HitObject.RequiredGoodHits)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = countHit >= drumRoll.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good; Judgement.TaikoResult = countHit >= HitObject.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good;
} }
else else
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;

View File

@ -3,79 +3,37 @@
using System; using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input; using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableDrumRollTick : DrawableTaikoHitObject public class DrawableDrumRollTick : DrawableTaikoHitObject<DrumRollTick>
{ {
/// <summary>
/// The size of a tick.
/// </summary>
private const float tick_size = TaikoHitObject.CIRCLE_RADIUS / 2;
/// <summary>
/// Any tick that is not the first for a drumroll is not filled, but is instead displayed
/// as a hollow circle. This is what controls the border width of that circle.
/// </summary>
private const float tick_border_width = tick_size / 4;
private readonly DrumRollTick tick;
private readonly CircularContainer bodyContainer;
public DrawableDrumRollTick(DrumRollTick tick) public DrawableDrumRollTick(DrumRollTick tick)
: base(tick) : base(tick)
{ {
this.tick = tick;
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
RelativePositionAxes = Axes.X;
Size = new Vector2(tick_size);
Children = new[]
{
bodyContainer = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = tick_border_width,
BorderColour = Color4.White,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = tick.FirstTick ? 1 : 0,
AlwaysPresent = true
}
}
}
};
} }
protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = tick.IsStrong }; protected override TaikoPiece CreateMainPiece() => new TickPiece
{
Filled = HitObject.FirstTick
};
protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = HitObject.IsStrong };
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
if (!userTriggered) if (!userTriggered)
{ {
if (Judgement.TimeOffset > tick.HitWindow) if (Judgement.TimeOffset > HitObject.HitWindow)
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
return; return;
} }
if (Math.Abs(Judgement.TimeOffset) < tick.HitWindow) if (Math.Abs(Judgement.TimeOffset) < HitObject.HitWindow)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great; Judgement.TaikoResult = TaikoHitResult.Great;
@ -87,7 +45,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
switch (state) switch (state)
{ {
case ArmedState.Hit: case ArmedState.Hit:
bodyContainer.ScaleTo(0, 100, EasingTypes.OutQuint); Content.ScaleTo(0, 100, EasingTypes.OutQuint);
break; break;
} }
} }

View File

@ -4,29 +4,19 @@
using System; using System;
using System.Linq; using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input; using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public abstract class DrawableHit : DrawableTaikoHitObject public abstract class DrawableHit : DrawableTaikoHitObject<Hit>
{ {
/// <summary> /// <summary>
/// A list of keys which can result in hits for this HitObject. /// A list of keys which can result in hits for this HitObject.
/// </summary> /// </summary>
protected abstract Key[] HitKeys { get; } protected abstract Key[] HitKeys { get; }
protected override Container<Drawable> Content => bodyContainer;
protected readonly CirclePiece Circle;
private readonly Hit hit;
private readonly Container bodyContainer;
/// <summary> /// <summary>
/// Whether the last key pressed is a valid hit key. /// Whether the last key pressed is a valid hit key.
/// </summary> /// </summary>
@ -35,41 +25,28 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
protected DrawableHit(Hit hit) protected DrawableHit(Hit hit)
: base(hit) : base(hit)
{ {
this.hit = hit;
AddInternal(bodyContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new[]
{
Circle = CreateCirclePiece()
}
});
Circle.KiaiMode = HitObject.Kiai;
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
if (!userTriggered) if (!userTriggered)
{ {
if (Judgement.TimeOffset > hit.HitWindowGood) if (Judgement.TimeOffset > HitObject.HitWindowGood)
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
return; return;
} }
double hitOffset = Math.Abs(Judgement.TimeOffset); double hitOffset = Math.Abs(Judgement.TimeOffset);
if (hitOffset > hit.HitWindowMiss) if (hitOffset > HitObject.HitWindowMiss)
return; return;
if (!validKeyPressed) if (!validKeyPressed)
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
else if (hitOffset < hit.HitWindowGood) else if (hitOffset < HitObject.HitWindowGood)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = hitOffset < hit.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good; Judgement.TaikoResult = hitOffset < HitObject.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good;
} }
else else
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
@ -92,16 +69,16 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
switch (State) switch (State)
{ {
case ArmedState.Idle: case ArmedState.Idle:
Delay(hit.HitWindowMiss); Delay(HitObject.HitWindowMiss);
break; break;
case ArmedState.Miss: case ArmedState.Miss:
FadeOut(100); FadeOut(100);
break; break;
case ArmedState.Hit: case ArmedState.Hit:
bodyContainer.ScaleTo(0.8f, 400, EasingTypes.OutQuad); Content.ScaleTo(0.8f, 400, EasingTypes.OutQuad);
bodyContainer.MoveToY(-200, 250, EasingTypes.Out); Content.MoveToY(-200, 250, EasingTypes.Out);
bodyContainer.Delay(250); Content.Delay(250);
bodyContainer.MoveToY(0, 500, EasingTypes.In); Content.MoveToY(0, 500, EasingTypes.In);
FadeOut(600); FadeOut(600);
break; break;

View File

@ -15,13 +15,13 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
public DrawableRimHit(Hit hit) public DrawableRimHit(Hit hit)
: base(hit) : base(hit)
{ {
Circle.Add(new RimHitSymbolPiece()); MainPiece.Add(new RimHitSymbolPiece());
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
Circle.AccentColour = colours.BlueDarker; MainPiece.AccentColour = colours.BlueDarker;
} }
} }
} }

View File

@ -15,13 +15,13 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
public DrawableStrongCentreHit(Hit hit) public DrawableStrongCentreHit(Hit hit)
: base(hit) : base(hit)
{ {
Circle.Add(new CentreHitSymbolPiece()); MainPiece.Add(new CentreHitSymbolPiece());
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
Circle.AccentColour = colours.PinkDarker; MainPiece.AccentColour = colours.PinkDarker;
} }
} }
} }

View File

@ -1,20 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableStrongDrumRoll : DrawableDrumRoll
{
public DrawableStrongDrumRoll(DrumRoll drumRoll)
: base(drumRoll)
{
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = true };
protected override CirclePiece CreateCirclePiece() => new StrongCirclePiece();
}
}

View File

@ -6,8 +6,8 @@ using System.Linq;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input; using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
@ -28,9 +28,9 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
} }
protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement(); protected override TaikoPiece CreateMainPiece() => new CirclePiece(true);
protected override CirclePiece CreateCirclePiece() => new StrongCirclePiece(); protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement();
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {

View File

@ -15,13 +15,13 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
public DrawableStrongRimHit(Hit hit) public DrawableStrongRimHit(Hit hit)
: base(hit) : base(hit)
{ {
Circle.Add(new RimHitSymbolPiece()); MainPiece.Add(new RimHitSymbolPiece());
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
Circle.AccentColour = colours.BlueDarker; MainPiece.AccentColour = colours.BlueDarker;
} }
} }
} }

View File

@ -18,7 +18,7 @@ using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableSwell : DrawableTaikoHitObject public class DrawableSwell : DrawableTaikoHitObject<Swell>
{ {
/// <summary> /// <summary>
/// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime. /// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime.
@ -31,8 +31,6 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
private const float target_ring_scale = 5f; private const float target_ring_scale = 5f;
private const float inner_ring_alpha = 0.65f; private const float inner_ring_alpha = 0.65f;
private readonly Swell swell;
private readonly Container bodyContainer; private readonly Container bodyContainer;
private readonly CircularContainer targetRing; private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing; private readonly CircularContainer expandingRing;
@ -54,12 +52,11 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
public DrawableSwell(Swell swell) public DrawableSwell(Swell swell)
: base(swell) : base(swell)
{ {
this.swell = swell;
Children = new Drawable[] Children = new Drawable[]
{ {
bodyContainer = new Container bodyContainer = new Container
{ {
AutoSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
expandingRing = new CircularContainer expandingRing = new CircularContainer
@ -120,6 +117,8 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
}, },
circlePiece = new CirclePiece circlePiece = new CirclePiece
{ {
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new [] Children = new []
{ {
symbol = new SwellSymbolPiece() symbol = new SwellSymbolPiece()
@ -146,18 +145,18 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
userHits++; userHits++;
var completion = (float)userHits / swell.RequiredHits; var completion = (float)userHits / HitObject.RequiredHits;
expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50); expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50);
expandingRing.Delay(50); expandingRing.Delay(50);
expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint); expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint);
expandingRing.DelayReset(); expandingRing.DelayReset();
symbol.RotateTo((float)(completion * swell.Duration / 8), 4000, EasingTypes.OutQuint); symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, EasingTypes.OutQuint);
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint); expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint);
if (userHits == swell.RequiredHits) if (userHits == HitObject.RequiredHits)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great; Judgement.TaikoResult = TaikoHitResult.Great;
@ -169,7 +168,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
return; return;
//TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
if (userHits > swell.RequiredHits / 2) if (userHits > HitObject.RequiredHits / 2)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Good; Judgement.TaikoResult = TaikoHitResult.Good;
@ -189,7 +188,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
Delay(preempt, true); Delay(preempt, true);
Delay(Judgement.TimeOffset + swell.Duration, true); Delay(Judgement.TimeOffset + HitObject.Duration, true);
const float out_transition_time = 300; const float out_transition_time = 300;

View File

@ -3,15 +3,18 @@
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces; using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Input; using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public abstract class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject, TaikoJudgement> public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableHitObject<TaikoHitObject, TaikoJudgement>
where TaikoHitType : TaikoHitObject
{ {
/// <summary> /// <summary>
/// A list of keys which this hit object will accept. These are the standard Taiko keys for now. /// A list of keys which this hit object will accept. These are the standard Taiko keys for now.
@ -19,35 +22,56 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables
/// </summary> /// </summary>
private readonly List<Key> validKeys = new List<Key>(new[] { Key.D, Key.F, Key.J, Key.K }); private readonly List<Key> validKeys = new List<Key>(new[] { Key.D, Key.F, Key.J, Key.K });
protected DrawableTaikoHitObject(TaikoHitObject hitObject) public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
protected override Container<Drawable> Content => bodyContainer;
protected readonly TaikoPiece MainPiece;
private readonly Container bodyContainer;
public new TaikoHitType HitObject;
protected DrawableTaikoHitObject(TaikoHitType hitObject)
: base(hitObject) : base(hitObject)
{ {
HitObject = hitObject;
Anchor = Anchor.CentreLeft; Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft; Origin = Anchor.Custom;
AutoSizeAxes = Axes.Both;
RelativePositionAxes = Axes.X; RelativePositionAxes = Axes.X;
AddInternal(bodyContainer = new Container
{
AutoSizeAxes = Axes.Both,
Children = new[]
{
MainPiece = CreateMainPiece()
}
});
MainPiece.KiaiMode = HitObject.Kiai;
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2; LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2;
base.LoadComplete(); base.LoadComplete();
} }
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement(); protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected virtual CirclePiece CreateCirclePiece() => new CirclePiece(); protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(HitObject.IsStrong);
/// <summary> /// <summary>
/// Sets the scroll position of the DrawableHitObject relative to the offset between /// Sets the scroll position of the DrawableHitObject relative to the offset between
/// a time value and the HitObject's StartTime. /// a time value and the HitObject's StartTime.
/// </summary> /// </summary>
/// <param name="time"></param> /// <param name="time"></param>
protected virtual void UpdateScrollPosition(double time) protected virtual void UpdateScrollPosition(double time) => MoveToX((float)((HitObject.StartTime - time) / HitObject.PreEmpt));
{
MoveToX((float)((HitObject.StartTime - time) / HitObject.PreEmpt));
}
protected override void Update() protected override void Update()
{ {

View File

@ -1,12 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
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;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -15,26 +13,30 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
/// <summary> /// <summary>
/// A circle piece which is used uniformly through osu!taiko to visualise hitobjects. /// A circle piece which is used uniformly through osu!taiko to visualise hitobjects.
/// <para> /// <para>
/// The body of this piece will overshoot its parent by <see cref="CirclePiece.Height"/> to form /// Note that this can actually be non-circle if the width is changed. See <see cref="ElongatedCirclePiece"/>
/// a rounded (_[-Width-]_) figure such that a regular "circle" is the result of a parent with Width = 0. /// for a usage example.
/// </para> /// </para>
/// </summary> /// </summary>
public class CirclePiece : Container, IHasAccentColour public class CirclePiece : TaikoPiece
{ {
public const float SYMBOL_SIZE = TaikoHitObject.CIRCLE_RADIUS * 2f * 0.45f; public const float SYMBOL_SIZE = TaikoHitObject.CIRCLE_RADIUS * 2f * 0.45f;
public const float SYMBOL_BORDER = 8; public const float SYMBOL_BORDER = 8;
public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER; public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER;
private Color4 accentColour; /// <summary>
/// The amount to scale up the base circle to show it as a "strong" piece.
/// </summary>
private const float strong_scale = 1.5f;
/// <summary> /// <summary>
/// The colour of the inner circle and outer glows. /// The colour of the inner circle and outer glows.
/// </summary> /// </summary>
public Color4 AccentColour public override Color4 AccentColour
{ {
get { return accentColour; } get { return base.AccentColour; }
set set
{ {
accentColour = value; base.AccentColour = value;
background.Colour = AccentColour; background.Colour = AccentColour;
@ -42,107 +44,92 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
} }
} }
private bool kiaiMode;
/// <summary> /// <summary>
/// Whether Kiai mode effects are enabled for this circle piece. /// Whether Kiai mode effects are enabled for this circle piece.
/// </summary> /// </summary>
public bool KiaiMode public override bool KiaiMode
{ {
get { return kiaiMode; } get { return base.KiaiMode; }
set set
{ {
kiaiMode = value; base.KiaiMode = value;
resetEdgeEffects(); resetEdgeEffects();
} }
} }
public override Anchor Origin protected override Container<Drawable> Content => content;
{
get { return Anchor.CentreLeft; }
set { throw new InvalidOperationException($"{nameof(CirclePiece)} must always use CentreLeft origin."); }
}
protected override Container<Drawable> Content => SymbolContainer; private readonly Container content;
protected readonly Container SymbolContainer;
private readonly Container background; private readonly Container background;
private readonly Container innerLayer;
public CirclePiece() public CirclePiece(bool isStrong = false)
{ {
RelativeSizeAxes = Axes.X; AddInternal(new Drawable[]
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", background = new CircularContainer
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{ {
background = new CircularContainer Name = "Background",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{ {
Name = "Background", new Box
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{ {
new Box Anchor = Anchor.Centre,
{ Origin = Anchor.Centre,
Anchor = Anchor.Centre, RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre, },
RelativeSizeAxes = Axes.Both, new Triangles
},
new Triangles
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
ColourLight = Color4.White,
ColourDark = Color4.White.Darken(0.1f)
}
}
},
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,
Anchor = Anchor.Centre, RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre, ColourLight = Color4.White,
RelativeSizeAxes = Axes.Both, ColourDark = Color4.White.Darken(0.1f)
Alpha = 0,
AlwaysPresent = true
}
} }
},
SymbolContainer = new Container
{
Name = "Symbol",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
} }
},
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
}
}
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
Name = "Symbol",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
} }
}); });
}
protected override void Update() if (isStrong)
{ {
// Add the overshoot to compensate for corner radius Size *= strong_scale;
innerLayer.Width = DrawWidth + DrawHeight;
//for symbols etc.
Content.Scale *= strong_scale;
}
} }
private void resetEdgeEffects() private void resetEdgeEffects()

View File

@ -0,0 +1,41 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics.Primitives;
using osu.Game.Modes.Taiko.UI;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
public class ElongatedCirclePiece : CirclePiece
{
/// <summary>
/// As we are being used to define the absolute size of hits, we need to be given a relative reference of our containing <see cref="TaikoPlayfield"/>.
/// </summary>
public Func<float> PlayfieldLengthReference;
/// <summary>
/// The length of this piece as a multiple of the value returned by <see cref="PlayfieldLengthReference"/>
/// </summary>
public float Length;
public ElongatedCirclePiece(bool isStrong = false) : base(isStrong)
{
}
protected override void Update()
{
base.Update();
var contentPadding = DrawHeight / 2 * Content.Scale.X;
Content.Padding = new MarginPadding
{
Left = contentPadding,
Right = contentPadding,
};
Width = (PlayfieldLengthReference?.Invoke() ?? 0) * Length + DrawHeight;
}
}
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
/// <summary>
/// A type of circle piece which is drawn at a higher scale to represent a "strong" piece.
/// </summary>
public class StrongCirclePiece : CirclePiece
{
/// <summary>
/// The amount to scale up the base circle to show it as a "strong" piece.
/// </summary>
private const float strong_scale = 1.5f;
public StrongCirclePiece()
{
SymbolContainer.Scale = new Vector2(strong_scale);
}
public override Vector2 Size => new Vector2(base.Size.X, base.Size.Y * strong_scale);
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
public class TaikoPiece : Container, IHasAccentColour
{
private Color4 accentColour;
/// <summary>
/// The colour of the inner circle and outer glows.
/// </summary>
public virtual Color4 AccentColour
{
get { return accentColour; }
set
{
accentColour = value;
}
}
private bool kiaiMode;
/// <summary>
/// Whether Kiai mode effects are enabled for this circle piece.
/// </summary>
public virtual bool KiaiMode
{
get { return kiaiMode; }
set
{
kiaiMode = value;
}
}
public TaikoPiece()
{
//just a default
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2);
}
}
}

View File

@ -0,0 +1,60 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
public class TickPiece : TaikoPiece
{
/// <summary>
/// Any tick that is not the first for a drumroll is not filled, but is instead displayed
/// as a hollow circle. This is what controls the border width of that circle.
/// </summary>
private const float tick_border_width = TaikoHitObject.CIRCLE_RADIUS / 2 / 4;
/// <summary>
/// The size of a tick.
/// </summary>
private const float tick_size = TaikoHitObject.CIRCLE_RADIUS / 2;
private bool filled;
public bool Filled
{
get { return filled; }
set
{
filled = value;
fillBox.Alpha = filled ? 1 : 0;
}
}
private readonly Box fillBox;
public TickPiece()
{
Size = new Vector2(tick_size);
Add(new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = tick_border_width,
BorderColour = Color4.White,
Children = new[]
{
fillBox = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
});
}
}
}

View File

@ -25,7 +25,7 @@ namespace osu.Game.Modes.Taiko.Objects
public float VelocityMultiplier = 1; public float VelocityMultiplier = 1;
/// <summary> /// <summary>
/// The time to scroll in the HitObject. /// The time from the initial right (off-screen) spawn position to the centre of the hit target.
/// </summary> /// </summary>
public double PreEmpt; public double PreEmpt;

View File

@ -133,8 +133,6 @@ namespace osu.Game.Modes.Taiko.UI
var drumRoll = h as DrumRoll; var drumRoll = h as DrumRoll;
if (drumRoll != null) if (drumRoll != null)
{ {
if (h.IsStrong)
return new DrawableStrongDrumRoll(drumRoll);
return new DrawableDrumRoll(drumRoll); return new DrawableDrumRoll(drumRoll);
} }

View File

@ -61,18 +61,19 @@
<Compile Include="Objects\Drawables\DrawableStrongRimHit.cs" /> <Compile Include="Objects\Drawables\DrawableStrongRimHit.cs" />
<Compile Include="Objects\Drawables\DrawableCentreHit.cs" /> <Compile Include="Objects\Drawables\DrawableCentreHit.cs" />
<Compile Include="Objects\Drawables\DrawableHit.cs" /> <Compile Include="Objects\Drawables\DrawableHit.cs" />
<Compile Include="Objects\Drawables\DrawableStrongDrumRoll.cs" />
<Compile Include="Objects\Drawables\DrawableStrongCentreHit.cs" /> <Compile Include="Objects\Drawables\DrawableStrongCentreHit.cs" />
<Compile Include="Objects\Drawables\DrawableStrongHit.cs" /> <Compile Include="Objects\Drawables\DrawableStrongHit.cs" />
<Compile Include="Objects\Drawables\Pieces\ElongatedCirclePiece.cs" />
<Compile Include="Objects\Drawables\Pieces\CentreHitSymbolPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\CentreHitSymbolPiece.cs" />
<Compile Include="Objects\Drawables\DrawableDrumRoll.cs" /> <Compile Include="Objects\Drawables\DrawableDrumRoll.cs" />
<Compile Include="Objects\Drawables\DrawableDrumRollTick.cs" /> <Compile Include="Objects\Drawables\DrawableDrumRollTick.cs" />
<Compile Include="Objects\Drawables\DrawableSwell.cs" /> <Compile Include="Objects\Drawables\DrawableSwell.cs" />
<Compile Include="Objects\Drawables\DrawableTaikoHitObject.cs" /> <Compile Include="Objects\Drawables\DrawableTaikoHitObject.cs" />
<Compile Include="Objects\Drawables\Pieces\RimHitSymbolPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\RimHitSymbolPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\StrongCirclePiece.cs" />
<Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" /> <Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
<Compile Include="Objects\Drawables\Pieces\SwellSymbolPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\SwellSymbolPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\TaikoPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\TickPiece.cs" />
<Compile Include="Objects\DrumRoll.cs" /> <Compile Include="Objects\DrumRoll.cs" />
<Compile Include="Objects\DrumRollTick.cs" /> <Compile Include="Objects\DrumRollTick.cs" />
<Compile Include="Objects\Hit.cs" /> <Compile Include="Objects\Hit.cs" />