mirror of
https://github.com/osukey/osukey.git
synced 2025-07-02 16:59:53 +09:00
Separate slider body to bypass snaking logic
The snaking logic contains a lot of caching/optimisations and offsetting of the path which is tedious to re-compute when the path changes.
This commit is contained in:
@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="SliderBody"/> which changes its curve depending on the snaking progress.
|
||||
/// </summary>
|
||||
public class SnakingSliderBody : SliderBody, ISliderProgress
|
||||
{
|
||||
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
|
||||
|
||||
public readonly Bindable<bool> SnakingIn = new Bindable<bool>();
|
||||
public readonly Bindable<bool> SnakingOut = new Bindable<bool>();
|
||||
|
||||
public double? SnakedStart { get; private set; }
|
||||
public double? SnakedEnd { get; private set; }
|
||||
|
||||
public override Vector2 PathOffset => snakedPathOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The top-left position of the path when fully snaked.
|
||||
/// </summary>
|
||||
private Vector2 snakedPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The offset of the path from <see cref="snakedPosition"/> when fully snaked.
|
||||
/// </summary>
|
||||
private Vector2 snakedPathOffset;
|
||||
|
||||
private readonly Slider slider;
|
||||
|
||||
public SnakingSliderBody(Slider slider)
|
||||
{
|
||||
this.slider = slider;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
// Generate the entire curve
|
||||
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
|
||||
SetVertices(CurrentCurve);
|
||||
|
||||
// The body is sized to the full path size to avoid excessive autosize computations
|
||||
Size = Path.Size;
|
||||
|
||||
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
|
||||
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (span >= slider.SpanCount() - 1)
|
||||
{
|
||||
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
|
||||
{
|
||||
start = 0;
|
||||
end = SnakingOut ? spanProgress : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = SnakingOut ? spanProgress : 0;
|
||||
}
|
||||
}
|
||||
|
||||
setRange(start, end);
|
||||
}
|
||||
|
||||
private void setRange(double p0, double p1)
|
||||
{
|
||||
if (p0 > p1)
|
||||
MathHelper.Swap(ref p0, ref p1);
|
||||
|
||||
if (SnakedStart == p0 && SnakedEnd == p1) return;
|
||||
|
||||
SnakedStart = p0;
|
||||
SnakedEnd = p1;
|
||||
|
||||
slider.Curve.GetPathToProgress(CurrentCurve, p0, p1);
|
||||
|
||||
SetVertices(CurrentCurve);
|
||||
|
||||
// The bounding box of the path expands as it snakes, which in turn shifts the position of the path.
|
||||
// Depending on the direction of expansion, it may appear as if the path is expanding towards the position of the slider
|
||||
// rather than expanding out from the position of the slider.
|
||||
// To remove this effect, the path's position is shifted towards its final snaked position
|
||||
|
||||
Path.Position = snakedPosition - Path.PositionInBoundingBox(Vector2.Zero);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user