From 18534d64ab8128d2f36b1424ec95c505b9a194ae Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Tue, 21 Sep 2021 19:52:54 -0700 Subject: [PATCH 01/11] Improve slider ball and follow circle animations --- .../Objects/Drawables/DrawableSlider.cs | 4 ---- .../Skinning/Default/SliderBall.cs | 19 ++++++++++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 3acec4498d58..8f5cf2672116 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -327,13 +327,9 @@ protected override void UpdateHitStateTransforms(ArmedState state) const float fade_out_time = 450; - // intentionally pile on an extra FadeOut to make it happen much faster. - Ball.FadeOut(fade_out_time / 4, Easing.Out); - switch (state) { case ArmedState.Hit: - Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out); if (SliderBody?.SnakingOut.Value == true) Body.FadeOut(40); // short fade to allow for any body colour to smoothly disappear. break; diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs index 8943a9107675..d97b29ed821a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs @@ -103,14 +103,22 @@ private set tracking = value; if (InputTracksVisualSize) - followCircle.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint); + { + if (tracking) + followCircle.ScaleTo(2f, 200, Easing.OutQuint); + else + followCircle.ScaleTo(1.6f, 200, Easing.None); + } else { // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. - followCircle.ScaleTo(tracking ? 2.4f : 1f); + followCircle.ScaleTo(tracking ? 2f : 1f); } - followCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint); + if (tracking) + followCircle.FadeIn(100, Easing.OutQuint); + else + followCircle.FadeOut(200, Easing.InQuint); } } @@ -205,6 +213,11 @@ public void UpdateProgress(double completionProgress) ball.Rotation = -90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI); lastPosition = newPos; + + if (completionProgress >= 1f) + ball.FadeOut(); + else + ball.FadeIn(); } private class FollowCircleContainer : CircularContainer From 024cf2ee08b1004ff53e1a64296bdee140150a35 Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Tue, 21 Sep 2021 20:53:52 -0700 Subject: [PATCH 02/11] Revert follow circle size to 2.4x --- osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs index d97b29ed821a..9dd60bc17c5e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs @@ -105,14 +105,14 @@ private set if (InputTracksVisualSize) { if (tracking) - followCircle.ScaleTo(2f, 200, Easing.OutQuint); + followCircle.ScaleTo(2.4f, 200, Easing.OutQuint); else followCircle.ScaleTo(1.6f, 200, Easing.None); } else { // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. - followCircle.ScaleTo(tracking ? 2f : 1f); + followCircle.ScaleTo(tracking ? 2.4f : 1f); } if (tracking) From cffc4248f581420f60cea5c425d188b793a400fb Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Wed, 22 Sep 2021 01:30:21 -0700 Subject: [PATCH 03/11] Refactor slider ball and only change anims for legacy skins --- .../TestSceneSliderApplication.cs | 4 +- .../Objects/Drawables/DrawableSlider.cs | 4 +- .../Drawables/DrawableSliderBall.cs} | 139 +++++------------- .../Skinning/Default/DefaultFollowCircle.cs | 60 ++++++++ .../Skinning/Default/DefaultSliderBall.cs | 58 ++++++++ .../Skinning/Legacy/LegacyFollowCircle.cs | 61 ++++++++ .../Legacy/OsuLegacySkinTransformer.cs | 12 +- 7 files changed, 227 insertions(+), 111 deletions(-) rename osu.Game.Rulesets.Osu/{Skinning/Default/SliderBall.cs => Objects/Drawables/DrawableSliderBall.cs} (66%) create mode 100644 osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index e698766aac95..e86a5947fe1c 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -89,10 +89,10 @@ public void TestBallTintChangedOnAccentChange() }); AddStep("set accent white", () => dho.AccentColour.Value = Color4.White); - AddAssert("ball is white", () => dho.ChildrenOfType().Single().AccentColour == Color4.White); + AddAssert("ball is white", () => dho.ChildrenOfType().Single().AccentColour == Color4.White); AddStep("set accent red", () => dho.AccentColour.Value = Color4.Red); - AddAssert("ball is red", () => dho.ChildrenOfType().Single().AccentColour == Color4.Red); + AddAssert("ball is red", () => dho.ChildrenOfType().Single().AccentColour == Color4.Red); } private Slider prepareObject(Slider slider) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 8f5cf2672116..831088bbc1ed 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -29,7 +29,7 @@ public class DrawableSlider : DrawableOsuHitObject public DrawableSliderHead HeadCircle => headContainer.Child; public DrawableSliderTail TailCircle => tailContainer.Child; - public SliderBall Ball { get; private set; } + public DrawableSliderBall Ball { get; private set; } public SkinnableDrawable Body { get; private set; } /// @@ -73,7 +73,7 @@ private void load() repeatContainer = new Container { RelativeSizeAxes = Axes.Both }, headContainer = new Container { RelativeSizeAxes = Axes.Both }, OverlayElementContainer = new Container { RelativeSizeAxes = Axes.Both, }, - Ball = new SliderBall(this) + Ball = new DrawableSliderBall(this) { GetInitialHitAction = () => HeadCircle.HitAction, BypassAutoSizeAxes = Axes.Both, diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs similarity index 66% rename from osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs rename to osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index 9dd60bc17c5e..2f0edad5d21c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -1,28 +1,23 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; -namespace osu.Game.Rulesets.Osu.Skinning.Default +namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition, IHasAccentColour + public class DrawableSliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition, IHasAccentColour { public Func GetInitialHitAction; @@ -33,16 +28,18 @@ public Color4 AccentColour } /// - /// Whether to track accurately to the visual size of this . + /// Whether to track accurately to the visual size of this . /// If false, tracking will be performed at the final scale at all times. /// public bool InputTracksVisualSize = true; - private readonly Drawable followCircle; private readonly DrawableSlider drawableSlider; - private readonly Drawable ball; + private readonly SkinnableDrawable followCircle; + private readonly SkinnableDrawable ball; + + private readonly FollowReceptor followArea; - public SliderBall(DrawableSlider drawableSlider) + public DrawableSliderBall(DrawableSlider drawableSlider) { this.drawableSlider = drawableSlider; @@ -50,15 +47,17 @@ public SliderBall(DrawableSlider drawableSlider) Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); - Children = new[] + Children = new Drawable[] { - followCircle = new FollowCircleContainer + followArea = new FollowReceptor { + Anchor = Anchor.Centre, Origin = Anchor.Centre, + }, + followCircle = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()) + { Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()), + Origin = Anchor.Centre, }, ball = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBall), _ => new DefaultSliderBall()) { @@ -90,37 +89,7 @@ public override void ApplyTransformsAt(double time, bool propagateChildren = fal base.ApplyTransformsAt(time, false); } - private bool tracking; - - public bool Tracking - { - get => tracking; - private set - { - if (value == tracking) - return; - - tracking = value; - - if (InputTracksVisualSize) - { - if (tracking) - followCircle.ScaleTo(2.4f, 200, Easing.OutQuint); - else - followCircle.ScaleTo(1.6f, 200, Easing.None); - } - else - { - // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. - followCircle.ScaleTo(tracking ? 2.4f : 1f); - } - - if (tracking) - followCircle.FadeIn(100, Easing.OutQuint); - else - followCircle.FadeOut(200, Easing.InQuint); - } - } + public bool Tracking { get; private set; } /// /// If the cursor moves out of the ball's radius we still need to be able to receive positional updates to stop tracking. @@ -176,7 +145,7 @@ protected override void Update() // in valid time range Time.Current >= drawableSlider.HitObject.StartTime && Time.Current < drawableSlider.HitObject.EndTime && // in valid position range - lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) && + lastScreenSpaceMousePosition.HasValue && followArea.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) && // valid action (actions?.Any(isValidTrackingAction) ?? false); @@ -220,73 +189,37 @@ public void UpdateProgress(double completionProgress) ball.FadeIn(); } - private class FollowCircleContainer : CircularContainer + private class FollowReceptor : CircularContainer { public override bool HandlePositionalInput => true; - } - public class DefaultFollowCircle : CompositeDrawable - { - public DefaultFollowCircle() - { - RelativeSizeAxes = Axes.Both; - - InternalChild = new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderThickness = 5, - BorderColour = Color4.Orange, - Blending = BlendingParameters.Additive, - Child = new Box - { - Colour = Color4.Orange, - RelativeSizeAxes = Axes.Both, - Alpha = 0.2f, - } - }; - } - } - - public class DefaultSliderBall : CompositeDrawable - { - private Box box; + private DrawableSliderBall sliderBall; [BackgroundDependencyLoader] - private void load(DrawableHitObject drawableObject, ISkinSource skin) + private void load(DrawableHitObject drawableObject) { var slider = (DrawableSlider)drawableObject; + sliderBall = slider.Ball; RelativeSizeAxes = Axes.Both; - float radius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS; + slider.Tracking.BindValueChanged(trackingChanged, true); + } + + private void trackingChanged(ValueChangedEvent e) + { + bool tracking = e.NewValue; - InternalChild = new CircularContainer + if (sliderBall.InputTracksVisualSize) + this.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint); + else { - Masking = true, - RelativeSizeAxes = Axes.Both, - Scale = new Vector2(radius / OsuHitObject.OBJECT_RADIUS), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Blending = BlendingParameters.Additive, - BorderThickness = 10, - BorderColour = Color4.White, - Alpha = 1, - Child = box = new Box - { - Blending = BlendingParameters.Additive, - RelativeSizeAxes = Axes.Both, - Colour = Color4.White, - AlwaysPresent = true, - Alpha = 0 - } - }; + // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. + this.ScaleTo(tracking ? 2.4f : 1f); + } - slider.Tracking.BindValueChanged(trackingChanged, true); + this.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint); } - - private void trackingChanged(ValueChangedEvent tracking) => - box.FadeTo(tracking.NewValue ? 0.3f : 0.05f, 200, Easing.OutQuint); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs new file mode 100644 index 000000000000..22e16da076da --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs @@ -0,0 +1,60 @@ +// 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.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Skinning.Default +{ + public class DefaultFollowCircle : CompositeDrawable + { + private DrawableSliderBall sliderBall; + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject) + { + var slider = (DrawableSlider)drawableObject; + sliderBall = slider.Ball; + + RelativeSizeAxes = Axes.Both; + + InternalChild = new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = 5, + BorderColour = Color4.Orange, + Blending = BlendingParameters.Additive, + Child = new Box + { + Colour = Color4.Orange, + RelativeSizeAxes = Axes.Both, + Alpha = 0.2f, + } + }; + + slider.Tracking.BindValueChanged(trackingChanged, true); + } + + private void trackingChanged(ValueChangedEvent e) + { + bool tracking = e.NewValue; + + if (sliderBall.InputTracksVisualSize) + this.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint); + else + { + // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. + this.ScaleTo(tracking ? 2.4f : 1f); + } + + this.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs new file mode 100644 index 000000000000..8de25dfe0f32 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs @@ -0,0 +1,58 @@ +// 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.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Skinning.Default +{ + public class DefaultSliderBall : CompositeDrawable + { + private Box box; + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject, ISkinSource skin) + { + var slider = (DrawableSlider)drawableObject; + + RelativeSizeAxes = Axes.Both; + + float radius = skin.GetConfig(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS; + + InternalChild = new CircularContainer + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(radius / OsuHitObject.OBJECT_RADIUS), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + BorderThickness = 10, + BorderColour = Color4.White, + Alpha = 1, + Child = box = new Box + { + Blending = BlendingParameters.Additive, + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + AlwaysPresent = true, + Alpha = 0 + } + }; + + slider.Tracking.BindValueChanged(trackingChanged, true); + } + + private void trackingChanged(ValueChangedEvent tracking) => + box.FadeTo(tracking.NewValue ? 0.3f : 0.05f, 200, Easing.OutQuint); + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs new file mode 100644 index 000000000000..bae4b8de41b2 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs @@ -0,0 +1,61 @@ +// 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.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Rulesets.Osu.Skinning.Legacy +{ + public class LegacyFollowCircle : CompositeDrawable + { + private readonly Drawable animationContent; + private DrawableSliderBall sliderBall; + + public LegacyFollowCircle(Drawable animationContent) + { + this.animationContent = animationContent; + } + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject) + { + var slider = (DrawableSlider)drawableObject; + sliderBall = slider.Ball; + + RelativeSizeAxes = Axes.Both; + + InternalChild = animationContent; + animationContent.Anchor = Anchor.Centre; + animationContent.Origin = Anchor.Centre; + + slider.Tracking.BindValueChanged(trackingChanged, true); + } + + private void trackingChanged(ValueChangedEvent e) + { + bool tracking = e.NewValue; + + if (sliderBall.InputTracksVisualSize) + { + if (tracking) + this.ScaleTo(2.4f, 200, Easing.OutQuint); + else + this.ScaleTo(1.9f, 200, Easing.None); + } + else + { + // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. + this.ScaleTo(tracking ? 2.4f : 1f); + } + + if (tracking) + this.FadeIn(100, Easing.OutQuint); + else + this.FadeOut(200, Easing.InQuint); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 8a24e364205b..0f42a1d33efd 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -36,11 +36,15 @@ public override Drawable GetDrawableComponent(ISkinComponent component) return this.GetAnimation(component.LookupName, true, false, true, startAtCurrentTime: false); case OsuSkinComponents.SliderFollowCircle: - var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true); - if (followCircle != null) + var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true); + if (followCircleContent != null) + { // follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x - followCircle.Scale *= 0.5f; - return followCircle; + followCircleContent.Scale *= 0.5f; + return new LegacyFollowCircle(followCircleContent); + } + + return null; case OsuSkinComponents.SliderBall: var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: ""); From 6b25b4ea8899f363450e9d3221177344737458bc Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Wed, 22 Sep 2021 02:16:37 -0700 Subject: [PATCH 04/11] Add back fast fade out for default slider balls and follow circles --- .../Objects/Drawables/DrawableSliderBall.cs | 7 ----- .../Skinning/Default/DefaultFollowCircle.cs | 25 +++++++++++++++--- .../Skinning/Default/DefaultSliderBall.cs | 26 ++++++++++++++++++- .../Skinning/Legacy/LegacyFollowCircle.cs | 7 +++-- .../Skinning/Legacy/LegacySliderBall.cs | 23 ++++++++++++++-- 5 files changed, 70 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index 2f0edad5d21c..0ba87945bdea 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -182,11 +182,6 @@ public void UpdateProgress(double completionProgress) ball.Rotation = -90 + (float)(-Math.Atan2(diff.X, diff.Y) * 180 / Math.PI); lastPosition = newPos; - - if (completionProgress >= 1f) - ball.FadeOut(); - else - ball.FadeIn(); } private class FollowReceptor : CircularContainer @@ -217,8 +212,6 @@ private void trackingChanged(ValueChangedEvent e) // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. this.ScaleTo(tracking ? 2.4f : 1f); } - - this.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs index 22e16da076da..5b9e54e89f73 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs @@ -14,15 +14,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default { public class DefaultFollowCircle : CompositeDrawable { - private DrawableSliderBall sliderBall; + private DrawableSlider slider; [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) { - var slider = (DrawableSlider)drawableObject; - sliderBall = slider.Ball; + slider = (DrawableSlider)drawableObject; RelativeSizeAxes = Axes.Both; + Alpha = 1f; InternalChild = new CircularContainer { @@ -40,13 +40,14 @@ private void load(DrawableHitObject drawableObject) }; slider.Tracking.BindValueChanged(trackingChanged, true); + slider.ApplyCustomUpdateState += updateStateTransforms; } private void trackingChanged(ValueChangedEvent e) { bool tracking = e.NewValue; - if (sliderBall.InputTracksVisualSize) + if (slider.Ball.InputTracksVisualSize) this.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint); else { @@ -56,5 +57,21 @@ private void trackingChanged(ValueChangedEvent e) this.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint); } + + private void updateStateTransforms(DrawableHitObject obj, ArmedState state) + { + using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) + { + const float fade_out_time = 450; + + this.FadeOut(fade_out_time / 4, Easing.Out); + switch (state) + { + case ArmedState.Hit: + this.ScaleTo(slider.HitObject.Scale * 1.4f, fade_out_time, Easing.Out); + break; + } + } + } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs index 8de25dfe0f32..b5626a0986b6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs @@ -19,10 +19,12 @@ public class DefaultSliderBall : CompositeDrawable { private Box box; + private DrawableSlider slider; + [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject, ISkinSource skin) { - var slider = (DrawableSlider)drawableObject; + slider = (DrawableSlider)drawableObject; RelativeSizeAxes = Axes.Both; @@ -50,9 +52,31 @@ private void load(DrawableHitObject drawableObject, ISkinSource skin) }; slider.Tracking.BindValueChanged(trackingChanged, true); + slider.ApplyCustomUpdateState += updateStateTransforms; } private void trackingChanged(ValueChangedEvent tracking) => box.FadeTo(tracking.NewValue ? 0.3f : 0.05f, 200, Easing.OutQuint); + + private void updateStateTransforms(DrawableHitObject obj, ArmedState state) + { + using (BeginAbsoluteSequence(slider.StateUpdateTime)) + { + this.FadeIn(); + } + + using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) + { + const float fade_out_time = 450; + + this.FadeOut(fade_out_time / 4, Easing.Out); + switch (state) + { + case ArmedState.Hit: + this.ScaleTo(slider.HitObject.Scale * 1.4f, fade_out_time, Easing.Out); + break; + } + } + } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs index bae4b8de41b2..92b4ac1da4db 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public class LegacyFollowCircle : CompositeDrawable { private readonly Drawable animationContent; - private DrawableSliderBall sliderBall; + private DrawableSlider slider; public LegacyFollowCircle(Drawable animationContent) { @@ -23,8 +23,7 @@ public LegacyFollowCircle(Drawable animationContent) [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) { - var slider = (DrawableSlider)drawableObject; - sliderBall = slider.Ball; + slider = (DrawableSlider)drawableObject; RelativeSizeAxes = Axes.Both; @@ -39,7 +38,7 @@ private void trackingChanged(ValueChangedEvent e) { bool tracking = e.NewValue; - if (sliderBall.InputTracksVisualSize) + if (slider.Ball.InputTracksVisualSize) { if (tracking) this.ScaleTo(2.4f, 200, Easing.OutQuint); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs index e4e148366563..f9b2a0179c52 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs @@ -5,6 +5,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; using osuTK.Graphics; @@ -13,9 +15,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public class LegacySliderBall : CompositeDrawable { private readonly Drawable animationContent; - private readonly ISkin skin; + private DrawableSlider slider; + private Sprite layerNd; private Sprite layerSpec; @@ -28,8 +31,10 @@ public LegacySliderBall(Drawable animationContent, ISkin skin) } [BackgroundDependencyLoader] - private void load() + private void load(DrawableHitObject drawableObject) { + slider = (DrawableSlider)drawableObject; + var ballColour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White; InternalChildren = new[] @@ -54,6 +59,8 @@ private void load() Blending = BlendingParameters.Additive, }, }; + + slider.ApplyCustomUpdateState += updateStateTransforms; } protected override void UpdateAfterChildren() @@ -66,5 +73,17 @@ protected override void UpdateAfterChildren() layerNd.Rotation = -appliedRotation; layerSpec.Rotation = -appliedRotation; } + + private void updateStateTransforms(DrawableHitObject obj, ArmedState state) + { + using (BeginAbsoluteSequence(slider.StateUpdateTime)) + { + this.FadeIn(); + } + using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) + { + this.FadeOut(); + } + } } } From 4d4ddeef9b4add4bcfc3cf8f01f2573d299f1ff8 Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Wed, 22 Sep 2021 02:39:24 -0700 Subject: [PATCH 05/11] Adjust default slider ball and follow circle fast fade outs --- .../Skinning/Default/DefaultFollowCircle.cs | 8 +------- .../Skinning/Default/DefaultSliderBall.cs | 3 ++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs index 5b9e54e89f73..69805090fd62 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs @@ -62,15 +62,9 @@ private void updateStateTransforms(DrawableHitObject obj, ArmedState state) { using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) { - const float fade_out_time = 450; + const float fade_out_time = 100; this.FadeOut(fade_out_time / 4, Easing.Out); - switch (state) - { - case ArmedState.Hit: - this.ScaleTo(slider.HitObject.Scale * 1.4f, fade_out_time, Easing.Out); - break; - } } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs index b5626a0986b6..33ea883556e1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs @@ -63,6 +63,7 @@ private void updateStateTransforms(DrawableHitObject obj, ArmedState state) using (BeginAbsoluteSequence(slider.StateUpdateTime)) { this.FadeIn(); + this.ScaleTo(1f); } using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) @@ -73,7 +74,7 @@ private void updateStateTransforms(DrawableHitObject obj, ArmedState state) switch (state) { case ArmedState.Hit: - this.ScaleTo(slider.HitObject.Scale * 1.4f, fade_out_time, Easing.Out); + this.ScaleTo(1.4f, fade_out_time, Easing.Out); break; } } From 8a697f08baa6838509b8b663fdcf81373a7da5b0 Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Wed, 22 Sep 2021 19:53:07 -0700 Subject: [PATCH 06/11] Move slider ball transforms to HitObjectApplied event --- .../Objects/Drawables/DrawableSliderBall.cs | 6 ++++-- .../Skinning/Default/DefaultFollowCircle.cs | 12 ++++++------ .../Skinning/Default/DefaultSliderBall.cs | 14 +++++++++----- .../Skinning/Legacy/LegacyFollowCircle.cs | 4 ++-- .../Skinning/Legacy/LegacySliderBall.cs | 9 +++++---- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index 0ba87945bdea..de56e14d669c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -21,6 +21,8 @@ public class DrawableSliderBall : CircularContainer, ISliderProgress, IRequireHi { public Func GetInitialHitAction; + public const float FOLLOW_AREA = 2.4f; + public Color4 AccentColour { get => ball.Colour; @@ -206,11 +208,11 @@ private void trackingChanged(ValueChangedEvent e) bool tracking = e.NewValue; if (sliderBall.InputTracksVisualSize) - this.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint); + this.ScaleTo(tracking ? FOLLOW_AREA : 1f, 300, Easing.OutQuint); else { // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. - this.ScaleTo(tracking ? 2.4f : 1f); + this.ScaleTo(tracking ? FOLLOW_AREA : 1f); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs index 69805090fd62..aa0d2791666e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs @@ -40,7 +40,7 @@ private void load(DrawableHitObject drawableObject) }; slider.Tracking.BindValueChanged(trackingChanged, true); - slider.ApplyCustomUpdateState += updateStateTransforms; + slider.HitObjectApplied += hitObjectApplied; } private void trackingChanged(ValueChangedEvent e) @@ -48,21 +48,21 @@ private void trackingChanged(ValueChangedEvent e) bool tracking = e.NewValue; if (slider.Ball.InputTracksVisualSize) - this.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint); + this.ScaleTo(tracking ? DrawableSliderBall.FOLLOW_AREA: 1f, 300, Easing.OutQuint); else { // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. - this.ScaleTo(tracking ? 2.4f : 1f); + this.ScaleTo(tracking ? DrawableSliderBall.FOLLOW_AREA : 1f); } this.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint); } - private void updateStateTransforms(DrawableHitObject obj, ArmedState state) + private void hitObjectApplied(DrawableHitObject obj) { - using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) + using (BeginAbsoluteSequence(slider.HitObject.EndTime)) { - const float fade_out_time = 100; + const float fade_out_time = 450; this.FadeOut(fade_out_time / 4, Easing.Out); } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs index 33ea883556e1..eaf63962d93b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs @@ -52,25 +52,29 @@ private void load(DrawableHitObject drawableObject, ISkinSource skin) }; slider.Tracking.BindValueChanged(trackingChanged, true); + slider.HitObjectApplied += hitObjectApplied; slider.ApplyCustomUpdateState += updateStateTransforms; } private void trackingChanged(ValueChangedEvent tracking) => box.FadeTo(tracking.NewValue ? 0.3f : 0.05f, 200, Easing.OutQuint); - - private void updateStateTransforms(DrawableHitObject obj, ArmedState state) + + private void hitObjectApplied(DrawableHitObject obj) { - using (BeginAbsoluteSequence(slider.StateUpdateTime)) + using (BeginAbsoluteSequence(slider.HitObject.StartTime)) { this.FadeIn(); this.ScaleTo(1f); } + } - using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) + private void updateStateTransforms(DrawableHitObject obj, ArmedState state) + { + using (BeginAbsoluteSequence(slider.HitObject.EndTime)) { const float fade_out_time = 450; - this.FadeOut(fade_out_time / 4, Easing.Out); + this.FadeOut(fade_out_time / 4, Easing.In); switch (state) { case ArmedState.Hit: diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs index 92b4ac1da4db..b1803ec9a61b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs @@ -41,14 +41,14 @@ private void trackingChanged(ValueChangedEvent e) if (slider.Ball.InputTracksVisualSize) { if (tracking) - this.ScaleTo(2.4f, 200, Easing.OutQuint); + this.ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200, Easing.OutQuint); else this.ScaleTo(1.9f, 200, Easing.None); } else { // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. - this.ScaleTo(tracking ? 2.4f : 1f); + this.ScaleTo(tracking ? DrawableSliderBall.FOLLOW_AREA : 1f); } if (tracking) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs index f9b2a0179c52..cc5b682f90f3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs @@ -60,7 +60,7 @@ private void load(DrawableHitObject drawableObject) }, }; - slider.ApplyCustomUpdateState += updateStateTransforms; + slider.HitObjectApplied += hitObjectApplied; } protected override void UpdateAfterChildren() @@ -74,13 +74,14 @@ protected override void UpdateAfterChildren() layerSpec.Rotation = -appliedRotation; } - private void updateStateTransforms(DrawableHitObject obj, ArmedState state) + private void hitObjectApplied(DrawableHitObject obj) { - using (BeginAbsoluteSequence(slider.StateUpdateTime)) + using (BeginAbsoluteSequence(slider.HitObject.StartTime)) { this.FadeIn(); } - using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) + + using (BeginAbsoluteSequence(slider.HitObject.EndTime)) { this.FadeOut(); } From 681d04dd11a1f0372dcedfb7ddc8d2c7b4cb5fa0 Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Wed, 22 Sep 2021 21:39:24 -0700 Subject: [PATCH 07/11] Add legacy follow circle expansion anim on tracking stop --- .../Skinning/Default/DefaultFollowCircle.cs | 15 ++++++----- .../Skinning/Default/DefaultSliderBall.cs | 22 +++++++--------- .../Skinning/Legacy/LegacyFollowCircle.cs | 26 +++++++++++++++---- .../Skinning/Legacy/LegacySliderBall.cs | 11 ++++---- 4 files changed, 43 insertions(+), 31 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs index aa0d2791666e..6d36dea5b097 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs @@ -40,7 +40,7 @@ private void load(DrawableHitObject drawableObject) }; slider.Tracking.BindValueChanged(trackingChanged, true); - slider.HitObjectApplied += hitObjectApplied; + slider.ApplyCustomUpdateState += updateStateTransforms; } private void trackingChanged(ValueChangedEvent e) @@ -58,14 +58,15 @@ private void trackingChanged(ValueChangedEvent e) this.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint); } - private void hitObjectApplied(DrawableHitObject obj) + private void updateStateTransforms(DrawableHitObject obj, ArmedState state) { - using (BeginAbsoluteSequence(slider.HitObject.EndTime)) - { - const float fade_out_time = 450; + if (!(obj is DrawableSlider)) + return; + + const float fade_out_time = 112.5f; - this.FadeOut(fade_out_time / 4, Easing.Out); - } + using (BeginAbsoluteSequence(slider.HitObject.EndTime)) + this.FadeOut(fade_out_time, Easing.Out); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs index eaf63962d93b..02584274c0c4 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs @@ -52,33 +52,29 @@ private void load(DrawableHitObject drawableObject, ISkinSource skin) }; slider.Tracking.BindValueChanged(trackingChanged, true); - slider.HitObjectApplied += hitObjectApplied; slider.ApplyCustomUpdateState += updateStateTransforms; } private void trackingChanged(ValueChangedEvent tracking) => box.FadeTo(tracking.NewValue ? 0.3f : 0.05f, 200, Easing.OutQuint); - private void hitObjectApplied(DrawableHitObject obj) + private void updateStateTransforms(DrawableHitObject obj, ArmedState state) { + if (!(obj is DrawableSlider)) + return; + + const float fade_out_time = 112.5f; + using (BeginAbsoluteSequence(slider.HitObject.StartTime)) - { - this.FadeIn(); - this.ScaleTo(1f); - } - } + this.FadeIn().ScaleTo(1f); - private void updateStateTransforms(DrawableHitObject obj, ArmedState state) - { using (BeginAbsoluteSequence(slider.HitObject.EndTime)) { - const float fade_out_time = 450; - - this.FadeOut(fade_out_time / 4, Easing.In); + this.FadeOut(fade_out_time, Easing.Out); switch (state) { case ArmedState.Hit: - this.ScaleTo(1.4f, fade_out_time, Easing.Out); + this.ScaleTo(1.1f, fade_out_time, Easing.Out); break; } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs index b1803ec9a61b..a135df7aae26 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Framework.Logging; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { @@ -32,10 +33,14 @@ private void load(DrawableHitObject drawableObject) animationContent.Origin = Anchor.Centre; slider.Tracking.BindValueChanged(trackingChanged, true); + slider.ApplyCustomUpdateState += updateStateTransforms; } private void trackingChanged(ValueChangedEvent e) { + if (slider.Judged) + return; + bool tracking = e.NewValue; if (slider.Ball.InputTracksVisualSize) @@ -43,7 +48,7 @@ private void trackingChanged(ValueChangedEvent e) if (tracking) this.ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200, Easing.OutQuint); else - this.ScaleTo(1.9f, 200, Easing.None); + this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 2, 100, Easing.OutQuint).Then().ScaleTo(1f); } else { @@ -51,10 +56,21 @@ private void trackingChanged(ValueChangedEvent e) this.ScaleTo(tracking ? DrawableSliderBall.FOLLOW_AREA : 1f); } - if (tracking) - this.FadeIn(100, Easing.OutQuint); - else - this.FadeOut(200, Easing.InQuint); + this.FadeTo(tracking ? 1f : 0f, 100, Easing.OutQuint); + } + + private void updateStateTransforms(DrawableHitObject obj, ArmedState state) + { + if (!(obj is DrawableSlider)) + return; + + const float fade_out_time = 200f; + + using (BeginAbsoluteSequence(slider.HitObject.EndTime)) + { + this.FadeOut(fade_out_time, Easing.InQuint); + this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 0.8f, fade_out_time, Easing.None); + } } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs index cc5b682f90f3..6bfcc6a27e0b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs @@ -60,7 +60,7 @@ private void load(DrawableHitObject drawableObject) }, }; - slider.HitObjectApplied += hitObjectApplied; + slider.ApplyCustomUpdateState += updateStateTransforms; } protected override void UpdateAfterChildren() @@ -74,17 +74,16 @@ protected override void UpdateAfterChildren() layerSpec.Rotation = -appliedRotation; } - private void hitObjectApplied(DrawableHitObject obj) + private void updateStateTransforms(DrawableHitObject obj, ArmedState state) { + if (!(obj is DrawableSlider)) + return; + using (BeginAbsoluteSequence(slider.HitObject.StartTime)) - { this.FadeIn(); - } using (BeginAbsoluteSequence(slider.HitObject.EndTime)) - { this.FadeOut(); - } } } } From 196b9c964e9ed38f41183b9c8525ac14ed3c21a8 Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Fri, 24 Sep 2021 19:40:57 -0700 Subject: [PATCH 08/11] Local bindables and remove subscriptions on slider ball/follow circle disposal Implement trivial requested and InspectCode changes --- .../TestSceneSliderApplication.cs | 1 - .../Objects/Drawables/DrawableSliderBall.cs | 5 ++++- .../Skinning/Default/DefaultFollowCircle.cs | 22 ++++++++++++++----- .../Skinning/Default/DefaultSliderBall.cs | 18 +++++++++++---- .../Skinning/Legacy/LegacyFollowCircle.cs | 17 ++++++++++---- .../Skinning/Legacy/LegacySliderBall.cs | 12 ++++++++-- .../Legacy/OsuLegacySkinTransformer.cs | 1 + 7 files changed, 59 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index e86a5947fe1c..9fe6af1cb743 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -12,7 +12,6 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osu.Game.Tests.Visual; using osuTK; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index de56e14d669c..381473dd1074 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs index 6d36dea5b097..44fc38a63ad8 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default public class DefaultFollowCircle : CompositeDrawable { private DrawableSlider slider; + private readonly Bindable trackingBindable = new Bindable(); [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) @@ -22,7 +23,6 @@ private void load(DrawableHitObject drawableObject) slider = (DrawableSlider)drawableObject; RelativeSizeAxes = Axes.Both; - Alpha = 1f; InternalChild = new CircularContainer { @@ -39,7 +39,8 @@ private void load(DrawableHitObject drawableObject) } }; - slider.Tracking.BindValueChanged(trackingChanged, true); + trackingBindable.BindTo(slider.Tracking); + trackingBindable.BindValueChanged(trackingChanged, true); slider.ApplyCustomUpdateState += updateStateTransforms; } @@ -48,7 +49,7 @@ private void trackingChanged(ValueChangedEvent e) bool tracking = e.NewValue; if (slider.Ball.InputTracksVisualSize) - this.ScaleTo(tracking ? DrawableSliderBall.FOLLOW_AREA: 1f, 300, Easing.OutQuint); + this.ScaleTo(tracking ? DrawableSliderBall.FOLLOW_AREA : 1f, 300, Easing.OutQuint); else { // We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration. @@ -62,11 +63,22 @@ private void updateStateTransforms(DrawableHitObject obj, ArmedState state) { if (!(obj is DrawableSlider)) return; - + const float fade_out_time = 112.5f; - using (BeginAbsoluteSequence(slider.HitObject.EndTime)) + using (BeginAbsoluteSequence(slider.StateUpdateTime)) + this.FadeIn(); + + using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) this.FadeOut(fade_out_time, Easing.Out); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (slider != null) + slider.ApplyCustomUpdateState -= updateStateTransforms; + } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs index 02584274c0c4..b0cedf91a701 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs @@ -18,8 +18,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default public class DefaultSliderBall : CompositeDrawable { private Box box; - private DrawableSlider slider; + private readonly Bindable trackingBindable = new Bindable(); [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject, ISkinSource skin) @@ -51,7 +51,8 @@ private void load(DrawableHitObject drawableObject, ISkinSource skin) } }; - slider.Tracking.BindValueChanged(trackingChanged, true); + trackingBindable.BindTo(slider.Tracking); + trackingBindable.BindValueChanged(trackingChanged, true); slider.ApplyCustomUpdateState += updateStateTransforms; } @@ -65,12 +66,13 @@ private void updateStateTransforms(DrawableHitObject obj, ArmedState state) const float fade_out_time = 112.5f; - using (BeginAbsoluteSequence(slider.HitObject.StartTime)) + using (BeginAbsoluteSequence(slider.StateUpdateTime)) this.FadeIn().ScaleTo(1f); - using (BeginAbsoluteSequence(slider.HitObject.EndTime)) + using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) { this.FadeOut(fade_out_time, Easing.Out); + switch (state) { case ArmedState.Hit: @@ -79,5 +81,13 @@ private void updateStateTransforms(DrawableHitObject obj, ArmedState state) } } } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (slider != null) + slider.ApplyCustomUpdateState -= updateStateTransforms; + } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs index a135df7aae26..44e3a1a830f3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Framework.Logging; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { @@ -15,6 +14,7 @@ public class LegacyFollowCircle : CompositeDrawable { private readonly Drawable animationContent; private DrawableSlider slider; + private readonly Bindable trackingBindable = new Bindable(); public LegacyFollowCircle(Drawable animationContent) { @@ -32,7 +32,8 @@ private void load(DrawableHitObject drawableObject) animationContent.Anchor = Anchor.Centre; animationContent.Origin = Anchor.Centre; - slider.Tracking.BindValueChanged(trackingChanged, true); + trackingBindable.BindTo(slider.Tracking); + trackingBindable.BindValueChanged(trackingChanged, true); slider.ApplyCustomUpdateState += updateStateTransforms; } @@ -66,11 +67,19 @@ private void updateStateTransforms(DrawableHitObject obj, ArmedState state) const float fade_out_time = 200f; - using (BeginAbsoluteSequence(slider.HitObject.EndTime)) + using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) { this.FadeOut(fade_out_time, Easing.InQuint); - this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 0.8f, fade_out_time, Easing.None); + this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 0.8f, fade_out_time); } } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (slider != null) + slider.ApplyCustomUpdateState -= updateStateTransforms; + } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs index 6bfcc6a27e0b..808a5f642d43 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs @@ -79,11 +79,19 @@ private void updateStateTransforms(DrawableHitObject obj, ArmedState state) if (!(obj is DrawableSlider)) return; - using (BeginAbsoluteSequence(slider.HitObject.StartTime)) + using (BeginAbsoluteSequence(slider.StateUpdateTime)) this.FadeIn(); - using (BeginAbsoluteSequence(slider.HitObject.EndTime)) + using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) this.FadeOut(); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (slider != null) + slider.ApplyCustomUpdateState -= updateStateTransforms; + } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 0f42a1d33efd..fd1b1458db40 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -37,6 +37,7 @@ public override Drawable GetDrawableComponent(ISkinComponent component) case OsuSkinComponents.SliderFollowCircle: var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true); + if (followCircleContent != null) { // follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x From eeb84cc505c5d3f3ea790308da6636b69e2a4cb9 Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Sat, 25 Sep 2021 16:00:02 -0700 Subject: [PATCH 09/11] Restored default slider ball/follow circle fade values --- .../Skinning/Default/DefaultFollowCircle.cs | 4 ++-- osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs index 44fc38a63ad8..a6bd4d9fe8fb 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs @@ -64,13 +64,13 @@ private void updateStateTransforms(DrawableHitObject obj, ArmedState state) if (!(obj is DrawableSlider)) return; - const float fade_out_time = 112.5f; + const float fade_out_time = 450f; using (BeginAbsoluteSequence(slider.StateUpdateTime)) this.FadeIn(); using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) - this.FadeOut(fade_out_time, Easing.Out); + this.FadeOut(fade_out_time / 4, Easing.Out); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs index b0cedf91a701..9a1f648b1870 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSliderBall.cs @@ -64,19 +64,19 @@ private void updateStateTransforms(DrawableHitObject obj, ArmedState state) if (!(obj is DrawableSlider)) return; - const float fade_out_time = 112.5f; + const float fade_out_time = 450f; using (BeginAbsoluteSequence(slider.StateUpdateTime)) this.FadeIn().ScaleTo(1f); using (BeginAbsoluteSequence(slider.HitStateUpdateTime)) { - this.FadeOut(fade_out_time, Easing.Out); + this.FadeOut(fade_out_time / 4, Easing.Out); switch (state) { case ArmedState.Hit: - this.ScaleTo(1.1f, fade_out_time, Easing.Out); + this.ScaleTo(1.4f, fade_out_time, Easing.Out); break; } } From a77f7688568a3b0c8e70a24c36ca8830b3f5eefb Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Thu, 3 Mar 2022 02:53:06 -0800 Subject: [PATCH 10/11] Cache/resolve drawable slider ball --- .../Objects/Drawables/DrawableSlider.cs | 20 +++++++++------- .../Objects/Drawables/DrawableSliderBall.cs | 23 ++++++++----------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 831088bbc1ed..fe4f916b80e2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -29,7 +29,10 @@ public class DrawableSlider : DrawableOsuHitObject public DrawableSliderHead HeadCircle => headContainer.Child; public DrawableSliderTail TailCircle => tailContainer.Child; - public DrawableSliderBall Ball { get; private set; } + public DrawableSliderBall Ball => ball; + [Cached] + private readonly DrawableSliderBall ball; + public SkinnableDrawable Body { get; private set; } /// @@ -60,6 +63,13 @@ public DrawableSlider() public DrawableSlider([CanBeNull] Slider s = null) : base(s) { + ball = new DrawableSliderBall + { + GetInitialHitAction = () => HeadCircle.HitAction, + BypassAutoSizeAxes = Axes.Both, + AlwaysPresent = true, + Alpha = 0 + }; } [BackgroundDependencyLoader] @@ -73,13 +83,7 @@ private void load() repeatContainer = new Container { RelativeSizeAxes = Axes.Both }, headContainer = new Container { RelativeSizeAxes = Axes.Both }, OverlayElementContainer = new Container { RelativeSizeAxes = Axes.Both, }, - Ball = new DrawableSliderBall(this) - { - GetInitialHitAction = () => HeadCircle.HitAction, - BypassAutoSizeAxes = Axes.Both, - AlwaysPresent = true, - Alpha = 0 - }, + ball, slidingSample = new PausableSkinnableSound { Looping = true } }; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index 381473dd1074..87e3ab9bacb6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -38,18 +38,16 @@ public Color4 AccentColour /// public bool InputTracksVisualSize = true; - private readonly DrawableSlider drawableSlider; - private readonly SkinnableDrawable followCircle; - private readonly SkinnableDrawable ball; + private DrawableSlider drawableSlider; + private SkinnableDrawable ball; + private FollowReceptor followArea; - private readonly FollowReceptor followArea; - - public DrawableSliderBall(DrawableSlider drawableSlider) + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject) { - this.drawableSlider = drawableSlider; + drawableSlider = (DrawableSlider)drawableObject; Origin = Anchor.Centre; - Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Children = new Drawable[] @@ -59,7 +57,7 @@ public DrawableSliderBall(DrawableSlider drawableSlider) Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - followCircle = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()) + new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -196,13 +194,12 @@ private class FollowReceptor : CircularContainer private DrawableSliderBall sliderBall; [BackgroundDependencyLoader] - private void load(DrawableHitObject drawableObject) + private void load(DrawableHitObject drawableObject, DrawableSliderBall sliderBall) { - var slider = (DrawableSlider)drawableObject; - sliderBall = slider.Ball; - + this.sliderBall = sliderBall; RelativeSizeAxes = Axes.Both; + var slider = (DrawableSlider)drawableObject; slider.Tracking.BindValueChanged(trackingChanged, true); } From 08089573fad03312db3b2f85e583dce9ed2bc400 Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Fri, 4 Mar 2022 19:39:29 -0800 Subject: [PATCH 11/11] Fix InspectCode issues --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index fe4f916b80e2..1233ce626d72 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -29,9 +29,8 @@ public class DrawableSlider : DrawableOsuHitObject public DrawableSliderHead HeadCircle => headContainer.Child; public DrawableSliderTail TailCircle => tailContainer.Child; - public DrawableSliderBall Ball => ball; [Cached] - private readonly DrawableSliderBall ball; + public DrawableSliderBall Ball { get; private set; } public SkinnableDrawable Body { get; private set; } @@ -63,7 +62,7 @@ public DrawableSlider() public DrawableSlider([CanBeNull] Slider s = null) : base(s) { - ball = new DrawableSliderBall + Ball = new DrawableSliderBall { GetInitialHitAction = () => HeadCircle.HitAction, BypassAutoSizeAxes = Axes.Both, @@ -83,7 +82,7 @@ private void load() repeatContainer = new Container { RelativeSizeAxes = Axes.Both }, headContainer = new Container { RelativeSizeAxes = Axes.Both }, OverlayElementContainer = new Container { RelativeSizeAxes = Axes.Both, }, - ball, + Ball, slidingSample = new PausableSkinnableSound { Looping = true } };