mirror of
https://github.com/ppy/osu.git
synced 2024-11-06 06:17:23 +08:00
Add support for pooling explosions in taiko
This commit is contained in:
parent
716b9048c1
commit
8b74666cc3
@ -38,11 +38,11 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
// the hit needs to be added to hierarchy in order for nested objects to be created correctly.
|
||||
// setting zero alpha is supposed to prevent the test from looking broken.
|
||||
hit.With(h => h.Alpha = 0),
|
||||
new HitExplosion(hit, hit.Type)
|
||||
new HitExplosion(hit.Type)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
}.With(explosion => explosion.Apply(hit))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,22 +1,24 @@
|
||||
// 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.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
{
|
||||
public class LegacyHitExplosion : CompositeDrawable
|
||||
public class LegacyHitExplosion : CompositeDrawable, IHitExplosion
|
||||
{
|
||||
private readonly Drawable sprite;
|
||||
private readonly Drawable strongSprite;
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
private DrawableStrongNestedHit nestedStrongHit;
|
||||
private bool switchedToStrongSprite;
|
||||
private readonly Drawable sprite;
|
||||
|
||||
[CanBeNull]
|
||||
private readonly Drawable strongSprite;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new legacy hit explosion.
|
||||
@ -27,14 +29,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
/// </remarks>
|
||||
/// <param name="sprite">The normal legacy explosion sprite.</param>
|
||||
/// <param name="strongSprite">The strong legacy explosion sprite.</param>
|
||||
public LegacyHitExplosion(Drawable sprite, Drawable strongSprite = null)
|
||||
public LegacyHitExplosion(Drawable sprite, [CanBeNull] Drawable strongSprite = null)
|
||||
{
|
||||
this.sprite = sprite;
|
||||
this.strongSprite = strongSprite;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(DrawableHitObject judgedObject)
|
||||
private void load()
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
@ -56,17 +58,15 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
s.Origin = Anchor.Centre;
|
||||
}));
|
||||
}
|
||||
|
||||
if (judgedObject is DrawableHit hit)
|
||||
nestedStrongHit = hit.NestedHitObjects.SingleOrDefault() as DrawableStrongNestedHit;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
public void Animate(DrawableHitObject drawableHitObject)
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
const double animation_time = 120;
|
||||
|
||||
(sprite as IFramedAnimation)?.GotoFrame(0);
|
||||
(strongSprite as IFramedAnimation)?.GotoFrame(0);
|
||||
|
||||
this.FadeInFromZero(animation_time).Then().FadeOut(animation_time * 1.5);
|
||||
|
||||
this.ScaleTo(0.6f)
|
||||
@ -77,24 +77,13 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
Expire(true);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
public void AnimateSecondHit()
|
||||
{
|
||||
base.Update();
|
||||
if (strongSprite == null)
|
||||
return;
|
||||
|
||||
if (shouldSwitchToStrongSprite() && !switchedToStrongSprite)
|
||||
{
|
||||
sprite.FadeOut(50, Easing.OutQuint);
|
||||
strongSprite.FadeIn(50, Easing.OutQuint);
|
||||
switchedToStrongSprite = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool shouldSwitchToStrongSprite()
|
||||
{
|
||||
if (nestedStrongHit == null || strongSprite == null)
|
||||
return false;
|
||||
|
||||
return nestedStrongHit.IsHit;
|
||||
sprite.FadeOut(50, Easing.OutQuint);
|
||||
strongSprite.FadeIn(50, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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 JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -13,19 +14,25 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
internal class DefaultHitExplosion : CircularContainer
|
||||
internal class DefaultHitExplosion : CircularContainer, IHitExplosion
|
||||
{
|
||||
private readonly DrawableHitObject judgedObject;
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
private readonly HitResult result;
|
||||
|
||||
public DefaultHitExplosion(DrawableHitObject judgedObject, HitResult result)
|
||||
[CanBeNull]
|
||||
private Box body;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
public DefaultHitExplosion(HitResult result)
|
||||
{
|
||||
this.judgedObject = judgedObject;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@ -40,26 +47,38 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
if (!result.IsHit())
|
||||
return;
|
||||
|
||||
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new Box
|
||||
body = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = isRim ? colours.BlueDarker : colours.PinkDarker,
|
||||
}
|
||||
};
|
||||
|
||||
updateColour();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
private void updateColour([CanBeNull] DrawableHitObject judgedObject = null)
|
||||
{
|
||||
base.LoadComplete();
|
||||
if (body == null)
|
||||
return;
|
||||
|
||||
bool isRim = (judgedObject?.HitObject as Hit)?.Type == HitType.Rim;
|
||||
body.Colour = isRim ? colours.BlueDarker : colours.PinkDarker;
|
||||
}
|
||||
|
||||
public void Animate(DrawableHitObject drawableHitObject)
|
||||
{
|
||||
updateColour(drawableHitObject);
|
||||
|
||||
this.ScaleTo(3f, 1000, Easing.OutQuint);
|
||||
this.FadeOut(500);
|
||||
|
||||
Expire(true);
|
||||
}
|
||||
|
||||
public void AnimateSecondHit()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,12 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osuTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@ -16,31 +18,35 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
/// <summary>
|
||||
/// A circle explodes from the hit target to indicate a hitobject has been hit.
|
||||
/// </summary>
|
||||
internal class HitExplosion : CircularContainer
|
||||
internal class HitExplosion : PoolableDrawable
|
||||
{
|
||||
public override bool RemoveWhenNotAlive => true;
|
||||
|
||||
[Cached(typeof(DrawableHitObject))]
|
||||
public readonly DrawableHitObject JudgedObject;
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
private readonly HitResult result;
|
||||
|
||||
[CanBeNull]
|
||||
public DrawableHitObject JudgedObject;
|
||||
|
||||
private SkinnableDrawable skinnable;
|
||||
|
||||
public override double LifetimeStart => skinnable.Drawable.LifetimeStart;
|
||||
|
||||
public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd;
|
||||
|
||||
public HitExplosion(DrawableHitObject judgedObject, HitResult result)
|
||||
/// <summary>
|
||||
/// This constructor only exists to meet the <c>new()</c> type constraint of <see cref="DrawablePool{T}"/>.
|
||||
/// </summary>
|
||||
public HitExplosion()
|
||||
: this(HitResult.Great)
|
||||
{
|
||||
}
|
||||
|
||||
public HitExplosion(HitResult result)
|
||||
{
|
||||
JudgedObject = judgedObject;
|
||||
this.result = result;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE);
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
RelativePositionAxes = Axes.Both;
|
||||
}
|
||||
@ -48,7 +54,44 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(result)), _ => new DefaultHitExplosion(JudgedObject, result));
|
||||
InternalChild = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(result)), _ => new DefaultHitExplosion(result));
|
||||
skinnable.OnSkinChanged += runAnimation;
|
||||
}
|
||||
|
||||
public void Apply([CanBeNull] DrawableHitObject drawableHitObject)
|
||||
{
|
||||
JudgedObject = drawableHitObject;
|
||||
}
|
||||
|
||||
protected override void PrepareForUse()
|
||||
{
|
||||
base.PrepareForUse();
|
||||
runAnimation();
|
||||
}
|
||||
|
||||
protected override void FreeAfterUse()
|
||||
{
|
||||
base.FreeAfterUse();
|
||||
|
||||
// clean up transforms on free instead of on prepare as is usually the case
|
||||
// to avoid potentially overriding the effects of VisualiseSecondHit() in the case it is called before PrepareForUse().
|
||||
ApplyTransformsAt(double.MinValue, true);
|
||||
ClearTransforms(true);
|
||||
}
|
||||
|
||||
private void runAnimation()
|
||||
{
|
||||
if (JudgedObject?.Result == null)
|
||||
return;
|
||||
|
||||
double resultTime = JudgedObject.Result.TimeAbsolute;
|
||||
|
||||
LifetimeStart = resultTime;
|
||||
|
||||
using (BeginAbsoluteSequence(resultTime))
|
||||
(skinnable.Drawable as IHitExplosion)?.Animate(JudgedObject);
|
||||
|
||||
LifetimeEnd = skinnable.Drawable.LatestTransformEndTime;
|
||||
}
|
||||
|
||||
private static TaikoSkinComponents getComponentName(HitResult result)
|
||||
@ -68,12 +111,13 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
throw new ArgumentOutOfRangeException(nameof(result), $"Invalid result type: {result}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms this hit explosion to visualise a secondary hit.
|
||||
/// </summary>
|
||||
public void VisualiseSecondHit()
|
||||
public void VisualiseSecondHit(JudgementResult judgementResult)
|
||||
{
|
||||
this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50);
|
||||
using (BeginAbsoluteSequence(judgementResult.TimeAbsolute))
|
||||
{
|
||||
this.ResizeTo(new Vector2(TaikoStrongableHitObject.DEFAULT_STRONG_SIZE), 50);
|
||||
(skinnable.Drawable as IHitExplosion)?.AnimateSecondHit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
24
osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs
Normal file
24
osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// 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.Graphics.Pooling;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Pool for hit explosions of a specific type.
|
||||
/// </summary>
|
||||
internal class HitExplosionPool : DrawablePool<HitExplosion>
|
||||
{
|
||||
private readonly HitResult hitResult;
|
||||
|
||||
public HitExplosionPool(HitResult hitResult)
|
||||
: base(15)
|
||||
{
|
||||
this.hitResult = hitResult;
|
||||
}
|
||||
|
||||
protected override HitExplosion CreateNewDrawable() => new HitExplosion(hitResult);
|
||||
}
|
||||
}
|
23
osu.Game.Rulesets.Taiko/UI/IHitExplosion.cs
Normal file
23
osu.Game.Rulesets.Taiko/UI/IHitExplosion.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// 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.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for hit explosions shown on the playfield's hit target in taiko.
|
||||
/// </summary>
|
||||
public interface IHitExplosion
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows the hit explosion for the supplied <paramref name="drawableHitObject"/>.
|
||||
/// </summary>
|
||||
void Animate(DrawableHitObject drawableHitObject);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the hit explosion to visualise a secondary hit.
|
||||
/// </summary>
|
||||
void AnimateSecondHit();
|
||||
}
|
||||
}
|
@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
private SkinnableDrawable mascot;
|
||||
|
||||
private readonly IDictionary<HitResult, DrawablePool<DrawableTaikoJudgement>> judgementPools = new Dictionary<HitResult, DrawablePool<DrawableTaikoJudgement>>();
|
||||
private readonly IDictionary<HitResult, HitExplosionPool> explosionPools = new Dictionary<HitResult, HitExplosionPool>();
|
||||
|
||||
private ProxyContainer topLevelHitContainer;
|
||||
private Container rightArea;
|
||||
@ -166,10 +167,15 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
RegisterPool<SwellTick, DrawableSwellTick>(100);
|
||||
|
||||
var hitWindows = new TaikoHitWindows();
|
||||
|
||||
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => hitWindows.IsHitResultAllowed(r)))
|
||||
{
|
||||
judgementPools.Add(result, new DrawablePool<DrawableTaikoJudgement>(15));
|
||||
explosionPools.Add(result, new HitExplosionPool(result));
|
||||
}
|
||||
|
||||
AddRangeInternal(judgementPools.Values);
|
||||
AddRangeInternal(explosionPools.Values);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -281,7 +287,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
case TaikoStrongJudgement _:
|
||||
if (result.IsHit)
|
||||
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit();
|
||||
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(result);
|
||||
break;
|
||||
|
||||
case TaikoDrumRollTickJudgement _:
|
||||
@ -315,7 +321,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
private void addExplosion(DrawableHitObject drawableObject, HitResult result, HitType type)
|
||||
{
|
||||
hitExplosionContainer.Add(new HitExplosion(drawableObject, result));
|
||||
hitExplosionContainer.Add(explosionPools[result]
|
||||
.Get(explosion => explosion.Apply(drawableObject)));
|
||||
if (drawableObject.HitObject.Kiai)
|
||||
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user