mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 03:02:54 +08:00
Merge pull request #2107 from smoogipoo/sliderbody-rework
Give DrawableSlider an accurate position and size
This commit is contained in:
commit
b9b5d00096
@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
StartTime = lastTickTime,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
StartTime = spanStartTime + t,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
@ -120,14 +120,14 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = spanStartTime + spanDuration,
|
||||
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||
|
||||
public float EndX => Curve.PositionAt(this.ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;
|
||||
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
|
@ -78,8 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
|
||||
List<Vector2> curve = drawableSlider.Body.CurrentCurve;
|
||||
|
||||
var positionOnCurve = isRepeatAtEnd ? end : start;
|
||||
Position = positionOnCurve + drawableSlider.HitObject.StackOffset;
|
||||
Position = isRepeatAtEnd ? end : start;
|
||||
|
||||
if (curve.Count < 2)
|
||||
return;
|
||||
@ -90,10 +89,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] == positionOnCurve)
|
||||
if (curve[i] == Position)
|
||||
continue;
|
||||
|
||||
Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - positionOnCurve.Y, curve[i].X - positionOnCurve.X));
|
||||
Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ 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
|
||||
@ -30,6 +31,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
slider = s;
|
||||
|
||||
Position = s.StackedPosition;
|
||||
|
||||
DrawableSliderTail tail;
|
||||
Container<DrawableSliderTick> ticks;
|
||||
Container<DrawableRepeatPoint> repeatPoints;
|
||||
@ -39,20 +42,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Body = new SliderBody(s)
|
||||
{
|
||||
AccentColour = AccentColour,
|
||||
Position = s.StackedPosition,
|
||||
PathWidth = s.Scale * 64,
|
||||
},
|
||||
ticks = new Container<DrawableSliderTick>(),
|
||||
repeatPoints = new Container<DrawableRepeatPoint>(),
|
||||
ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
||||
repeatPoints = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both },
|
||||
Ball = new SliderBall(s)
|
||||
{
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(s.Scale),
|
||||
AccentColour = AccentColour,
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0
|
||||
},
|
||||
HeadCircle = new DrawableHitCircle(s.HeadCircle),
|
||||
tail = new DrawableSliderTail(s.TailCircle)
|
||||
HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.HeadCircle.Position - s.Position },
|
||||
tail = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.Position - s.Position }
|
||||
};
|
||||
|
||||
components.Add(Body);
|
||||
@ -65,10 +68,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
foreach (var tick in s.NestedHitObjects.OfType<SliderTick>())
|
||||
{
|
||||
var drawableTick = new DrawableSliderTick(tick)
|
||||
{
|
||||
Position = tick.StackedPosition
|
||||
};
|
||||
var drawableTick = new DrawableSliderTick(tick) { Position = tick.Position - s.Position };
|
||||
|
||||
ticks.Add(drawableTick);
|
||||
components.Add(drawableTick);
|
||||
@ -77,10 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
foreach (var repeatPoint in s.NestedHitObjects.OfType<RepeatPoint>())
|
||||
{
|
||||
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this)
|
||||
{
|
||||
Position = repeatPoint.StackedPosition
|
||||
};
|
||||
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { Position = repeatPoint.Position - s.Position };
|
||||
|
||||
repeatPoints.Add(drawableRepeatPoint);
|
||||
components.Add(drawableRepeatPoint);
|
||||
@ -107,11 +104,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
|
||||
if (!HeadCircle.IsHit)
|
||||
HeadCircle.Position = slider.StackedPositionAt(completionProgress);
|
||||
HeadCircle.Position = slider.CurvePositionAt(completionProgress);
|
||||
|
||||
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(completionProgress);
|
||||
foreach (var c in components.OfType<ITrackSnaking>()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0));
|
||||
foreach (var t in components.OfType<IRequireTracking>()) t.Tracking = Ball.Tracking;
|
||||
|
||||
Size = Body.Size;
|
||||
OriginPosition = Body.PathOffset;
|
||||
|
||||
if (DrawSize.X > 0 && DrawSize.Y > 0)
|
||||
{
|
||||
var childAnchorPosition = Vector2.Divide(OriginPosition, DrawSize);
|
||||
foreach (var obj in NestedHitObjects)
|
||||
obj.RelativeAnchorPosition = childAnchorPosition;
|
||||
Ball.RelativeAnchorPosition = childAnchorPosition;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
|
@ -19,8 +19,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public DrawableSliderTail(HitCircle hitCircle)
|
||||
: base(hitCircle)
|
||||
{
|
||||
AlwaysPresent = true;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
FillMode = FillMode.Fit;
|
||||
|
||||
AlwaysPresent = true;
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
@ -141,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
public void UpdateProgress(double completionProgress)
|
||||
{
|
||||
Position = slider.StackedPositionAt(completionProgress);
|
||||
Position = slider.CurvePositionAt(completionProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
set { path.PathWidth = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Offset in absolute coordinates from the start of the curve.
|
||||
/// </summary>
|
||||
public Vector2 PathOffset { get; private set; }
|
||||
|
||||
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
|
||||
|
||||
public readonly Bindable<bool> SnakingIn = new Bindable<bool>();
|
||||
public readonly Bindable<bool> SnakingOut = new Bindable<bool>();
|
||||
|
||||
@ -75,6 +82,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
private int textureWidth => (int)PathWidth * 2;
|
||||
|
||||
private Vector2 topLeftOffset;
|
||||
|
||||
private readonly Slider slider;
|
||||
public SliderBody(Slider s)
|
||||
{
|
||||
@ -84,6 +93,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
container = new BufferedContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CacheDrawnFrameBuffer = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -107,11 +117,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
if (updateSnaking(p0, p1))
|
||||
{
|
||||
// Autosizing does not give us the desired behaviour here.
|
||||
// We want the container to have the same size as the slider,
|
||||
// and to be positioned such that the slider head is at (0,0).
|
||||
container.Size = path.Size;
|
||||
container.Position = -path.PositionInBoundingBox(slider.Curve.PositionAt(0) - CurrentCurve[0]);
|
||||
// The path is generated such that its size encloses it. This change of size causes the path
|
||||
// to move around while snaking, so we need to offset it to make sure it maintains the
|
||||
// same position as when it is fully snaked.
|
||||
var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
|
||||
path.Position = topLeftOffset - newTopLeftOffset;
|
||||
|
||||
container.ForceRedraw();
|
||||
}
|
||||
@ -121,6 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
private void load()
|
||||
{
|
||||
reloadTexture();
|
||||
computeSize();
|
||||
}
|
||||
|
||||
private void reloadTexture()
|
||||
@ -164,7 +175,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
path.Texture = texture;
|
||||
}
|
||||
|
||||
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
|
||||
private void computeSize()
|
||||
{
|
||||
// Generate the entire curve
|
||||
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
|
||||
foreach (Vector2 p in CurrentCurve)
|
||||
path.AddVertex(p);
|
||||
|
||||
Size = path.Size;
|
||||
|
||||
topLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
|
||||
PathOffset = path.PositionInBoundingBox(CurrentCurve[0]);
|
||||
}
|
||||
|
||||
private bool updateSnaking(double p0, double p1)
|
||||
{
|
||||
if (SnakedStart == p0 && SnakedEnd == p1) return false;
|
||||
@ -176,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
path.ClearVertices();
|
||||
foreach (Vector2 p in CurrentCurve)
|
||||
path.AddVertex(p - CurrentCurve[0]);
|
||||
path.AddVertex(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public Vector2 StackedPositionAt(double t) => this.PositionAt(t) + StackOffset;
|
||||
public override Vector2 EndPosition => this.PositionAt(1);
|
||||
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
|
||||
public override Vector2 EndPosition => Position + this.CurvePositionAt(1);
|
||||
|
||||
public SliderCurve Curve { get; } = new SliderCurve();
|
||||
|
||||
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
HeadCircle = new HitCircle
|
||||
{
|
||||
StartTime = StartTime,
|
||||
Position = StackedPosition,
|
||||
Position = Position,
|
||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||
ComboColour = ComboColour,
|
||||
Samples = Samples,
|
||||
@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
TailCircle = new HitCircle
|
||||
{
|
||||
StartTime = EndTime,
|
||||
Position = StackedEndPosition,
|
||||
Position = EndPosition,
|
||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||
ComboColour = ComboColour
|
||||
};
|
||||
@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
SpanIndex = span,
|
||||
SpanStartTime = spanStartTime,
|
||||
StartTime = spanStartTime + timeProgress * SpanDuration,
|
||||
Position = Curve.PositionAt(distanceProgress),
|
||||
Position = Position + Curve.PositionAt(distanceProgress),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
RepeatIndex = repeatIndex,
|
||||
SpanDuration = SpanDuration,
|
||||
StartTime = StartTime + repeat * SpanDuration,
|
||||
Position = Curve.PositionAt(repeat % 2),
|
||||
Position = Position + Curve.PositionAt(repeat % 2),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
|
@ -118,8 +118,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-(distance / 2), 0),
|
||||
new Vector2(distance / 2, 0),
|
||||
Vector2.Zero,
|
||||
new Vector2(distance, 0),
|
||||
},
|
||||
Distance = distance,
|
||||
RepeatCount = repeats,
|
||||
@ -139,9 +139,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(0, 200),
|
||||
new Vector2(200, 0)
|
||||
Vector2.Zero,
|
||||
new Vector2(200, 200),
|
||||
new Vector2(400, 0)
|
||||
},
|
||||
Distance = 600,
|
||||
RepeatCount = repeats,
|
||||
@ -163,12 +163,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(-50, 75),
|
||||
new Vector2(0, 100),
|
||||
new Vector2(100, -200),
|
||||
Vector2.Zero,
|
||||
new Vector2(150, 75),
|
||||
new Vector2(200, 0),
|
||||
new Vector2(230, 0)
|
||||
new Vector2(300, -200),
|
||||
new Vector2(400, 0),
|
||||
new Vector2(430, 0)
|
||||
},
|
||||
Distance = 793.4417,
|
||||
RepeatCount = repeats,
|
||||
@ -190,11 +190,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(-50, 75),
|
||||
new Vector2(0, 100),
|
||||
new Vector2(100, -200),
|
||||
new Vector2(230, 0)
|
||||
Vector2.Zero,
|
||||
new Vector2(150, 75),
|
||||
new Vector2(200, 100),
|
||||
new Vector2(300, -200),
|
||||
new Vector2(430, 0)
|
||||
},
|
||||
Distance = 480,
|
||||
RepeatCount = repeats,
|
||||
@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(0, 0),
|
||||
Vector2.Zero,
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(0, 0),
|
||||
new Vector2(0, -200),
|
||||
@ -247,10 +247,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
CurveType = CurveType.Catmull,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-100, 0),
|
||||
new Vector2(-50, -50),
|
||||
new Vector2(50, 50),
|
||||
new Vector2(100, 0)
|
||||
Vector2.Zero,
|
||||
new Vector2(50, -50),
|
||||
new Vector2(150, 50),
|
||||
new Vector2(200, 0)
|
||||
},
|
||||
Distance = 300,
|
||||
RepeatCount = repeats,
|
||||
|
@ -35,13 +35,13 @@ namespace osu.Game.Tests.Visual
|
||||
new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f },
|
||||
new Slider
|
||||
{
|
||||
Position = new Vector2(128, 256),
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(128, 256),
|
||||
new Vector2(344, 256),
|
||||
Vector2.Zero,
|
||||
new Vector2(216, 0),
|
||||
},
|
||||
Distance = 400,
|
||||
Position = new Vector2(128, 256),
|
||||
Velocity = 1,
|
||||
TickDistance = 100,
|
||||
Scale = 0.5f,
|
||||
|
@ -41,9 +41,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
}
|
||||
else if ((type & ConvertHitObjectType.Slider) > 0)
|
||||
{
|
||||
var pos = new Vector2(int.Parse(split[0]), int.Parse(split[1]));
|
||||
|
||||
CurveType curveType = CurveType.Catmull;
|
||||
double length = 0;
|
||||
var points = new List<Vector2> { new Vector2(int.Parse(split[0]), int.Parse(split[1])) };
|
||||
var points = new List<Vector2> { Vector2.Zero };
|
||||
|
||||
string[] pointsplit = split[5].Split('|');
|
||||
foreach (string t in pointsplit)
|
||||
@ -69,7 +71,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
}
|
||||
|
||||
string[] temp = t.Split(':');
|
||||
points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)));
|
||||
points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos);
|
||||
}
|
||||
|
||||
int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);
|
||||
@ -134,7 +136,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
for (int i = 0; i < nodes; i++)
|
||||
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
|
||||
|
||||
result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount, nodeSamples);
|
||||
result = CreateSlider(pos, combo, points, length, curveType, repeatCount, nodeSamples);
|
||||
}
|
||||
else if ((type & ConvertHitObjectType.Spinner) > 0)
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Objects.Types
|
||||
/// <param name="obj">The curve.</param>
|
||||
/// <param name="progress">[0, 1] where 0 is the start time of the <see cref="HitObject"/> and 1 is the end time of the <see cref="HitObject"/>.</param>
|
||||
/// <returns>The position on the curve.</returns>
|
||||
public static Vector2 PositionAt(this IHasCurve obj, double progress)
|
||||
public static Vector2 CurvePositionAt(this IHasCurve obj, double progress)
|
||||
=> obj.Curve.PositionAt(obj.ProgressAt(progress));
|
||||
|
||||
/// <summary>
|
||||
|
Loading…
Reference in New Issue
Block a user