1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-21 23:05:34 +08:00

KiaiHitExplosion pool

This commit is contained in:
Nikita-str 2024-12-24 08:41:30 +03:00
parent 7deccd1136
commit d57ed5841a
6 changed files with 153 additions and 101 deletions

View File

@ -7,12 +7,14 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.UI;
using osuTK;
namespace osu.Game.Rulesets.Taiko.Skinning.Default
{
public partial class DefaultKiaiHitExplosion : CircularContainer
public partial class DefaultKiaiHitExplosion : CircularContainer, IAnimatableHitExplosion
{
public override bool RemoveWhenNotAlive => true;
@ -51,14 +53,16 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default
};
}
protected override void LoadComplete()
public void Animate(DrawableHitObject _drawableHitObject)
{
base.LoadComplete();
this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint);
this.FadeOut(250);
}
Expire(true);
public void AnimateSecondHit()
{
this.ScaleTo(new Vector2(TaikoStrongableHitObject.STRONG_SCALE, 3f), 500, Easing.OutQuint);
this.FadeOut(250);
}
}
}

View File

@ -2,35 +2,23 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using osuTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
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;
using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Taiko.UI
{
/// <summary>
/// A circle explodes from the hit target to indicate a hitobject has been hit.
/// </summary>
internal partial class HitExplosion : PoolableDrawable
internal partial class HitExplosion : HitExplosionBase
{
public override bool RemoveWhenNotAlive => true;
public override bool RemoveCompletedTransforms => false;
private readonly HitResult result;
private double? secondHitTime;
public DrawableHitObject? JudgedObject;
private SkinnableDrawable skinnable = null!;
/// <summary>
/// This constructor only exists to meet the <c>new()</c> type constraint of <see cref="DrawablePool{T}"/>.
/// </summary>
@ -42,60 +30,12 @@ namespace osu.Game.Rulesets.Taiko.UI
public HitExplosion(HitResult result)
{
this.result = result;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE);
RelativeSizeAxes = Axes.Both;
RelativePositionAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = skinnable = new SkinnableDrawable(new TaikoSkinComponentLookup(getComponentName(result)), _ => new DefaultHitExplosion(result));
skinnable.OnSkinChanged += runAnimation;
}
public void Apply(DrawableHitObject? drawableHitObject)
{
JudgedObject = drawableHitObject;
secondHitTime = null;
}
protected override void PrepareForUse()
{
base.PrepareForUse();
runAnimation();
}
private void runAnimation()
{
if (JudgedObject?.Result == null)
return;
double resultTime = JudgedObject.Result.TimeAbsolute;
LifetimeStart = resultTime;
ApplyTransformsAt(double.MinValue, true);
ClearTransforms(true);
using (BeginAbsoluteSequence(resultTime))
(skinnable.Drawable as IAnimatableHitExplosion)?.Animate(JudgedObject);
if (secondHitTime != null)
{
using (BeginAbsoluteSequence(secondHitTime.Value))
{
(skinnable.Drawable as IAnimatableHitExplosion)?.AnimateSecondHit();
}
}
LifetimeEnd = skinnable.Drawable.LatestTransformEndTime;
}
protected override SkinnableDrawable OnLoadSkinnableCreate() =>
new SkinnableDrawable(new TaikoSkinComponentLookup(getComponentName(result)), _ => new DefaultHitExplosion(result));
private static TaikoSkinComponents getComponentName(HitResult result)
{
@ -113,11 +53,5 @@ namespace osu.Game.Rulesets.Taiko.UI
throw new ArgumentOutOfRangeException(nameof(result), $"Invalid result type: {result}");
}
public void VisualiseSecondHit(JudgementResult judgementResult)
{
secondHitTime = judgementResult.TimeAbsolute;
runAnimation();
}
}
}

View File

@ -0,0 +1,87 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Pooling;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko.UI
{
/// <summary>
/// A base class for taiko explosions from target hitting to indicate a hitobject has been hit.
/// </summary>
internal abstract partial class HitExplosionBase : PoolableDrawable
{
protected abstract SkinnableDrawable OnLoadSkinnableCreate();
public override bool RemoveWhenNotAlive => true;
public override bool RemoveCompletedTransforms => false;
protected double? SecondHitTime;
public DrawableHitObject? JudgedObject;
protected SkinnableDrawable Skinnable = null!;
public HitExplosionBase()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = Skinnable = OnLoadSkinnableCreate();
Skinnable.OnSkinChanged += RunAnimation;
}
public void Apply(DrawableHitObject? drawableHitObject)
{
JudgedObject = drawableHitObject;
SecondHitTime = null;
}
protected override void PrepareForUse()
{
base.PrepareForUse();
RunAnimation();
}
protected void RunAnimation()
{
if (JudgedObject?.Result is null) return;
double resultTime = JudgedObject.Result.TimeAbsolute;
LifetimeStart = resultTime;
// Clear transforms
ApplyTransformsAt(double.MinValue, true);
ClearTransforms(true);
if (Skinnable.Drawable is IAnimatableHitExplosion animatable)
{
using (BeginAbsoluteSequence(resultTime))
animatable.Animate(JudgedObject);
if (SecondHitTime != null)
using (BeginAbsoluteSequence(SecondHitTime.Value))
animatable.AnimateSecondHit();
}
LifetimeEnd = Skinnable.Drawable.LatestTransformEndTime;
}
public void VisualiseSecondHit(JudgementResult judgementResult)
{
SecondHitTime = judgementResult.TimeAbsolute;
RunAnimation();
}
}
}

