diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 79a4714e33..db704b0553 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -78,7 +78,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0; List curve = drawableSlider.Body.CurrentCurve; - Position = isRepeatAtEnd ? end : start; + var positionOnCurve = isRepeatAtEnd ? end : start; + Position = positionOnCurve + drawableSlider.HitObject.StackOffset; if (curve.Count < 2) return; @@ -89,10 +90,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // find the next vector2 in the curve which is not equal to our current position to infer a rotation. for (int i = searchStart; i >= 0 && i < curve.Count; i += direction) { - if (curve[i] == Position) + if (curve[i] == positionOnCurve) continue; - Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X)); + Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - positionOnCurve.Y, curve[i].X - positionOnCurve.X)); break; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index b55e2372dc..278f3d79f4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics.Primitives; using osu.Game.Configuration; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { var drawableTick = new DrawableSliderTick(tick) { - Position = tick.Position + Position = tick.StackedPosition }; ticks.Add(drawableTick); @@ -81,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { - Position = repeatPoint.Position + Position = repeatPoint.StackedPosition }; repeatPoints.Add(drawableRepeatPoint); @@ -97,7 +96,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut); } - private int currentSpan; public bool Tracking; protected override void Update() @@ -106,16 +104,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Tracking = Ball.Tracking; - GetCurrentProgress(out int span, out double progress); - - if (span > currentSpan) - currentSpan = span; + double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. if (!HeadCircle.IsHit) - HeadCircle.Position = slider.Curve.PositionAt(progress); + HeadCircle.Position = slider.StackedPositionAt(completionProgress); - foreach (var c in components.OfType()) c.UpdateProgress(progress, span); + foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); foreach (var t in components.OfType()) t.Tracking = Ball.Tracking; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 2fda299389..61db10b694 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -139,9 +139,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - public void UpdateProgress(double progress, int span) + public void UpdateProgress(double completionProgress) { - Position = slider.Curve.PositionAt(progress); + Position = slider.StackedPositionAt(completionProgress); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 31ef1c05e6..a83ee3a2e1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -48,8 +48,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return; accentColour = value; - if (LoadState == LoadState.Ready) - Schedule(reloadTexture); + if (LoadState >= LoadState.Ready) + reloadTexture(); } } @@ -66,8 +66,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return; borderColour = value; - if (LoadState == LoadState.Ready) - Schedule(reloadTexture); + if (LoadState >= LoadState.Ready) + reloadTexture(); } } @@ -181,8 +181,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return true; } - public void UpdateProgress(double progress, int span) + public void UpdateProgress(double completionProgress) { + var span = slider.SpanAt(completionProgress); + var spanProgress = slider.ProgressAt(completionProgress); + double start = 0; double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1; @@ -191,11 +194,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1) { start = 0; - end = SnakingOut ? progress : 1; + end = SnakingOut ? spanProgress : 1; } else { - start = SnakingOut ? progress : 0; + start = SnakingOut ? spanProgress : 0; } } diff --git a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs index 54f783b664..a0566eaf17 100644 --- a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs +++ b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs @@ -5,6 +5,10 @@ namespace osu.Game.Rulesets.Osu.Objects { public interface ISliderProgress { - void UpdateProgress(double progress, int span); + /// + /// Updates the progress of this element along the slider. + /// + /// Amount of the slider completed. + void UpdateProgress(double completionProgress); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 5dd3d7aa89..ce6c88a340 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -66,18 +66,6 @@ namespace osu.Game.Rulesets.Osu.Objects /// public double SpanDuration => Duration / this.SpanCount(); - private int stackHeight; - - public override int StackHeight - { - get { return stackHeight; } - set - { - stackHeight = value; - Curve.Offset = StackOffset; - } - } - public double Velocity; public double TickDistance; diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index a22ac6aed1..274f7bff62 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -315,11 +315,11 @@ namespace osu.Game.Rulesets.Osu.Replays for (double j = FrameDelay; j < s.Duration; j += FrameDelay) { - Vector2 pos = s.PositionAt(j / s.Duration); + Vector2 pos = s.StackedPositionAt(j / s.Duration); AddFrameToReplay(new ReplayFrame(h.StartTime + j, pos.X, pos.Y, button)); } - AddFrameToReplay(new ReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button)); + AddFrameToReplay(new ReplayFrame(s.EndTime, s.StackedEndPosition.X, s.StackedEndPosition.Y, button)); } // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed! diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs index 55fa37882d..90a0a450a7 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs @@ -88,10 +88,15 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("Catmull Slider", () => testCatmull()); AddStep("Catmull Slider 1 Repeat", () => testCatmull(1)); AddStep("Catmull Slider 2 Repeats", () => testCatmull(2)); + + AddStep("Big Single, Large StackOffset", () => testSimpleBigLargeStackOffset()); + AddStep("Big 1 Repeat, Large StackOffset", () => testSimpleBigLargeStackOffset(1)); } private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats); + private void testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10); + private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats); private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats); @@ -104,7 +109,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15); - private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2) + private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0) { var slider = new Slider { @@ -118,7 +123,8 @@ namespace osu.Game.Rulesets.Osu.Tests }, Distance = distance, RepeatCount = repeats, - RepeatSamples = createEmptySamples(repeats) + RepeatSamples = createEmptySamples(repeats), + StackHeight = stackHeight }; addSlider(slider, circleSize, speedMultiplier); diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs index 4a65d12977..901d24e531 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs @@ -60,7 +60,9 @@ namespace osu.Game.Tests.Visual AddStep("Load Beatmaps", () => { carousel.BeatmapSets = beatmapSets; }); - AddUntilStep(() => carousel.BeatmapSets.Any(), "Wait for load"); + bool changed = false; + carousel.BeatmapSetsChanged = () => changed = true; + AddUntilStep(() => changed, "Wait for load"); testTraversal(); testFiltering(); diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index 7f03854ea9..c03bdb240e 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -30,21 +30,19 @@ namespace osu.Game.Rulesets.Objects.Types public static class HasCurveExtensions { /// - /// Computes the position on the curve at a given progress, accounting for repeat logic. - /// - /// Ranges from [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - /// + /// Computes the position on the curve relative to how much of the has been completed. /// /// The curve. - /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. + /// [0, 1] where 0 is the start time of the and 1 is the end time of the . + /// The position on the curve. public static Vector2 PositionAt(this IHasCurve obj, double progress) => obj.Curve.PositionAt(obj.ProgressAt(progress)); /// - /// Finds the progress along the curve, accounting for repeat logic. + /// Computes the progress along the curve relative to how much of the has been completed. /// /// The curve. - /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. + /// [0, 1] where 0 is the start time of the and 1 is the end time of the . /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. public static double ProgressAt(this IHasCurve obj, double progress) {