1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-20 17:29:54 +08:00

remove ISkinnableSwell

This commit removes ISkinnableSwell for taiko swell animations. In place
of it, an event named UpdateHitProgress is added to DrawableSwell, and
the skin swells are converted to listen to said event and
ApplyCustomUpdateState, like how spinner skinning is implemented for
std.
This commit is contained in:
Nathan Du
2025-01-31 17:51:35 +08:00
Unverified
parent 4fd8a4dc5a
commit 2a5540b392
4 changed files with 101 additions and 67 deletions
@@ -38,6 +38,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// </summary>
public bool MustAlternate { get; internal set; } = true;
public event Action<int, int> 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;
}
}
@@ -1,17 +0,0 @@
// 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;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Objects
{
public interface ISkinnableSwell
{
void AnimateSwellProgress(DrawableTaikoHitObject<Swell> swell, int numHits);
void AnimateSwellCompletion(ArmedState state);
void AnimateSwellStart(DrawableTaikoHitObject<Swell> swell);
}
}
@@ -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> 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> 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;
}
}
}
}
@@ -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> 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> 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);
}
}
}