From 400fcedf0ff3e7a7de3c73731320124cca8484d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 14:40:48 +0900 Subject: [PATCH 1/6] Remove stored hitobject references from skinnable components --- .../Objects/Drawables/DrawableSlider.cs | 6 ++++- .../Objects/Drawables/DrawableSpinner.cs | 2 +- .../Objects/Drawables/Pieces/CirclePiece.cs | 2 +- .../Drawables/Pieces/DefaultSpinnerDisc.cs | 27 +++++++++---------- .../Drawables/Pieces/MainCirclePiece.cs | 4 +-- .../Drawables/Pieces/PlaySliderBody.cs | 13 +++------ .../Objects/Drawables/Pieces/SliderBall.cs | 12 ++++----- .../Drawables/Pieces/SnakingSliderBody.cs | 24 ++++++++++------- .../Pieces/SpinnerRotationTracker.cs | 10 +++---- .../Skinning/LegacyMainCirclePiece.cs | 5 ++-- .../Skinning/LegacyNewStyleSpinner.cs | 16 +++++------ .../Skinning/LegacyOldStyleSpinner.cs | 10 +++---- .../Skinning/LegacySliderBall.cs | 3 +-- 13 files changed, 65 insertions(+), 69 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index b743d2e4d0..8008d87c6d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -32,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody; + public readonly IBindable PathVersion = new Bindable(); + private Container headContainer; private Container tailContainer; private Container tickContainer; @@ -51,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables tailContainer = new Container { RelativeSizeAxes = Axes.Both }, tickContainer = new Container { RelativeSizeAxes = Axes.Both }, repeatContainer = new Container { RelativeSizeAxes = Axes.Both }, - Ball = new SliderBall(HitObject, this) + Ball = new SliderBall(this) { GetInitialHitAction = () => HeadCircle.HitAction, BypassAutoSizeAxes = Axes.Both, @@ -61,6 +63,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables headContainer = new Container { RelativeSizeAxes = Axes.Both }, }; + PathVersion.BindTo(HitObject.Path.Version); + PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true); StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true); ScaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue), true); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 50ea45c378..8dd63018e2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Children = new Drawable[] { new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBody), _ => new DefaultSpinnerDisc()), - RotationTracker = new SpinnerRotationTracker(HitObject) + RotationTracker = new SpinnerRotationTracker(this) } }, SpmCounter = new SpinnerSpmCounter diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index e95cdc7ee3..c455c66e8d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Origin = Anchor.Centre, Texture = textures.Get(@"Gameplay/osu/disc"), }, - new TrianglesPiece((int)drawableHitObject.HitObject.StartTime) + new TrianglesPiece(drawableHitObject.GetHashCode()) { RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 17a734f0f4..4c682d96ce 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { private DrawableSpinner drawableSpinner; - private Spinner spinner; - private const float initial_scale = 1.3f; private const float idle_alpha = 0.2f; private const float tracking_alpha = 0.4f; @@ -52,7 +50,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private void load(OsuColour colours, DrawableHitObject drawableHitObject) { drawableSpinner = (DrawableSpinner)drawableHitObject; - spinner = drawableSpinner.HitObject; normalColour = colours.BlueDark; completeColour = colours.YellowLight; @@ -130,18 +127,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (!(drawableHitObject is DrawableSpinner)) return; - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) { this.ScaleTo(initial_scale); this.RotateTo(0); - using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) + using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) { // constant ambient rotation to give the spinner "spinning" character. - this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration); + this.RotateTo((float)(25 * drawableSpinner.HitObject.Duration / 2000), drawableSpinner.HitObject.TimePreempt + drawableSpinner.HitObject.Duration); } - using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset, true)) + using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt + drawableSpinner.HitObject.Duration + drawableHitObject.Result.TimeOffset, true)) { switch (state) { @@ -157,26 +154,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) { centre.ScaleTo(0); mainContainer.ScaleTo(0); - using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) + using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) { - centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint); - mainContainer.ScaleTo(0.2f, spinner.TimePreempt / 4, Easing.OutQuint); + centre.ScaleTo(0.3f, drawableSpinner.HitObject.TimePreempt / 4, Easing.OutQuint); + mainContainer.ScaleTo(0.2f, drawableSpinner.HitObject.TimePreempt / 4, Easing.OutQuint); - using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) + using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) { - centre.ScaleTo(0.5f, spinner.TimePreempt / 2, Easing.OutQuint); - mainContainer.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint); + centre.ScaleTo(0.5f, drawableSpinner.HitObject.TimePreempt / 2, Easing.OutQuint); + mainContainer.ScaleTo(1, drawableSpinner.HitObject.TimePreempt / 2, Easing.OutQuint); } } } // transforms we have from completing the spinner will be rolled back, so reapply immediately. - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) updateComplete(state == ArmedState.Hit, 0); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs index cb3787a493..d2f4b71f19 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) { - OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; + var drawableOsuObject = (DrawableOsuHitObject)drawableObject; state.BindTo(drawableObject.State); state.BindValueChanged(updateState, true); @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces circle.Colour = colour.NewValue; }, true); - indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable); + indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs index cedf2f6e09..29dff53f54 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs @@ -17,23 +17,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private IBindable pathVersion; private IBindable accentColour; - [Resolved] - private DrawableHitObject drawableObject { get; set; } - [Resolved(CanBeNull = true)] private OsuRulesetConfigManager config { get; set; } - private Slider slider; - [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource skin, DrawableHitObject drawableObject) { - slider = (Slider)drawableObject.HitObject; + var drawableSlider = (DrawableSlider)drawableObject; - scaleBindable = slider.ScaleBindable.GetBoundCopy(); + scaleBindable = drawableSlider.ScaleBindable.GetBoundCopy(); scaleBindable.BindValueChanged(scale => PathRadius = OsuHitObject.OBJECT_RADIUS * scale.NewValue, true); - pathVersion = slider.Path.Version.GetBoundCopy(); + pathVersion = drawableSlider.PathVersion.GetBoundCopy(); pathVersion.BindValueChanged(_ => Refresh()); accentColour = drawableObject.AccentColour.GetBoundCopy(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 07dc6021c9..c5bf790377 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -30,15 +30,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces set => ball.Colour = value; } - private readonly Slider slider; private readonly Drawable followCircle; private readonly DrawableSlider drawableSlider; private readonly Drawable ball; - public SliderBall(Slider slider, DrawableSlider drawableSlider = null) + public SliderBall(DrawableSlider drawableSlider) { this.drawableSlider = drawableSlider; - this.slider = slider; Origin = Anchor.Centre; @@ -133,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (headCircleHitAction == null) timeToAcceptAnyKeyAfter = null; - var actions = drawableSlider?.OsuActionInputManager?.PressedActions; + var actions = drawableSlider.OsuActionInputManager?.PressedActions; // if the head circle was hit with a specific key, tracking should only occur while that key is pressed. if (headCircleHitAction != null && timeToAcceptAnyKeyAfter == null) @@ -147,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Tracking = // in valid time range - Time.Current >= slider.StartTime && Time.Current < slider.EndTime && + Time.Current >= drawableSlider.HitObject.StartTime && Time.Current < drawableSlider.HitObject.EndTime && // in valid position range lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) && // valid action @@ -172,9 +170,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void UpdateProgress(double completionProgress) { - var newPos = slider.CurvePositionAt(completionProgress); + var newPos = drawableSlider.HitObject.CurvePositionAt(completionProgress); - var diff = lastPosition.HasValue ? lastPosition.Value - newPos : newPos - slider.CurvePositionAt(completionProgress + 0.01f); + var diff = lastPosition.HasValue ? lastPosition.Value - newPos : newPos - drawableSlider.HitObject.CurvePositionAt(completionProgress + 0.01f); if (diff == Vector2.Zero) return; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs index e24fa865ad..2fefc2e08e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs @@ -51,27 +51,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces /// private Vector2 snakedPathOffset; - private Slider slider; + private DrawableSlider drawableSlider; [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) { - slider = (Slider)drawableObject.HitObject; + drawableSlider = (DrawableSlider)drawableObject; Refresh(); } public void UpdateProgress(double completionProgress) { - var span = slider.SpanAt(completionProgress); - var spanProgress = slider.ProgressAt(completionProgress); + if (drawableSlider == null) + return; + + var span = drawableSlider.HitObject.SpanAt(completionProgress); + var spanProgress = drawableSlider.HitObject.ProgressAt(completionProgress); double start = 0; - double end = SnakingIn.Value ? Math.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1; + double end = SnakingIn.Value ? Math.Clamp((Time.Current - (drawableSlider.HitObject.StartTime - drawableSlider.HitObject.TimePreempt)) / (drawableSlider.HitObject.TimePreempt / 3), 0, 1) : 1; - if (span >= slider.SpanCount() - 1) + if (span >= drawableSlider.HitObject.SpanCount() - 1) { - if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1) + if (Math.Min(span, drawableSlider.HitObject.SpanCount() - 1) % 2 == 1) { start = 0; end = SnakingOut.Value ? spanProgress : 1; @@ -87,8 +90,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void Refresh() { + if (drawableSlider == null) + return; + // Generate the entire curve - slider.Path.GetPathToProgress(CurrentCurve, 0, 1); + drawableSlider.HitObject.Path.GetPathToProgress(CurrentCurve, 0, 1); SetVertices(CurrentCurve); // Force the body to be the final path size to avoid excessive autosize computations @@ -132,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces SnakedStart = p0; SnakedEnd = p1; - slider.Path.GetPathToProgress(CurrentCurve, p0, p1); + drawableSlider.HitObject.Path.GetPathToProgress(CurrentCurve, p0, p1); SetVertices(CurrentCurve); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs index 05ed38d241..910899c307 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class SpinnerRotationTracker : CircularContainer { - private readonly Spinner spinner; - public override bool IsPresent => true; // handle input when hidden - public SpinnerRotationTracker(Spinner s) + private readonly DrawableSpinner drawableSpinner; + + public SpinnerRotationTracker(DrawableSpinner drawableSpinner) { - spinner = s; + this.drawableSpinner = drawableSpinner; RelativeSizeAxes = Axes.Both; } @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces /// /// Whether currently in the correct time range to allow spinning. /// - private bool isSpinnableTime => spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current; + private bool isSpinnableTime => drawableSpinner.HitObject.StartTime <= Time.Current && drawableSpinner.HitObject.EndTime > Time.Current; protected override bool OnMouseMove(MouseMoveEvent e) { diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 382d6e53cc..418bf124ab 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; 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; @@ -47,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Skinning [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) { - OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; + var drawableOsuObject = (DrawableOsuHitObject)drawableObject; bool allowFallback = false; @@ -111,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Skinning state.BindTo(drawableObject.State); accentColour.BindTo(drawableObject.AccentColour); - indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable); + indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); Texture getTextureWithFallback(string name) { diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index 018dc78ddb..f07b1038fb 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -75,23 +75,21 @@ namespace osu.Game.Rulesets.Osu.Skinning private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - if (!(drawableHitObject is DrawableSpinner)) + if (!(drawableHitObject is DrawableSpinner spinner)) return; - var spinner = drawableSpinner.HitObject; - - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) - this.FadeInFromZero(spinner.TimeFadeIn / 2); + using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimeFadeIn / 2, true)) + this.FadeInFromZero(spinner.HitObject.TimeFadeIn / 2); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) { fixedMiddle.FadeColour(Color4.White); - using (BeginDelayedSequence(spinner.TimePreempt, true)) - fixedMiddle.FadeColour(Color4.Red, spinner.Duration); + using (BeginDelayedSequence(spinner.HitObject.TimePreempt, true)) + fixedMiddle.FadeColour(Color4.Red, spinner.HitObject.Duration); } } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index 7b0d7acbbc..5fcd8e06b1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -94,16 +94,14 @@ namespace osu.Game.Rulesets.Osu.Skinning private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - if (!(drawableHitObject is DrawableSpinner)) + if (!(drawableHitObject is DrawableSpinner spinner)) return; - var spinner = drawableSpinner.HitObject; - - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) - this.FadeInFromZero(spinner.TimeFadeIn / 2); + using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimeFadeIn / 2, true)) + this.FadeInFromZero(spinner.HitObject.TimeFadeIn / 2); } protected override void Update() diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs index 25ab96445a..836069013d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Skinning; using osuTK.Graphics; @@ -26,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning } [BackgroundDependencyLoader] - private void load(ISkinSource skin, DrawableHitObject drawableObject) + private void load(ISkinSource skin) { var ballColour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White; From 9c1c9945af19ea710a995c4febc931813d0be54a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 15:01:45 +0900 Subject: [PATCH 2/6] Make FollowPointRenderer use hitobject models --- .../TestSceneFollowPoints.cs | 10 ++--- .../Connections/FollowPointConnection.cs | 44 +++++++++---------- .../Connections/FollowPointRenderer.cs | 12 ++--- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 5 ++- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs index 87da7ef417..6c077eb214 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs @@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Tests } hitObjectContainer.Add(drawableObject); - followPointRenderer.AddFollowPoints(drawableObject); + followPointRenderer.AddFollowPoints(objects[i]); } }); } @@ -180,10 +180,10 @@ namespace osu.Game.Rulesets.Osu.Tests { AddStep("remove hitobject", () => { - var drawableObject = getFunc?.Invoke(); + var drawableObject = getFunc.Invoke(); hitObjectContainer.Remove(drawableObject); - followPointRenderer.RemoveFollowPoints(drawableObject); + followPointRenderer.RemoveFollowPoints(drawableObject.HitObject); }); } @@ -215,10 +215,10 @@ namespace osu.Game.Rulesets.Osu.Tests DrawableOsuHitObject expectedStart = getObject(i); DrawableOsuHitObject expectedEnd = i < hitObjectContainer.Count - 1 ? getObject(i + 1) : null; - if (getGroup(i).Start != expectedStart) + if (getGroup(i).Start != expectedStart.HitObject) throw new AssertionException($"Object {i} expected to be the start of group {i}."); - if (getGroup(i).End != expectedEnd) + if (getGroup(i).End != expectedEnd?.HitObject) throw new AssertionException($"Object {(expectedEnd == null ? "null" : i.ToString())} expected to be the end of group {i}."); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 2c41e6b0e9..3a9e19b361 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -31,19 +32,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections /// The which s will exit from. /// [NotNull] - public readonly DrawableOsuHitObject Start; + public readonly OsuHitObject Start; /// /// Creates a new . /// /// The which s will exit from. - public FollowPointConnection([NotNull] DrawableOsuHitObject start) + public FollowPointConnection([NotNull] OsuHitObject start) { Start = start; RelativeSizeAxes = Axes.Both; - StartTime.BindTo(Start.HitObject.StartTimeBindable); + StartTime.BindTo(start.StartTimeBindable); } protected override void LoadComplete() @@ -52,13 +53,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections bindEvents(Start); } - private DrawableOsuHitObject end; + private OsuHitObject end; /// /// The which s will enter. /// [CanBeNull] - public DrawableOsuHitObject End + public OsuHitObject End { get => end; set @@ -75,10 +76,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections } } - private void bindEvents(DrawableOsuHitObject drawableObject) + private void bindEvents(OsuHitObject obj) { - drawableObject.HitObject.PositionBindable.BindValueChanged(_ => scheduleRefresh()); - drawableObject.HitObject.DefaultsApplied += _ => scheduleRefresh(); + obj.PositionBindable.BindValueChanged(_ => scheduleRefresh()); + obj.DefaultsApplied += _ => scheduleRefresh(); } private void scheduleRefresh() @@ -88,23 +89,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections private void refresh() { - OsuHitObject osuStart = Start.HitObject; - double startTime = osuStart.GetEndTime(); + double startTime = Start.GetEndTime(); LifetimeStart = startTime; - OsuHitObject osuEnd = End?.HitObject; - - if (osuEnd == null || osuEnd.NewCombo || osuStart is Spinner || osuEnd is Spinner) + if (End == null || End.NewCombo || Start is Spinner || End is Spinner) { // ensure we always set a lifetime for full LifetimeManagementContainer benefits LifetimeEnd = LifetimeStart; return; } - Vector2 startPosition = osuStart.StackedEndPosition; - Vector2 endPosition = osuEnd.StackedPosition; - double endTime = osuEnd.StartTime; + Vector2 startPosition = Start.StackedEndPosition; + Vector2 endPosition = End.StackedPosition; + double endTime = End.StartTime; Vector2 distanceVector = endPosition - startPosition; int distance = (int)distanceVector.Length; @@ -130,10 +128,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections AddInternal(fp = new FollowPoint()); + Debug.Assert(End != null); + fp.Position = pointStartPosition; fp.Rotation = rotation; fp.Alpha = 0; - fp.Scale = new Vector2(1.5f * osuEnd.Scale); + fp.Scale = new Vector2(1.5f * End.Scale); firstTransformStartTime ??= fadeInTime; @@ -141,12 +141,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections using (fp.BeginAbsoluteSequence(fadeInTime)) { - fp.FadeIn(osuEnd.TimeFadeIn); - fp.ScaleTo(osuEnd.Scale, osuEnd.TimeFadeIn, Easing.Out); - fp.MoveTo(pointEndPosition, osuEnd.TimeFadeIn, Easing.Out); - fp.Delay(fadeOutTime - fadeInTime).FadeOut(osuEnd.TimeFadeIn); + fp.FadeIn(End.TimeFadeIn); + fp.ScaleTo(End.Scale, End.TimeFadeIn, Easing.Out); + fp.MoveTo(pointEndPosition, End.TimeFadeIn, Easing.Out); + fp.Delay(fadeOutTime - fadeInTime).FadeOut(End.TimeFadeIn); - finalTransformEndTime = fadeOutTime + osuEnd.TimeFadeIn; + finalTransformEndTime = fadeOutTime + End.TimeFadeIn; } point++; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index 11571ea761..be1392d7c3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -24,19 +24,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections public override bool RemoveCompletedTransforms => false; /// - /// Adds the s around a . + /// Adds the s around an . /// This includes s leading into , and s exiting . /// - /// The to add s for. - public void AddFollowPoints(DrawableOsuHitObject hitObject) + /// The to add s for. + public void AddFollowPoints(OsuHitObject hitObject) => addConnection(new FollowPointConnection(hitObject).With(g => g.StartTime.BindValueChanged(_ => onStartTimeChanged(g)))); /// - /// Removes the s around a . + /// Removes the s around an . /// This includes s leading into , and s exiting . /// - /// The to remove s for. - public void RemoveFollowPoints(DrawableOsuHitObject hitObject) => removeGroup(connections.Single(g => g.Start == hitObject)); + /// The to remove s for. + public void RemoveFollowPoints(OsuHitObject hitObject) => removeGroup(connections.Single(g => g.Start == hitObject)); /// /// Adds a to this . diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 50727d590a..19502369c4 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -20,6 +20,7 @@ using osu.Game.Skinning; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Osu.Configuration; +using osu.Game.Rulesets.Osu.Objects; using osuTK; namespace osu.Game.Rulesets.Osu.UI @@ -110,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.UI DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; osuHitObject.CheckHittable = hitPolicy.IsHittable; - followPoints.AddFollowPoints(osuHitObject); + followPoints.AddFollowPoints((OsuHitObject)h.HitObject); } public override bool Remove(DrawableHitObject h) @@ -118,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.UI bool result = base.Remove(h); if (result) - followPoints.RemoveFollowPoints((DrawableOsuHitObject)h); + followPoints.RemoveFollowPoints((OsuHitObject)h.HitObject); return result; } From 4457e363d39bfa4f8a6f954ab9dbb280b96dcdc2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 15:18:52 +0900 Subject: [PATCH 3/6] Bring back local variables to reduce two-level-deep variable access --- .../Drawables/Pieces/DefaultSpinnerDisc.cs | 26 ++++++++++--------- .../Drawables/Pieces/SnakingSliderBody.cs | 12 +++++---- .../Skinning/LegacyNewStyleSpinner.cs | 17 +++++++----- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 4c682d96ce..731852c221 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -127,18 +127,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (!(drawableHitObject is DrawableSpinner)) return; - using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) + Spinner spinner = drawableSpinner.HitObject; + + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) { this.ScaleTo(initial_scale); this.RotateTo(0); - using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) + using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) { // constant ambient rotation to give the spinner "spinning" character. - this.RotateTo((float)(25 * drawableSpinner.HitObject.Duration / 2000), drawableSpinner.HitObject.TimePreempt + drawableSpinner.HitObject.Duration); + this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration); } - using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt + drawableSpinner.HitObject.Duration + drawableHitObject.Result.TimeOffset, true)) + using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset, true)) { switch (state) { @@ -154,26 +156,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) { centre.ScaleTo(0); mainContainer.ScaleTo(0); - using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) + using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) { - centre.ScaleTo(0.3f, drawableSpinner.HitObject.TimePreempt / 4, Easing.OutQuint); - mainContainer.ScaleTo(0.2f, drawableSpinner.HitObject.TimePreempt / 4, Easing.OutQuint); + centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint); + mainContainer.ScaleTo(0.2f, spinner.TimePreempt / 4, Easing.OutQuint); - using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) + using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) { - centre.ScaleTo(0.5f, drawableSpinner.HitObject.TimePreempt / 2, Easing.OutQuint); - mainContainer.ScaleTo(1, drawableSpinner.HitObject.TimePreempt / 2, Easing.OutQuint); + centre.ScaleTo(0.5f, spinner.TimePreempt / 2, Easing.OutQuint); + mainContainer.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint); } } } // transforms we have from completing the spinner will be rolled back, so reapply immediately. - using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) updateComplete(state == ArmedState.Hit, 0); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs index 2fefc2e08e..e63f25b7bc 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs @@ -66,15 +66,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (drawableSlider == null) return; - var span = drawableSlider.HitObject.SpanAt(completionProgress); - var spanProgress = drawableSlider.HitObject.ProgressAt(completionProgress); + Slider slider = drawableSlider.HitObject; + + var span = slider.SpanAt(completionProgress); + var spanProgress = slider.ProgressAt(completionProgress); double start = 0; - double end = SnakingIn.Value ? Math.Clamp((Time.Current - (drawableSlider.HitObject.StartTime - drawableSlider.HitObject.TimePreempt)) / (drawableSlider.HitObject.TimePreempt / 3), 0, 1) : 1; + double end = SnakingIn.Value ? Math.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1; - if (span >= drawableSlider.HitObject.SpanCount() - 1) + if (span >= slider.SpanCount() - 1) { - if (Math.Min(span, drawableSlider.HitObject.SpanCount() - 1) % 2 == 1) + if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1) { start = 0; end = SnakingOut.Value ? spanProgress : 1; diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index f07b1038fb..b65e5a784c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; 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; @@ -75,21 +76,23 @@ namespace osu.Game.Rulesets.Osu.Skinning private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - if (!(drawableHitObject is DrawableSpinner spinner)) + if (!(drawableHitObject is DrawableSpinner d)) return; - using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) + Spinner spinner = d.HitObject; + + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimeFadeIn / 2, true)) - this.FadeInFromZero(spinner.HitObject.TimeFadeIn / 2); + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) + this.FadeInFromZero(spinner.TimeFadeIn / 2); - using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) { fixedMiddle.FadeColour(Color4.White); - using (BeginDelayedSequence(spinner.HitObject.TimePreempt, true)) - fixedMiddle.FadeColour(Color4.Red, spinner.HitObject.Duration); + using (BeginDelayedSequence(spinner.TimePreempt, true)) + fixedMiddle.FadeColour(Color4.Red, spinner.Duration); } } From e2d028908abe2271e1faeb74b9c6f84384ce049c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 15:25:20 +0900 Subject: [PATCH 4/6] Fix one more case of local variable preference --- .../Skinning/LegacyOldStyleSpinner.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index 5fcd8e06b1..1954ff6e38 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; 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; @@ -94,14 +95,16 @@ namespace osu.Game.Rulesets.Osu.Skinning private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - if (!(drawableHitObject is DrawableSpinner spinner)) + if (!(drawableHitObject is DrawableSpinner d)) return; - using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) + Spinner spinner = d.HitObject; + + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimeFadeIn / 2, true)) - this.FadeInFromZero(spinner.HitObject.TimeFadeIn / 2); + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) + this.FadeInFromZero(spinner.TimeFadeIn / 2); } protected override void Update() From 54f927ee11e7a114862ad2625f7ca9bc10bfd794 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 15:41:37 +0900 Subject: [PATCH 5/6] Move casts to DrawableHitObject instead --- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 19502369c4..321eeeab65 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -4,12 +4,15 @@ 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.Pooling; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; using osu.Game.Rulesets.Osu.Scoring; @@ -17,10 +20,6 @@ using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Skinning; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Game.Rulesets.Osu.Configuration; -using osu.Game.Rulesets.Osu.Objects; using osuTK; namespace osu.Game.Rulesets.Osu.UI @@ -96,6 +95,8 @@ namespace osu.Game.Rulesets.Osu.UI public override void Add(DrawableHitObject h) { + DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; + h.OnNewResult += onNewResult; h.OnLoadComplete += d => { @@ -108,18 +109,19 @@ namespace osu.Game.Rulesets.Osu.UI base.Add(h); - DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; osuHitObject.CheckHittable = hitPolicy.IsHittable; - followPoints.AddFollowPoints((OsuHitObject)h.HitObject); + followPoints.AddFollowPoints(osuHitObject.HitObject); } public override bool Remove(DrawableHitObject h) { + DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; + bool result = base.Remove(h); if (result) - followPoints.RemoveFollowPoints((OsuHitObject)h.HitObject); + followPoints.RemoveFollowPoints(osuHitObject.HitObject); return result; } From 414daab1dc685ec46ffc7f196d236507a66362bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 16:14:22 +0900 Subject: [PATCH 6/6] Fix paused samples potentially getting stuck in a playing state in rapid toggling Closes #10693. Should be obvious why. --- osu.Game/Skinning/PausableSkinnableSound.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/PausableSkinnableSound.cs b/osu.Game/Skinning/PausableSkinnableSound.cs index d340f67575..4f09aec0b6 100644 --- a/osu.Game/Skinning/PausableSkinnableSound.cs +++ b/osu.Game/Skinning/PausableSkinnableSound.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Threading; using osu.Game.Audio; using osu.Game.Screens.Play; @@ -25,6 +26,8 @@ namespace osu.Game.Skinning private readonly IBindable samplePlaybackDisabled = new Bindable(); + private ScheduledDelegate scheduledStart; + [BackgroundDependencyLoader(true)] private void load(ISamplePlaybackDisabler samplePlaybackDisabler) { @@ -39,12 +42,14 @@ namespace osu.Game.Skinning // let non-looping samples that have already been started play out to completion (sounds better than abruptly cutting off). if (!Looping) return; + cancelPendingStart(); + if (disabled.NewValue) base.Stop(); else { // schedule so we don't start playing a sample which is no longer alive. - Schedule(() => + scheduledStart = Schedule(() => { if (RequestedPlaying) base.Play(); @@ -56,6 +61,7 @@ namespace osu.Game.Skinning public override void Play() { + cancelPendingStart(); RequestedPlaying = true; if (samplePlaybackDisabled.Value) @@ -66,8 +72,15 @@ namespace osu.Game.Skinning public override void Stop() { + cancelPendingStart(); RequestedPlaying = false; base.Stop(); } + + private void cancelPendingStart() + { + scheduledStart?.Cancel(); + scheduledStart = null; + } } }