Skip to content

Commit 269b0b0

Browse files
committed
KiaiHitExplosion pool (wip)
* Is pooling faster than caching for sure? KiaiHitExplosion: ``` [Cached(typeof(DrawableHitObject))] public readonly DrawableHitObject JudgedObject; ``` TODO: * `DefaultKiaiHitExplosion.cs`: strong hit looks better with greater X scaling. but I don't sure about perf & it's not about this PR actually * `TaikoPlayfield.cs`: seems like Kiai explosion should be displayed only whin hit. Am I wrong? (`result.IsHit`)
1 parent 7b9f776 commit 269b0b0

File tree

6 files changed

+155
-101
lines changed

6 files changed

+155
-101
lines changed

osu.Game.Rulesets.Taiko/Skinning/Default/DefaultKiaiHitExplosion.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
using osu.Framework.Graphics.Effects;
88
using osu.Framework.Graphics.Shapes;
99
using osu.Game.Graphics;
10+
using osu.Game.Rulesets.Objects.Drawables;
1011
using osu.Game.Rulesets.Taiko.Objects;
12+
using osu.Game.Rulesets.Taiko.UI;
1113
using osuTK;
1214

1315
namespace osu.Game.Rulesets.Taiko.Skinning.Default
1416
{
15-
public partial class DefaultKiaiHitExplosion : CircularContainer
17+
public partial class DefaultKiaiHitExplosion : CircularContainer, IAnimatableHitExplosion
1618
{
1719
public override bool RemoveWhenNotAlive => true;
1820

@@ -51,14 +53,17 @@ private void load(OsuColour colours)
5153
};
5254
}
5355

54-
protected override void LoadComplete()
56+
public void Animate(DrawableHitObject _drawableHitObject)
5557
{
56-
base.LoadComplete();
57-
5858
this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint);
5959
this.FadeOut(250);
60+
}
6061

61-
Expire(true);
62+
public void AnimateSecondHit()
63+
{
64+
// TODO: STRONG_SCALE * 5
65+
this.ScaleTo(new Vector2(TaikoStrongableHitObject.STRONG_SCALE, 3f), 500, Easing.OutQuint);
66+
this.FadeOut(250);
6267
}
6368
}
6469
}

osu.Game.Rulesets.Taiko/UI/HitExplosion.cs

Lines changed: 4 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,23 @@
22
// See the LICENCE file in the repository root for full licence text.
33

44
using System;
5-
using osuTK;
6-
using osu.Framework.Allocation;
75
using osu.Framework.Graphics;
86
using osu.Framework.Graphics.Pooling;
9-
using osu.Game.Rulesets.Judgements;
10-
using osu.Game.Rulesets.Objects.Drawables;
117
using osu.Game.Rulesets.Scoring;
128
using osu.Game.Rulesets.Taiko.Objects;
139
using osu.Game.Rulesets.Taiko.Skinning.Default;
1410
using osu.Game.Skinning;
11+
using osuTK;
1512

