From 882f49039029b7dc3e287ccc302d04de89de10df Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 01:32:37 +0100 Subject: [PATCH 1/7] lazy load slider tail position --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 8 ++------ osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs | 8 ++++---- osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs | 8 ++++++++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 506145568e..2aca83ad03 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Objects set { repeatCount = value; - updateNestedPositions(); + endPositionCache.Invalidate(); } } @@ -165,7 +165,7 @@ namespace osu.Game.Rulesets.Osu.Objects public Slider() { SamplesBindable.CollectionChanged += (_, _) => UpdateNestedSamples(); - Path.Version.ValueChanged += _ => updateNestedPositions(); + Path.Version.ValueChanged += _ => endPositionCache.Invalidate(); } protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) @@ -218,7 +218,6 @@ namespace osu.Game.Rulesets.Osu.Objects { RepeatIndex = e.SpanIndex, StartTime = e.Time, - Position = EndPosition, StackHeight = StackHeight, ClassicSliderBehaviour = ClassicSliderBehaviour, }); @@ -245,9 +244,6 @@ namespace osu.Game.Rulesets.Osu.Objects if (HeadCircle != null) HeadCircle.Position = Position; - - if (TailCircle != null) - TailCircle.Position = EndPosition; } protected void UpdateNestedSamples() diff --git a/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs index 88a34fcb8f..2d5a5b7727 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs @@ -14,16 +14,16 @@ namespace osu.Game.Rulesets.Osu.Objects /// public abstract class SliderEndCircle : HitCircle { - private readonly Slider slider; + protected readonly Slider Slider; protected SliderEndCircle(Slider slider) { - this.slider = slider; + Slider = slider; } public int RepeatIndex { get; set; } - public double SpanDuration => slider.SpanDuration; + public double SpanDuration => Slider.SpanDuration; protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects else { // The first end circle should fade in with the slider. - TimePreempt += StartTime - slider.StartTime; + TimePreempt += StartTime - Slider.StartTime; } } diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index ceee513412..abe2c7074b 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -1,9 +1,11 @@ // 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 osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; +using osuTK; namespace osu.Game.Rulesets.Osu.Objects { @@ -15,6 +17,12 @@ namespace osu.Game.Rulesets.Osu.Objects /// public bool ClassicSliderBehaviour; + public override Vector2 Position + { + get => Slider.EndPosition; + set => throw new NotImplementedException(); + } + public SliderTailCircle(Slider slider) : base(slider) { From 5fa7f6ec53a4430b1db13f74e75e2e56b4a181fc Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 02:21:32 +0100 Subject: [PATCH 2/7] make drawables that update from path version update once per frame --- .../PathControlPointConnectionPiece.cs | 16 ++++++++++++++-- .../Skinning/Default/PlaySliderBody.cs | 6 ++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs index 67685d21a7..004a091cae 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -51,14 +51,26 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components base.LoadComplete(); hitObjectPosition = hitObject.PositionBindable.GetBoundCopy(); - hitObjectPosition.BindValueChanged(_ => updateConnectingPath()); + hitObjectPosition.BindValueChanged(_ => pathRequiresUpdate = true); pathVersion = hitObject.Path.Version.GetBoundCopy(); - pathVersion.BindValueChanged(_ => updateConnectingPath()); + pathVersion.BindValueChanged(_ => pathRequiresUpdate = true); updateConnectingPath(); } + private bool pathRequiresUpdate; + + protected override void Update() + { + base.Update(); + + if (!pathRequiresUpdate) return; + + updateConnectingPath(); + pathRequiresUpdate = false; + } + /// /// Updates the path connecting this control point to the next one. /// diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs index aa507cbaf0..cbc62a1c7c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs @@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default protected IBindable AccentColourBindable { get; private set; } = null!; - private IBindable pathVersion = null!; - [Resolved(CanBeNull = true)] private OsuRulesetConfigManager? config { get; set; } @@ -33,8 +31,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default ScaleBindable = drawableSlider.ScaleBindable.GetBoundCopy(); ScaleBindable.BindValueChanged(scale => PathRadius = OsuHitObject.OBJECT_RADIUS * scale.NewValue, true); - pathVersion = drawableSlider.PathVersion.GetBoundCopy(); - pathVersion.BindValueChanged(_ => Refresh()); + drawableObject.DefaultsApplied += _ => Refresh(); + drawableObject.HitObjectApplied += _ => Refresh(); AccentColourBindable = drawableObject.AccentColour.GetBoundCopy(); AccentColourBindable.BindValueChanged(accent => AccentColour = GetBodyAccentColour(skin, accent.NewValue), true); From e1186080b87436d78a591bb888ff8bfa93559dbf Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 02:24:33 +0100 Subject: [PATCH 3/7] simplify scheduling logic --- .../PathControlPointConnectionPiece.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs index 004a091cae..7e7d653dbd 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -51,26 +51,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components base.LoadComplete(); hitObjectPosition = hitObject.PositionBindable.GetBoundCopy(); - hitObjectPosition.BindValueChanged(_ => pathRequiresUpdate = true); + hitObjectPosition.BindValueChanged(_ => Scheduler.AddOnce(updateConnectingPath)); pathVersion = hitObject.Path.Version.GetBoundCopy(); - pathVersion.BindValueChanged(_ => pathRequiresUpdate = true); + pathVersion.BindValueChanged(_ => Scheduler.AddOnce(updateConnectingPath)); updateConnectingPath(); } - private bool pathRequiresUpdate; - - protected override void Update() - { - base.Update(); - - if (!pathRequiresUpdate) return; - - updateConnectingPath(); - pathRequiresUpdate = false; - } - /// /// Updates the path connecting this control point to the next one. /// From fca9b1f53630831c5ea3d56e09148c81b7ca9c57 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 12:50:39 +0100 Subject: [PATCH 4/7] Fix so it reacts to PathVersion with Scheduler --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 6 ++++++ osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 4099d47d61..2f012e33da 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -244,7 +244,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables else if (slidingSample.IsPlaying) slidingSample.Stop(); } + } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + // It's important that this is done in UpdateAfterChildren so that the SliderBody has a chance to update its Size and PathOffset on Update. double completionProgress = Math.Clamp((Time.Current - HitObject.StartTime) / HitObject.Duration, 0, 1); Ball.UpdateProgress(completionProgress); diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs index cbc62a1c7c..fb31f88d3c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default protected IBindable AccentColourBindable { get; private set; } = null!; + private IBindable pathVersion = null!; + [Resolved(CanBeNull = true)] private OsuRulesetConfigManager? config { get; set; } @@ -31,8 +33,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default ScaleBindable = drawableSlider.ScaleBindable.GetBoundCopy(); ScaleBindable.BindValueChanged(scale => PathRadius = OsuHitObject.OBJECT_RADIUS * scale.NewValue, true); - drawableObject.DefaultsApplied += _ => Refresh(); - drawableObject.HitObjectApplied += _ => Refresh(); + pathVersion = drawableSlider.PathVersion.GetBoundCopy(); + pathVersion.BindValueChanged(_ => Scheduler.AddOnce(Refresh)); AccentColourBindable = drawableObject.AccentColour.GetBoundCopy(); AccentColourBindable.BindValueChanged(accent => AccentColour = GetBodyAccentColour(skin, accent.NewValue), true); From ce643aa68f35369be1a975bb1ceb69fb54192cf2 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 13:54:04 +0100 Subject: [PATCH 5/7] revert overwriting Position getter in SliderTailCircle It would have very weird implications when combined with the position bindable which would be all wrong and stuff --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 4 ++++ osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs | 8 -------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 2aca83ad03..032f105ded 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -218,6 +218,7 @@ namespace osu.Game.Rulesets.Osu.Objects { RepeatIndex = e.SpanIndex, StartTime = e.Time, + Position = EndPosition, StackHeight = StackHeight, ClassicSliderBehaviour = ClassicSliderBehaviour, }); @@ -244,6 +245,9 @@ namespace osu.Game.Rulesets.Osu.Objects if (HeadCircle != null) HeadCircle.Position = Position; + + if (TailCircle != null) + TailCircle.Position = EndPosition; } protected void UpdateNestedSamples() diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index abe2c7074b..ceee513412 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -1,11 +1,9 @@ // 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 osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; -using osuTK; namespace osu.Game.Rulesets.Osu.Objects { @@ -17,12 +15,6 @@ namespace osu.Game.Rulesets.Osu.Objects /// public bool ClassicSliderBehaviour; - public override Vector2 Position - { - get => Slider.EndPosition; - set => throw new NotImplementedException(); - } - public SliderTailCircle(Slider slider) : base(slider) { From 86382f4408baa4bc292b2c510a9db49a8af0f6c9 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Mon, 15 Jan 2024 12:49:40 +0100 Subject: [PATCH 6/7] Clarify comment --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 2f012e33da..f5840248fb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -250,7 +250,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateAfterChildren(); - // It's important that this is done in UpdateAfterChildren so that the SliderBody has a chance to update its Size and PathOffset on Update. + // During slider path editing, the PlaySliderBody is scheduled to refresh once in the Update phase. + // It is crucial to perform the code below in UpdateAfterChildren. This ensures that the SliderBody has the opportunity + // to update its Size and PathOffset beforehand, ensuring correct placement. + double completionProgress = Math.Clamp((Time.Current - HitObject.StartTime) / HitObject.Duration, 0, 1); Ball.UpdateProgress(completionProgress); From 96ffe8e737e327517e4d585d1a5dd8dff754032b Mon Sep 17 00:00:00 2001 From: OliBomby Date: Mon, 15 Jan 2024 12:51:08 +0100 Subject: [PATCH 7/7] change wording --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index f5840248fb..ed4c02a4a9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -250,7 +250,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateAfterChildren(); - // During slider path editing, the PlaySliderBody is scheduled to refresh once in the Update phase. + // During slider path editing, the PlaySliderBody is scheduled to refresh once on Update. // It is crucial to perform the code below in UpdateAfterChildren. This ensures that the SliderBody has the opportunity // to update its Size and PathOffset beforehand, ensuring correct placement.