From ca061c4b939fdc8b8782ba0e80885f17da3965d5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Jun 2021 19:41:51 +0900 Subject: [PATCH 1/8] Factor out `SkinnableDrawable` component of the catcher to `SkinnableCatcher` --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 37 ++++++------------- .../UI/SkinnableCatcher.cs | 26 +++++++++++++ 2 files changed, 37 insertions(+), 26 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index ee2986c73c..dce89a9dae 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -17,7 +17,6 @@ using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Skinning; -using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Rulesets.Judgements; using osu.Game.Skinning; using osuTK; @@ -83,17 +82,18 @@ namespace osu.Game.Rulesets.Catch.UI /// private readonly Container droppedObjectTarget; - [Cached] - protected readonly Bindable CurrentStateBindable = new Bindable(); - - public CatcherAnimationState CurrentState => CurrentStateBindable.Value; + public CatcherAnimationState CurrentState + { + get => body.AnimationState.Value; + private set => body.AnimationState.Value = value; + } /// /// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable. /// public const float ALLOWED_CATCH_RANGE = 0.8f; - internal Texture CurrentTexture => ((ICatcherSprite)currentCatcher.Drawable).CurrentTexture; + internal Texture CurrentTexture => ((ICatcherSprite)body.Drawable).CurrentTexture; private bool dashing; @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Catch.UI /// private readonly float catchWidth; - private readonly SkinnableDrawable currentCatcher; + private readonly SkinnableCatcher body; private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR; private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR; @@ -161,13 +161,7 @@ namespace osu.Game.Rulesets.Catch.UI Anchor = Anchor.TopCentre, Origin = Anchor.BottomCentre, }, - currentCatcher = new SkinnableDrawable( - new CatchSkinComponent(CatchSkinComponents.Catcher), - _ => new DefaultCatcher()) - { - Anchor = Anchor.TopCentre, - OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE - }, + body = new SkinnableCatcher(), hitExplosionContainer = new HitExplosionContainer { Anchor = Anchor.TopCentre, @@ -268,17 +262,16 @@ namespace osu.Game.Rulesets.Catch.UI SetHyperDashState(); if (result.IsHit) - updateState(hitObject.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle); + CurrentState = hitObject.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle; else if (!(hitObject is Banana)) - updateState(CatcherAnimationState.Fail); + CurrentState = CatcherAnimationState.Fail; } public void OnRevertResult(DrawableCatchHitObject drawableObject, JudgementResult result) { var catchResult = (CatchJudgementResult)result; - if (CurrentState != catchResult.CatcherAnimationState) - updateState(catchResult.CatcherAnimationState); + CurrentState = catchResult.CatcherAnimationState; if (HyperDashing != catchResult.CatcherHyperDash) { @@ -373,14 +366,6 @@ namespace osu.Game.Rulesets.Catch.UI } } - private void updateState(CatcherAnimationState state) - { - if (CurrentState == state) - return; - - CurrentStateBindable.Value = state; - } - private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) { var caughtObject = getCaughtObject(drawableObject.HitObject); diff --git a/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs b/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs new file mode 100644 index 0000000000..5bd97858b2 --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Catch.Skinning.Default; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class SkinnableCatcher : SkinnableDrawable + { + [Cached] + public readonly Bindable AnimationState = new Bindable(); + + public SkinnableCatcher() + : base(new CatchSkinComponent(CatchSkinComponents.Catcher), _ => new DefaultCatcher()) + { + Anchor = Anchor.TopCentre; + // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling. + OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE; + } + } +} From 9b6ab4360e497a7b0e32fa1d21bf490d8d70e943 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Jun 2021 19:45:58 +0900 Subject: [PATCH 2/8] Use common skinnable catcher in catcher trails --- .../UI/CatcherTrailDisplay.cs | 4 ++-- .../UI/CatcherTrailSprite.cs | 18 +++++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 0aef215797..dbe41121c8 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -120,8 +120,8 @@ namespace osu.Game.Rulesets.Catch.UI { CatcherTrailSprite sprite = trailPool.Get(); - sprite.Texture = catcher.CurrentTexture; - sprite.Anchor = catcher.Anchor; + sprite.AnimationState = catcher.CurrentState; + sprite.Origin = catcher.Origin; sprite.Scale = catcher.Scale; sprite.Blending = BlendingParameters.Additive; sprite.RelativePositionAxes = catcher.RelativePositionAxes; diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs index 0e3e409fac..c4bb0aa1f2 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs @@ -3,32 +3,24 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osuTK; namespace osu.Game.Rulesets.Catch.UI { public class CatcherTrailSprite : PoolableDrawable { - public Texture Texture + public CatcherAnimationState AnimationState { - set => sprite.Texture = value; + set => body.AnimationState.Value = value; } - private readonly Sprite sprite; + private readonly SkinnableCatcher body; public CatcherTrailSprite() { - InternalChild = sprite = new Sprite - { - RelativeSizeAxes = Axes.Both - }; - Size = new Vector2(CatcherArea.CATCHER_SIZE); - - // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling. - OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE; + Origin = Anchor.TopCentre; + InternalChild = body = new SkinnableCatcher(); } protected override void FreeAfterUse() From c094914023aab5e29e71cba493a5858233fbd8cc Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Jun 2021 19:46:48 +0900 Subject: [PATCH 3/8] Simplify catcher trail creation --- osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs | 3 --- osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index dbe41121c8..382e796480 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -121,10 +121,7 @@ namespace osu.Game.Rulesets.Catch.UI CatcherTrailSprite sprite = trailPool.Get(); sprite.AnimationState = catcher.CurrentState; - sprite.Origin = catcher.Origin; sprite.Scale = catcher.Scale; - sprite.Blending = BlendingParameters.Additive; - sprite.RelativePositionAxes = catcher.RelativePositionAxes; sprite.Position = catcher.Position; target.Add(sprite); diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs index c4bb0aa1f2..8417e5a250 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs @@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Catch.UI { Size = new Vector2(CatcherArea.CATCHER_SIZE); Origin = Anchor.TopCentre; + Blending = BlendingParameters.Additive; InternalChild = body = new SkinnableCatcher(); } From 38a56d64d316404ac0ca2260ca1ad2efc46a5b94 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Jun 2021 19:47:18 +0900 Subject: [PATCH 4/8] Rename `CatcherTrailSprite` -> `CatcherTrail` --- ...{CatcherTrailSprite.cs => CatcherTrail.cs} | 4 ++-- .../UI/CatcherTrailDisplay.cs | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) rename osu.Game.Rulesets.Catch/UI/{CatcherTrailSprite.cs => CatcherTrail.cs} (90%) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs similarity index 90% rename from osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs rename to osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index 8417e5a250..90fb59db9a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailSprite.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -7,7 +7,7 @@ using osuTK; namespace osu.Game.Rulesets.Catch.UI { - public class CatcherTrailSprite : PoolableDrawable + public class CatcherTrail : PoolableDrawable { public CatcherAnimationState AnimationState { @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.UI private readonly SkinnableCatcher body; - public CatcherTrailSprite() + public CatcherTrail() { Size = new Vector2(CatcherArea.CATCHER_SIZE); Origin = Anchor.TopCentre; diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 382e796480..7e4a5b6a86 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -19,11 +19,11 @@ namespace osu.Game.Rulesets.Catch.UI { private readonly Catcher catcher; - private readonly DrawablePool trailPool; + private readonly DrawablePool trailPool; - private readonly Container dashTrails; - private readonly Container hyperDashTrails; - private readonly Container endGlowSprites; + private readonly Container dashTrails; + private readonly Container hyperDashTrails; + private readonly Container endGlowSprites; private Color4 hyperDashTrailsColour = Catcher.DEFAULT_HYPER_DASH_COLOUR; @@ -83,10 +83,10 @@ namespace osu.Game.Rulesets.Catch.UI InternalChildren = new Drawable[] { - trailPool = new DrawablePool(30), - dashTrails = new Container { RelativeSizeAxes = Axes.Both }, - hyperDashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, - endGlowSprites = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, + trailPool = new DrawablePool(30), + dashTrails = new Container { RelativeSizeAxes = Axes.Both }, + hyperDashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, + endGlowSprites = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, }; } @@ -116,9 +116,9 @@ namespace osu.Game.Rulesets.Catch.UI Scheduler.AddDelayed(displayTrail, catcher.HyperDashing ? 25 : 50); } - private CatcherTrailSprite createTrailSprite(Container target) + private CatcherTrail createTrailSprite(Container target) { - CatcherTrailSprite sprite = trailPool.Get(); + CatcherTrail sprite = trailPool.Get(); sprite.AnimationState = catcher.CurrentState; sprite.Scale = catcher.Scale; From df16d4baccff180aadb9a441d9315f11f1e67811 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Jun 2021 20:26:33 +0900 Subject: [PATCH 5/8] Remove `CurrentTexture` from catcher --- .../Skinning/Default/DefaultCatcher.cs | 4 +--- osu.Game.Rulesets.Catch/Skinning/ICatcherSprite.cs | 12 ------------ .../Skinning/Legacy/LegacyCatcherNew.cs | 6 +----- .../Skinning/Legacy/LegacyCatcherOld.cs | 7 +------ osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 --- 5 files changed, 3 insertions(+), 29 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch/Skinning/ICatcherSprite.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs index 364fc211a0..e423f21b98 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs @@ -12,12 +12,10 @@ using osu.Game.Rulesets.Catch.UI; namespace osu.Game.Rulesets.Catch.Skinning.Default { - public class DefaultCatcher : CompositeDrawable, ICatcherSprite + public class DefaultCatcher : CompositeDrawable { public Bindable CurrentState { get; } = new Bindable(); - public Texture CurrentTexture => sprite.Texture; - private readonly Sprite sprite; private readonly Dictionary textures = new Dictionary(); diff --git a/osu.Game.Rulesets.Catch/Skinning/ICatcherSprite.cs b/osu.Game.Rulesets.Catch/Skinning/ICatcherSprite.cs deleted file mode 100644 index 073868e947..0000000000 --- a/osu.Game.Rulesets.Catch/Skinning/ICatcherSprite.cs +++ /dev/null @@ -1,12 +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.Framework.Graphics.Textures; - -namespace osu.Game.Rulesets.Catch.Skinning -{ - public interface ICatcherSprite - { - Texture CurrentTexture { get; } - } -} diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs index 2bf8b28aa2..9df87c92ea 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs @@ -9,21 +9,17 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Game.Rulesets.Catch.UI; using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.Catch.Skinning.Legacy { - public class LegacyCatcherNew : CompositeDrawable, ICatcherSprite + public class LegacyCatcherNew : CompositeDrawable { [Resolved] private Bindable currentState { get; set; } - public Texture CurrentTexture => (currentDrawable as TextureAnimation)?.CurrentFrame ?? (currentDrawable as Sprite)?.Texture; - private readonly Dictionary drawables = new Dictionary(); private Drawable currentDrawable; diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs index a8948d2ed0..3e679171b2 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs @@ -3,19 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.Catch.Skinning.Legacy { - public class LegacyCatcherOld : CompositeDrawable, ICatcherSprite + public class LegacyCatcherOld : CompositeDrawable { - public Texture CurrentTexture => (InternalChild as TextureAnimation)?.CurrentFrame ?? (InternalChild as Sprite)?.Texture; - public LegacyCatcherOld() { RelativeSizeAxes = Axes.Both; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index dce89a9dae..1f01dbabb5 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -9,7 +9,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; -using osu.Framework.Graphics.Textures; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -93,8 +92,6 @@ namespace osu.Game.Rulesets.Catch.UI /// public const float ALLOWED_CATCH_RANGE = 0.8f; - internal Texture CurrentTexture => ((ICatcherSprite)body.Drawable).CurrentTexture; - private bool dashing; public bool Dashing From 73e443a0d9c5bc67192fef049d33dc8f477decb5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 16 Jun 2021 14:01:12 +0900 Subject: [PATCH 6/8] Add comments --- osu.Game.Rulesets.Catch/UI/CatcherTrail.cs | 6 ++++++ osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index 90fb59db9a..f273ef23ac 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -7,6 +7,12 @@ using osuTK; namespace osu.Game.Rulesets.Catch.UI { + /// + /// A trail of the catcher. + /// It also represents a hyper dash afterimage. + /// + // TODO: Trails shouldn't be animated when the skin has an animated catcher. + // The animation should be frozen at the animation frame at the time of the trail generation. public class CatcherTrail : PoolableDrawable { public CatcherAnimationState AnimationState diff --git a/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs b/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs index 5bd97858b2..1038af7a48 100644 --- a/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs +++ b/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs @@ -10,6 +10,10 @@ using osuTK; namespace osu.Game.Rulesets.Catch.UI { + /// + /// The visual representation of the . + /// It includes the body part of the catcher and the catcher plate. + /// public class SkinnableCatcher : SkinnableDrawable { [Cached] From 1632450918c9af1955f32fdc1a6a676550087440 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 16 Jun 2021 16:17:14 +0900 Subject: [PATCH 7/8] Add comments --- osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs b/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs index 1038af7a48..fc34ba4c8b 100644 --- a/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs +++ b/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs @@ -16,6 +16,9 @@ namespace osu.Game.Rulesets.Catch.UI /// public class SkinnableCatcher : SkinnableDrawable { + /// + /// This is used by skin elements to determine which texture of the catcher is used. + /// [Cached] public readonly Bindable AnimationState = new Bindable(); From b087c95581e960f80fa26af370aceb15dadf251e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 16 Jun 2021 16:14:42 +0900 Subject: [PATCH 8/8] Use a frozen clock for catcher trails --- osu.Game.Rulesets.Catch/UI/CatcherTrail.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index f273ef23ac..80522ab36b 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; +using osu.Framework.Timing; using osuTK; namespace osu.Game.Rulesets.Catch.UI @@ -11,8 +12,6 @@ namespace osu.Game.Rulesets.Catch.UI /// A trail of the catcher. /// It also represents a hyper dash afterimage. /// - // TODO: Trails shouldn't be animated when the skin has an animated catcher. - // The animation should be frozen at the animation frame at the time of the trail generation. public class CatcherTrail : PoolableDrawable { public CatcherAnimationState AnimationState @@ -27,7 +26,12 @@ namespace osu.Game.Rulesets.Catch.UI Size = new Vector2(CatcherArea.CATCHER_SIZE); Origin = Anchor.TopCentre; Blending = BlendingParameters.Additive; - InternalChild = body = new SkinnableCatcher(); + InternalChild = body = new SkinnableCatcher + { + // Using a frozen clock because trails should not be animated when the skin has an animated catcher. + // TODO: The animation should be frozen at the animation frame at the time of the trail generation. + Clock = new FramedClock(new ManualClock()), + }; } protected override void FreeAfterUse()