1613
namespace osu.Game.Rulesets.Taiko.UI
1714
{
1815
/// <summary>
1916
/// A circle explodes from the hit target to indicate a hitobject has been hit.
2017
/// </summary>
21-
internal partial class HitExplosion : PoolableDrawable
18+
internal partial class HitExplosion : HitExplosionBase
2219
{
23-
public override bool RemoveWhenNotAlive => true;
24-
public override bool RemoveCompletedTransforms => false;
25-
2620
private readonly HitResult result;
2721

28-
private double? secondHitTime;
29-
30-
public DrawableHitObject? JudgedObject;
31-
32-
private SkinnableDrawable skinnable = null!;
33-
3422
/// <summary>
3523
/// This constructor only exists to meet the <c>new()</c> type constraint of <see cref="DrawablePool{T}"/>.
3624
/// </summary>
@@ -42,60 +30,12 @@ public HitExplosion()
4230
public HitExplosion(HitResult result)
4331
{
4432
this.result = result;
45-
46-
Anchor = Anchor.Centre;
47-
Origin = Anchor.Centre;
48-
4933
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE);
50-
RelativeSizeAxes = Axes.Both;
51-
5234
RelativePositionAxes = Axes.Both;
5335
}
5436

55-
[BackgroundDependencyLoader]
56-
private void load()
57-
{
58-
InternalChild = skinnable = new SkinnableDrawable(new TaikoSkinComponentLookup(getComponentName(result)), _ => new DefaultHitExplosion(result));
59-
skinnable.OnSkinChanged += runAnimation;
60-
}
61-
62-
public void Apply(DrawableHitObject? drawableHitObject)
63-
{
64-
JudgedObject = drawableHitObject;
65-
secondHitTime = null;
66-
}
67-
68-
protected override void PrepareForUse()
69-
{
70-
base.PrepareForUse();
71-
runAnimation();
72-
}
73-
74-
private void runAnimation()
75-
{
76-
if (JudgedObject?.Result == null)
77-
return;
78-
79-
double resultTime = JudgedObject.Result.TimeAbsolute;
80-
81-
LifetimeStart = resultTime;
82-
83-
ApplyTransformsAt(double.MinValue, true);
84-
ClearTransforms(true);
85-
86-
using (BeginAbsoluteSequence(resultTime))
87-
(skinnable.Drawable as IAnimatableHitExplosion)?.Animate(JudgedObject);
88-
89-
if (secondHitTime != null)
90-
{
91-
using (BeginAbsoluteSequence(secondHitTime.Value))
92-
{
93-
(skinnable.Drawable as IAnimatableHitExplosion)?.AnimateSecondHit();
94-
}
95-
}
96-
97-
LifetimeEnd = skinnable.Drawable.LatestTransformEndTime;
98-
}
37+
protected override SkinnableDrawable OnLoadSkinnableCreate() =>
38+
new SkinnableDrawable(new TaikoSkinComponentLookup(getComponentName(result)), _ => new DefaultHitExplosion(result));
9939

10040
private static TaikoSkinComponents getComponentName(HitResult result)
10141
{
@@ -113,11 +53,5 @@ private static TaikoSkinComponents getComponentName(HitResult result)
11353

11454
throw new ArgumentOutOfRangeException(nameof(result), $"Invalid result type: {result}");
11555
}
116-
117-
public void VisualiseSecondHit(JudgementResult judgementResult)
118-
{
119-
secondHitTime = judgementResult.TimeAbsolute;
120-
runAnimation();
121-
}
12256
}
12357
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2+
// See the LICENCE file in the repository root for full licence text.
3+
4+
using osu.Framework.Allocation;
5+
using osu.Framework.Graphics;
6+
using osu.Framework.Graphics.Pooling;
7+
using osu.Game.Rulesets.Judgements;
8+
using osu.Game.Rulesets.Objects.Drawables;
9+
using osu.Game.Skinning;
10+
11+
namespace osu.Game.Rulesets.Taiko.UI
12+
{
13+
/// <summary>
14+
/// A base class for taiko explosions from target hitting to indicate a hitobject has been hit.
15+
/// </summary>
16+
internal abstract partial class HitExplosionBase : PoolableDrawable
17+
{
18+
protected abstract SkinnableDrawable OnLoadSkinnableCreate();
19+
20+
public override bool RemoveWhenNotAlive => true;
21+
public override bool RemoveCompletedTransforms => false;
22+
23+
24+
protected double? SecondHitTime;
25+
26+
public DrawableHitObject? JudgedObject;
27+
28+
protected SkinnableDrawable Skinnable = null!;
29+
30+
public HitExplosionBase()
31+
{
32+
Anchor = Anchor.Centre;
33+
Origin = Anchor.Centre;
34+
35+
RelativeSizeAxes = Axes.Both;
36+
}
37+
38+
[BackgroundDependencyLoader]
39+
private void load()
40+
{
41+
InternalChild = Skinnable = OnLoadSkinnableCreate();
42+
Skinnable.OnSkinChanged += RunAnimation;
43+
}
44+
45+
public void Apply(DrawableHitObject? drawableHitObject)
46+
{
47+
JudgedObject = drawableHitObject;
48+
SecondHitTime = null;
49+
}
50+
51+
protected override void PrepareForUse()
52+
{
53+
base.PrepareForUse();
54+
RunAnimation();
55+
}
56+
57+
protected void RunAnimation()
58+
{
59+
if (JudgedObject?.Result is null) return;
60+
61+
double resultTime = JudgedObject.Result.TimeAbsolute;
62+
LifetimeStart = resultTime;
63+
64+
// Clear transforms
65+
ApplyTransformsAt(double.MinValue, true);
66+
ClearTransforms(true);
67+
68+
if (Skinnable.Drawable is IAnimatableHitExplosion animatable)
69+
{
70+
using (BeginAbsoluteSequence(resultTime))
71+
animatable.Animate(JudgedObject);
72+
73+
if (SecondHitTime != null)
74+
using (BeginAbsoluteSequence(SecondHitTime.Value))
75+
animatable.AnimateSecondHit();
76+
}
77+
78+
LifetimeEnd = Skinnable.Drawable.LatestTransformEndTime;
79+
}
80+
81+
public void VisualiseSecondHit(JudgementResult judgementResult)
82+
{
83+
SecondHitTime = judgementResult.TimeAbsolute;
84+
RunAnimation();
85+
}
86+
}
87+
}
Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
22
// See the LICENCE file in the repository root for full licence text.
33

4-
using osu.Framework.Allocation;
5-
using osu.Framework.Graphics;
6-
using osu.Framework.Graphics.Containers;
4+
using osu.Framework.Graphics.Pooling;
75
using osu.Game.Rulesets.Objects.Drawables;
86
using osu.Game.Rulesets.Taiko.Objects;
97
using osu.Game.Rulesets.Taiko.Skinning.Default;
@@ -12,37 +10,30 @@
1210