View File

@ -1,9 +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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Pooling;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Skinning.Default;
@ -12,37 +10,30 @@ using osuTK;
namespace osu.Game.Rulesets.Taiko.UI
{
public partial class KiaiHitExplosion : Container
/// <summary>
/// An explosion from the hit target in Kiai mode to indicate a hitobject has been hit.
/// </summary>
internal partial class KiaiHitExplosion : HitExplosionBase
{
public override bool RemoveWhenNotAlive => true;
[Cached(typeof(DrawableHitObject))]
public readonly DrawableHitObject JudgedObject;
private readonly HitType hitType;
private SkinnableDrawable skinnable = null!;
/// <summary>
/// This constructor only exists to meet the <c>new()</c> type constraint of <see cref="DrawablePool{T}"/>.
/// </summary>
public KiaiHitExplosion() : this(HitType.Centre) { }
public override double LifetimeStart => skinnable.Drawable.LifetimeStart;
public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd;
public KiaiHitExplosion(DrawableHitObject judgedObject, HitType hitType)
public KiaiHitExplosion(HitType hitType)
{
JudgedObject = judgedObject;
this.hitType = hitType;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1);
}
[BackgroundDependencyLoader]
private void load()
public KiaiHitExplosion(DrawableHitObject judgedObject, HitType hitType) : this(hitType)
{
Child = skinnable = new SkinnableDrawable(new TaikoSkinComponentLookup(TaikoSkinComponents.TaikoExplosionKiai), _ => new DefaultKiaiHitExplosion(hitType));
Apply(judgedObject);
}
protected override SkinnableDrawable OnLoadSkinnableCreate() =>
new SkinnableDrawable(new TaikoSkinComponentLookup(TaikoSkinComponents.TaikoExplosionKiai), _ => new DefaultKiaiHitExplosion(hitType));
}
}

View 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.Framework.Graphics.Pooling;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.UI
{
/// <summary>
/// Pool for hit explosions of a specific type.
/// </summary>
internal partial class KiaiHitExplosionPool : DrawablePool<KiaiHitExplosion>
{
private readonly HitType hitType;
public KiaiHitExplosionPool(HitType hitType) : base(15)
{
this.hitType = hitType;
}
protected override KiaiHitExplosion CreateNewDrawable() => new KiaiHitExplosion(hitType);
}
}

View File

@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI
private JudgementPooler<DrawableTaikoJudgement> judgementPooler = null!;
private readonly IDictionary<HitResult, HitExplosionPool> explosionPools = new Dictionary<HitResult, HitExplosionPool>();
private readonly IDictionary<HitType, KiaiHitExplosionPool> kiaiExplosionPools = new Dictionary<HitType, KiaiHitExplosionPool>();
private ProxyContainer topLevelHitContainer = null!;
private InputDrum inputDrum = null!;
@ -202,6 +203,10 @@ namespace osu.Game.Rulesets.Taiko.UI
explosionPools.Add(result, new HitExplosionPool(result));
AddRangeInternal(explosionPools.Values);
foreach (var type in Enum.GetValues<HitType>())
kiaiExplosionPools.Add(type, new KiaiHitExplosionPool(type));
AddRangeInternal(kiaiExplosionPools.Values);
AddRangeInternal(poolsHit.Values);
}
@ -320,7 +325,11 @@ namespace osu.Game.Rulesets.Taiko.UI
{
case TaikoStrongJudgement:
if (result.IsHit)
{
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(result);
if (result.HitObject.Kiai)
kiaiExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(result);
}
break;
case TaikoDrumRollTickJudgement:
@ -356,8 +365,12 @@ namespace osu.Game.Rulesets.Taiko.UI
{
hitExplosionContainer.Add(explosionPools[result]
.Get(explosion => explosion.Apply(drawableObject)));
if (drawableObject.HitObject.Kiai)
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
//TODO: should we check `result.IsHit` ?
if (drawableObject.HitObject.Kiai && result.IsHit())
kiaiExplosionContainer.Add(
kiaiExplosionPools[type].Get(
explosion => explosion.Apply(drawableObject)));
}
private partial class ProxyContainer : LifetimeManagementContainer