diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 18d76d02a1..e0276db911 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -38,6 +38,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// public bool MustAlternate { get; internal set; } = true; + public event Action UpdateHitProgress; + public DrawableSwell() : this(null) { @@ -123,7 +125,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables int numHits = ticks.Count(r => r.IsHit); - (MainPiece.Drawable as ISkinnableSwell)?.AnimateSwellProgress(this, numHits); + UpdateHitProgress?.Invoke(numHits, HitObject.RequiredHits); if (numHits == HitObject.RequiredHits) ApplyMaxResult(); @@ -158,7 +160,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { base.UpdateStartTimeStateTransforms(); - (MainPiece.Drawable as ISkinnableSwell)?.AnimateSwellStart(this); } protected override void UpdateHitStateTransforms(ArmedState state) @@ -178,7 +179,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables LifetimeEnd = Time.Current + clear_animation_duration; HandleUserInput = false; - (MainPiece.Drawable as ISkinnableSwell)?.AnimateSwellCompletion(state); break; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/ISkinnableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/ISkinnableSwell.cs deleted file mode 100644 index 9bd169acd7..0000000000 --- a/osu.Game.Rulesets.Taiko/Objects/ISkinnableSwell.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Taiko.Objects.Drawables; - -namespace osu.Game.Rulesets.Taiko.Objects -{ - public interface ISkinnableSwell - { - void AnimateSwellProgress(DrawableTaikoHitObject swell, int numHits); - - void AnimateSwellCompletion(ArmedState state); - - void AnimateSwellStart(DrawableTaikoHitObject swell); - } -} diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultSwell.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultSwell.cs index bdb444db90..852116cbfe 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultSwell.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultSwell.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -15,13 +16,15 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.Skinning.Default { - public partial class DefaultSwell : Container, ISkinnableSwell + public partial class DefaultSwell : Container { private const float target_ring_thick_border = 1.4f; private const float target_ring_thin_border = 1f; private const float target_ring_scale = 5f; private const float inner_ring_alpha = 0.65f; + private DrawableSwell drawableSwell = null!; + private readonly Container bodyContainer; private readonly CircularContainer targetRing; private readonly CircularContainer expandingRing; @@ -102,6 +105,17 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default }); } + [BackgroundDependencyLoader] + private void load(DrawableHitObject hitObject, OsuColour colours) + { + drawableSwell = (DrawableSwell)hitObject; + drawableSwell.UpdateHitProgress += animateSwellProgress; + drawableSwell.ApplyCustomUpdateState += updateStateTransforms; + + expandingRing.Colour = colours.YellowLight; + targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); + } + protected virtual Drawable CreateCentreCircle() { return new SwellCirclePiece() @@ -111,18 +125,11 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default }; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void animateSwellProgress(int numHits, int requiredHits) { - expandingRing.Colour = colours.YellowLight; - targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); - } + float completion = (float)numHits / requiredHits; - public void AnimateSwellProgress(DrawableTaikoHitObject swell, int numHits) - { - float completion = (float)numHits / swell.HitObject.RequiredHits; - - centreCircle.RotateTo((float)(completion * swell.HitObject.Duration / 8), 4000, Easing.OutQuint); + centreCircle.RotateTo((float)(completion * drawableSwell.HitObject.Duration / 8), 4000, Easing.OutQuint); expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint); @@ -132,23 +139,42 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default .FadeTo(completion / 8, 2000, Easing.OutQuint); } - public void AnimateSwellCompletion(ArmedState state) + private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - const double transition_duration = 300; + if (!(drawableHitObject is DrawableSwell drawableSwell)) + return; - bodyContainer.FadeOut(transition_duration, Easing.OutQuad); - bodyContainer.ScaleTo(1.4f, transition_duration); - centreCircle.FadeOut(transition_duration, Easing.OutQuad); + Swell swell = drawableSwell.HitObject; + + using (BeginAbsoluteSequence(swell.StartTime)) + { + if (state == ArmedState.Idle) + expandingRing.FadeTo(0); + + const double ring_appear_offset = 100; + + targetRing.Delay(ring_appear_offset).ScaleTo(target_ring_scale, 400, Easing.OutQuint); + } + + using (BeginAbsoluteSequence(drawableSwell.HitStateUpdateTime)) + { + const double transition_duration = 300; + + bodyContainer.FadeOut(transition_duration, Easing.OutQuad); + bodyContainer.ScaleTo(1.4f, transition_duration); + centreCircle.FadeOut(transition_duration, Easing.OutQuad); + } } - public void AnimateSwellStart(DrawableTaikoHitObject swell) + protected override void Dispose(bool isDisposing) { - if (swell.IsHit == false) - expandingRing.FadeTo(0); + base.Dispose(isDisposing); - const double ring_appear_offset = 100; - - targetRing.Delay(ring_appear_offset).ScaleTo(target_ring_scale, 400, Easing.OutQuint); + if (drawableSwell.IsNotNull()) + { + drawableSwell.UpdateHitProgress -= animateSwellProgress; + drawableSwell.ApplyCustomUpdateState -= updateStateTransforms; + } } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacySwell.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacySwell.cs index e487c5e051..60a0b1d951 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacySwell.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacySwell.cs @@ -12,11 +12,14 @@ using osu.Framework.Audio.Sample; using osu.Game.Audio; using osuTK; using osu.Game.Rulesets.Objects.Drawables; +using osu.Framework.Extensions.ObjectExtensions; namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { - public partial class LegacySwell : Container, ISkinnableSwell + public partial class LegacySwell : Container { + private DrawableSwell drawableSwell = null!; + private Container bodyContainer = null!; private Sprite warning = null!; private Sprite spinnerCircle = null!; @@ -35,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, SkinManager skinManager) + private void load(DrawableHitObject hitObject, ISkinSource skin, SkinManager skinManager) { Child = new Container { @@ -96,49 +99,71 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy } }; + drawableSwell = (DrawableSwell)hitObject; + drawableSwell.UpdateHitProgress += animateSwellProgress; + drawableSwell.ApplyCustomUpdateState += updateStateTransforms; clearSample = skin.GetSample(new SampleInfo("spinner-osu")); } - public void AnimateSwellProgress(DrawableTaikoHitObject swell, int numHits) + private void animateSwellProgress(int numHits, int requiredHits) { - remainingHitsCountdown.Text = $"{swell.HitObject.RequiredHits - numHits}"; + remainingHitsCountdown.Text = $"{requiredHits - numHits}"; spinnerCircle.RotateTo(180f * numHits, 1000, Easing.OutQuint); } - public void AnimateSwellCompletion(ArmedState state) + private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - const double clear_transition_duration = 300; + if (!(drawableHitObject is DrawableSwell drawableSwell)) + return; - bodyContainer.FadeOut(clear_transition_duration, Easing.OutQuad); + Swell swell = drawableSwell.HitObject; - if (state == ArmedState.Hit) + using (BeginAbsoluteSequence(swell.StartTime)) { - if (!samplePlayed) + if (state == ArmedState.Idle) { - clearSample?.Play(); - samplePlayed = true; + remainingHitsCountdown.Text = $"{swell.RequiredHits}"; + samplePlayed = false; } - clearAnimation - .FadeIn(clear_transition_duration, Easing.InQuad) - .ScaleTo(0.8f, clear_transition_duration, Easing.InQuad) - .Delay(700).FadeOut(200, Easing.OutQuad); + const double body_transition_duration = 100; + + warning.FadeOut(body_transition_duration); + bodyContainer.FadeIn(body_transition_duration); + shrinkingRing.ResizeTo(0.1f, swell.Duration); + } + + using (BeginAbsoluteSequence(drawableSwell.HitStateUpdateTime)) + { + const double clear_transition_duration = 300; + + bodyContainer.FadeOut(clear_transition_duration, Easing.OutQuad); + + if (state == ArmedState.Hit) + { + if (!samplePlayed) + { + clearSample?.Play(); + samplePlayed = true; + } + + clearAnimation + .FadeIn(clear_transition_duration, Easing.InQuad) + .ScaleTo(0.8f, clear_transition_duration, Easing.InQuad) + .Delay(700).FadeOut(200, Easing.OutQuad); + } } } - public void AnimateSwellStart(DrawableTaikoHitObject swell) + protected override void Dispose(bool isDisposing) { - if (swell.IsHit == false) + base.Dispose(isDisposing); + + if (drawableSwell.IsNotNull()) { - remainingHitsCountdown.Text = $"{swell.HitObject.RequiredHits}"; - samplePlayed = false; + drawableSwell.UpdateHitProgress -= animateSwellProgress; + drawableSwell.ApplyCustomUpdateState -= updateStateTransforms; } - - const double body_transition_duration = 100; - - warning.FadeOut(body_transition_duration); - bodyContainer.FadeIn(body_transition_duration); - shrinkingRing.ResizeTo(0.1f, swell.HitObject.Duration); } } }