1311
namespace osu.Game.Rulesets.Taiko.UI
1412
{
15-
public partial class KiaiHitExplosion : Container
13+
/// <summary>
14+
/// An explosion from the hit target in Kiai mode to indicate a hitobject has been hit.
15+
/// </summary>
16+
internal partial class KiaiHitExplosion : HitExplosionBase
1617
{
17-
public override bool RemoveWhenNotAlive => true;
18-
19-
[Cached(typeof(DrawableHitObject))]
20-
public readonly DrawableHitObject JudgedObject;
21-
2218
private readonly HitType hitType;
2319

24-
private SkinnableDrawable skinnable = null!;
25-
26-
public override double LifetimeStart => skinnable.Drawable.LifetimeStart;
27-
28-
public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd;
20+
/// <summary>
21+
/// This constructor only exists to meet the <c>new()</c> type constraint of <see cref="DrawablePool{T}"/>.
22+
/// </summary>
23+
public KiaiHitExplosion() : this(HitType.Centre) { }
2924

30-
public KiaiHitExplosion(DrawableHitObject judgedObject, HitType hitType)
25+
public KiaiHitExplosion(HitType hitType)
3126
{
32-
JudgedObject = judgedObject;
3327
this.hitType = hitType;
34-
35-
Anchor = Anchor.Centre;
36-
Origin = Anchor.Centre;
37-
38-
RelativeSizeAxes = Axes.Both;
3928
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1);
4029
}
4130

42-
[BackgroundDependencyLoader]
43-
private void load()
31+
public KiaiHitExplosion(DrawableHitObject judgedObject, HitType hitType) : this(hitType)
4432
{
45-
Child = skinnable = new SkinnableDrawable(new TaikoSkinComponentLookup(TaikoSkinComponents.TaikoExplosionKiai), _ => new DefaultKiaiHitExplosion(hitType));
33+
Apply(judgedObject);
4634
}
35+
36+
protected override SkinnableDrawable OnLoadSkinnableCreate() =>
37+
new SkinnableDrawable(new TaikoSkinComponentLookup(TaikoSkinComponents.TaikoExplosionKiai), _ => new DefaultKiaiHitExplosion(hitType));
4738
}
4839
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2+
// See the LICENCE file in the repository root for full licence text.
3+
4+
using osu.Framework.Graphics.Pooling;
5+
using osu.Game.Rulesets.Taiko.Objects;
6+
7+
namespace osu.Game.Rulesets.Taiko.UI
8+
{
9+
/// <summary>
10+
/// Pool for hit explosions of a specific type.
11+
/// </summary>
12+
internal partial class KiaiHitExplosionPool : DrawablePool<KiaiHitExplosion>
13+
{
14+
private readonly HitType hitType;
15+
16+
public KiaiHitExplosionPool(HitType hitType)
17+
: base(15)
18+
{
19+
this.hitType = hitType;
20+
}
21+
22+
protected override KiaiHitExplosion CreateNewDrawable() => new KiaiHitExplosion(hitType);
23+
}
24+
}

osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public partial class TaikoPlayfield : ScrollingPlayfield
4141

4242
private JudgementPooler<DrawableTaikoJudgement> judgementPooler = null!;
4343
private readonly IDictionary<HitResult, HitExplosionPool> explosionPools = new Dictionary<HitResult, HitExplosionPool>();
44+
private readonly IDictionary<HitType, KiaiHitExplosionPool> kiaiExplosionPools = new Dictionary<HitType, KiaiHitExplosionPool>();
4445

4546
private ProxyContainer topLevelHitContainer = null!;
4647
private InputDrum inputDrum = null!;
@@ -201,6 +202,10 @@ private void load(OsuColour colours)
201202
foreach (var result in usableHitResults)
202203
explosionPools.Add(result, new HitExplosionPool(result));
203204
AddRangeInternal(explosionPools.Values);
205+
206+
foreach (var type in Enum.GetValues<HitType>())
207+
kiaiExplosionPools.Add(type, new KiaiHitExplosionPool(type));
208+
AddRangeInternal(kiaiExplosionPools.Values);
204209
}
205210

206211
protected override void LoadComplete()
@@ -300,7 +305,11 @@ internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result
300305
{
301306
case TaikoStrongJudgement:
302307
if (result.IsHit)
308+
{
303309
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(result);
310+
if (result.HitObject.Kiai)
311+
kiaiExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(result);
312+
}
304313
break;
305314

306315
case TaikoDrumRollTickJudgement:
@@ -336,8 +345,12 @@ private void addExplosion(DrawableHitObject drawableObject, HitResult result, Hi
336345
{
337346
hitExplosionContainer.Add(explosionPools[result]
338347
.Get(explosion => explosion.Apply(drawableObject)));
339-
if (drawableObject.HitObject.Kiai)
340-
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
348+
349+
//TODO: should we check `result.IsHit` ?
350+
if (drawableObject.HitObject.Kiai && result.IsHit())
351+
kiaiExplosionContainer.Add(
352+
kiaiExplosionPools[type].Get(
353+
explosion => explosion.Apply(drawableObject)));
341354
}
342355

343356
private partial class ProxyContainer : LifetimeManagementContainer

0 commit comments

Comments
 (0)