mirror of
https://github.com/ppy/osu.git
synced 2025-01-15 15:53:21 +08:00
Merge branch 'master' into hit-samples
# Conflicts: # osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs
This commit is contained in:
commit
c420c55291
@ -1 +1 @@
|
|||||||
Subproject commit 1b0d7a584cad85efcdf25c5155e6e62a4ccb5758
|
Subproject commit cea7e88cad687a6a6613655ddd74be3f15bf49db
|
@ -42,7 +42,8 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
objects.Add(new HitCircle()
|
objects.Add(new HitCircle()
|
||||||
{
|
{
|
||||||
StartTime = time,
|
StartTime = time,
|
||||||
Position = new Vector2(RNG.Next(0, 512), RNG.Next(0, 384))
|
Position = new Vector2(RNG.Next(0, 512), RNG.Next(0, 384)),
|
||||||
|
Scale = RNG.NextSingle(0.5f, 1.0f),
|
||||||
});
|
});
|
||||||
|
|
||||||
time += RNG.Next(50, 500);
|
time += RNG.Next(50, 500);
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Modes.Osu.Objects
|
|||||||
private Vector2[] subdivisionBuffer1;
|
private Vector2[] subdivisionBuffer1;
|
||||||
private Vector2[] subdivisionBuffer2;
|
private Vector2[] subdivisionBuffer2;
|
||||||
|
|
||||||
private const float TOLERANCE = 0.5f;
|
private const float TOLERANCE = 0.25f;
|
||||||
private const float TOLERANCE_SQ = TOLERANCE * TOLERANCE;
|
private const float TOLERANCE_SQ = TOLERANCE * TOLERANCE;
|
||||||
|
|
||||||
public BezierApproximator(List<Vector2> controlPoints)
|
public BezierApproximator(List<Vector2> controlPoints)
|
||||||
@ -36,7 +36,7 @@ namespace osu.Game.Modes.Osu.Objects
|
|||||||
private static bool IsFlatEnough(Vector2[] controlPoints)
|
private static bool IsFlatEnough(Vector2[] controlPoints)
|
||||||
{
|
{
|
||||||
for (int i = 1; i < controlPoints.Length - 1; i++)
|
for (int i = 1; i < controlPoints.Length - 1; i++)
|
||||||
if ((controlPoints[i - 1] - 2 * controlPoints[i] + controlPoints[i + 1]).LengthSquared > TOLERANCE_SQ)
|
if ((controlPoints[i - 1] - 2 * controlPoints[i] + controlPoints[i + 1]).LengthSquared > TOLERANCE_SQ * 4)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -96,7 +96,6 @@ namespace osu.Game.Modes.Osu.Objects
|
|||||||
/// Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing
|
/// Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing
|
||||||
/// the control points until their approximation error vanishes below a given threshold.
|
/// the control points until their approximation error vanishes below a given threshold.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="controlPoints">The control points describing the curve.</param>
|
|
||||||
/// <returns>A list of vectors representing the piecewise-linear approximation.</returns>
|
/// <returns>A list of vectors representing the piecewise-linear approximation.</returns>
|
||||||
public List<Vector2> CreateBezier()
|
public List<Vector2> CreateBezier()
|
||||||
{
|
{
|
||||||
|
102
osu.Game.Modes.Osu/Objects/CircularArcApproximator.cs
Normal file
102
osu.Game.Modes.Osu/Objects/CircularArcApproximator.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using OpenTK;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace osu.Game.Modes.Osu.Objects
|
||||||
|
{
|
||||||
|
public class CircularArcApproximator
|
||||||
|
{
|
||||||
|
private Vector2 A;
|
||||||
|
private Vector2 B;
|
||||||
|
private Vector2 C;
|
||||||
|
|
||||||
|
private int amountPoints;
|
||||||
|
|
||||||
|
private const float TOLERANCE = 0.1f;
|
||||||
|
|
||||||
|
public CircularArcApproximator(Vector2 A, Vector2 B, Vector2 C)
|
||||||
|
{
|
||||||
|
this.A = A;
|
||||||
|
this.B = B;
|
||||||
|
this.C = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a piecewise-linear approximation of a circular arc curve.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A list of vectors representing the piecewise-linear approximation.</returns>
|
||||||
|
public List<Vector2> CreateArc()
|
||||||
|
{
|
||||||
|
float aSq = (B - C).LengthSquared;
|
||||||
|
float bSq = (A - C).LengthSquared;
|
||||||
|
float cSq = (A - B).LengthSquared;
|
||||||
|
|
||||||
|
// If we have a degenerate triangle where a side-length is almost zero, then give up and fall
|
||||||
|
// back to a more numerically stable method.
|
||||||
|
if (Precision.AlmostEquals(aSq, 0) || Precision.AlmostEquals(bSq, 0) || Precision.AlmostEquals(cSq, 0))
|
||||||
|
return new List<Vector2>();
|
||||||
|
|
||||||
|
float s = aSq * (bSq + cSq - aSq);
|
||||||
|
float t = bSq * (aSq + cSq - bSq);
|
||||||
|
float u = cSq * (aSq + bSq - cSq);
|
||||||
|
|
||||||
|
float sum = s + t + u;
|
||||||
|
|
||||||
|
// If we have a degenerate triangle with an almost-zero size, then give up and fall
|
||||||
|
// back to a more numerically stable method.
|
||||||
|
if (Precision.AlmostEquals(sum, 0))
|
||||||
|
return new List<Vector2>();
|
||||||
|
|
||||||
|
Vector2 centre = (s * A + t * B + u * C) / sum;
|
||||||
|
Vector2 dA = A - centre;
|
||||||
|
Vector2 dC = C - centre;
|
||||||
|
|
||||||
|
float r = dA.Length;
|
||||||
|
|
||||||
|
double thetaStart = Math.Atan2(dA.Y, dA.X);
|
||||||
|
double thetaEnd = Math.Atan2(dC.Y, dC.X);
|
||||||
|
|
||||||
|
while (thetaEnd < thetaStart)
|
||||||
|
thetaEnd += 2 * Math.PI;
|
||||||
|
|
||||||
|
double dir = 1;
|
||||||
|
double thetaRange = thetaEnd - thetaStart;
|
||||||
|
|
||||||
|
// Decide in which direction to draw the circle, depending on which side of
|
||||||
|
// AC B lies.
|
||||||
|
Vector2 orthoAC = C - A;
|
||||||
|
orthoAC = new Vector2(orthoAC.Y, -orthoAC.X);
|
||||||
|
if (Vector2.Dot(orthoAC, B - A) < 0)
|
||||||
|
{
|
||||||
|
dir = -dir;
|
||||||
|
thetaRange = 2 * Math.PI - thetaRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We select the amount of points for the approximation by requiring the discrete curvature
|
||||||
|
// to be smaller than the provided tolerance. The exact angle required to meet the tolerance
|
||||||
|
// is: 2 * Math.Acos(1 - TOLERANCE / r)
|
||||||
|
if (2 * r <= TOLERANCE)
|
||||||
|
// This special case is required for extremely short sliders where the radius is smaller than
|
||||||
|
// the tolerance. This is a pathological rather than a realistic case.
|
||||||
|
amountPoints = 2;
|
||||||
|
else
|
||||||
|
amountPoints = Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - TOLERANCE / r))));
|
||||||
|
|
||||||
|
List<Vector2> output = new List<Vector2>(amountPoints);
|
||||||
|
|
||||||
|
for (int i = 0; i < amountPoints; ++i)
|
||||||
|
{
|
||||||
|
double fract = (double)i / (amountPoints - 1);
|
||||||
|
double theta = thetaStart + dir * fract * thetaRange;
|
||||||
|
Vector2 o = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * r;
|
||||||
|
output.Add(centre + o);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public class DrawableHitCircle : DrawableOsuHitObject
|
public class DrawableHitCircle : DrawableOsuHitObject
|
||||||
{
|
{
|
||||||
private OsuHitObject osuObject;
|
private HitCircle osuObject;
|
||||||
|
|
||||||
public ApproachCircle ApproachCircle;
|
public ApproachCircle ApproachCircle;
|
||||||
private CirclePiece circle;
|
private CirclePiece circle;
|
||||||
@ -23,12 +23,13 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
|||||||
private NumberPiece number;
|
private NumberPiece number;
|
||||||
private GlowPiece glow;
|
private GlowPiece glow;
|
||||||
|
|
||||||
public DrawableHitCircle(OsuHitObject h) : base(h)
|
public DrawableHitCircle(HitCircle h) : base(h)
|
||||||
{
|
{
|
||||||
osuObject = h;
|
osuObject = h;
|
||||||
|
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
Position = osuObject.Position;
|
Position = osuObject.Position;
|
||||||
|
Scale = new Vector2(osuObject.Scale);
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -104,7 +105,6 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
|||||||
ApproachCircle.Alpha = 0;
|
ApproachCircle.Alpha = 0;
|
||||||
ApproachCircle.Scale = new Vector2(2);
|
ApproachCircle.Scale = new Vector2(2);
|
||||||
explode.Alpha = 0;
|
explode.Alpha = 0;
|
||||||
Scale = new Vector2(0.5f); //this will probably need to be moved to DrawableHitObject at some point.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdatePreemptState()
|
protected override void UpdatePreemptState()
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using osu.Game.Modes.Objects;
|
using osu.Game.Modes.Objects;
|
||||||
using osu.Game.Modes.Objects.Drawables;
|
using osu.Game.Modes.Objects.Drawables;
|
||||||
|
|
||||||
|
@ -27,27 +27,35 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
slider = s;
|
slider = s;
|
||||||
|
|
||||||
Origin = Anchor.TopLeft;
|
|
||||||
Position = Vector2.Zero;
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
body = new SliderBody(s)
|
body = new SliderBody(s)
|
||||||
{
|
{
|
||||||
Position = s.Position,
|
Position = s.Position,
|
||||||
PathWidth = 36,
|
PathWidth = s.Scale * 72,
|
||||||
|
},
|
||||||
|
bouncer1 = new SliderBouncer(s, false)
|
||||||
|
{
|
||||||
|
Position = s.Curve.PositionAt(1),
|
||||||
|
Scale = new Vector2(s.Scale),
|
||||||
|
},
|
||||||
|
bouncer2 = new SliderBouncer(s, true)
|
||||||
|
{
|
||||||
|
Position = s.Position,
|
||||||
|
Scale = new Vector2(s.Scale),
|
||||||
|
},
|
||||||
|
ball = new SliderBall(s)
|
||||||
|
{
|
||||||
|
Scale = new Vector2(s.Scale),
|
||||||
},
|
},
|
||||||
bouncer1 = new SliderBouncer(slider, false) { Position = slider.Curve.PositionAt(1) },
|
|
||||||
bouncer2 = new SliderBouncer(slider, true) { Position = slider.Position },
|
|
||||||
initialCircle = new DrawableHitCircle(new HitCircle
|
initialCircle = new DrawableHitCircle(new HitCircle
|
||||||
{
|
{
|
||||||
StartTime = s.StartTime,
|
StartTime = s.StartTime,
|
||||||
Position = s.Position,
|
Position = s.Position,
|
||||||
|
Scale = s.Scale,
|
||||||
Colour = s.Colour,
|
Colour = s.Colour,
|
||||||
Sample = s.Sample,
|
Sample = s.Sample,
|
||||||
}),
|
}),
|
||||||
ball = new SliderBall(slider),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
components.Add(body);
|
components.Add(body);
|
||||||
@ -56,6 +64,10 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
|||||||
components.Add(bouncer2);
|
components.Add(bouncer2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since the DrawableSlider itself is just a container without a size we need to
|
||||||
|
// pass all input through.
|
||||||
|
public override bool Contains(Vector2 screenSpacePos) => true;
|
||||||
|
|
||||||
int currentRepeat;
|
int currentRepeat;
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
|||||||
ScaleTo(1.6f);
|
ScaleTo(1.6f);
|
||||||
ScaleTo(1, 100, EasingTypes.In);
|
ScaleTo(1, 100, EasingTypes.In);
|
||||||
|
|
||||||
MoveToRelative(new Vector2(0, 100), 800, EasingTypes.InQuint);
|
MoveToOffset(new Vector2(0, 100), 800, EasingTypes.InQuint);
|
||||||
RotateTo(40, 800, EasingTypes.InQuint);
|
RotateTo(40, 800, EasingTypes.InQuint);
|
||||||
|
|
||||||
Delay(600);
|
Delay(600);
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
|
|||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
private Box follow;
|
private Box follow;
|
||||||
|
|
||||||
const float width = 70;
|
const float width = 140;
|
||||||
|
|
||||||
public SliderBall(Slider slider)
|
public SliderBall(Slider slider)
|
||||||
{
|
{
|
||||||
@ -25,7 +25,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
|
|||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
BlendingMode = BlendingMode.Additive;
|
BlendingMode = BlendingMode.Additive;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
BorderThickness = 5;
|
BorderThickness = 10;
|
||||||
BorderColour = Color4.Orange;
|
BorderColour = Color4.Orange;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -45,7 +45,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
|
|||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
BorderThickness = 7,
|
BorderThickness = 14,
|
||||||
BorderColour = Color4.White,
|
BorderColour = Color4.White,
|
||||||
Alpha = 1,
|
Alpha = 1,
|
||||||
CornerRadius = width / 2,
|
CornerRadius = width / 2,
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
|
|||||||
Icon = FontAwesome.fa_eercast,
|
Icon = FontAwesome.fa_eercast,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
TextSize = 24,
|
TextSize = 48,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Game.Beatmaps.Samples;
|
using osu.Game.Beatmaps.Samples;
|
||||||
using osu.Game.Modes.Objects;
|
using osu.Game.Modes.Objects;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Modes.Osu.Objects
|
namespace osu.Game.Modes.Osu.Objects
|
||||||
{
|
{
|
||||||
@ -12,8 +13,17 @@ namespace osu.Game.Modes.Osu.Objects
|
|||||||
{
|
{
|
||||||
public Vector2 Position { get; set; }
|
public Vector2 Position { get; set; }
|
||||||
|
|
||||||
|
public float Scale { get; set; } = 1;
|
||||||
|
|
||||||
public virtual Vector2 EndPosition => Position;
|
public virtual Vector2 EndPosition => Position;
|
||||||
|
|
||||||
|
public override void SetDefaultsFromBeatmap(Beatmap beatmap)
|
||||||
|
{
|
||||||
|
base.SetDefaultsFromBeatmap(beatmap);
|
||||||
|
|
||||||
|
Scale = (1.0f - 0.7f * (beatmap.BeatmapInfo.BaseDifficulty.CircleSize - 5) / 5) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
internal enum HitObjectType
|
internal enum HitObjectType
|
||||||
{
|
{
|
||||||
|
@ -83,7 +83,7 @@ namespace osu.Game.Modes.Osu.Objects
|
|||||||
|
|
||||||
s.Curve = new SliderCurve
|
s.Curve = new SliderCurve
|
||||||
{
|
{
|
||||||
Path = points,
|
ControlPoints = points,
|
||||||
Length = length,
|
Length = length,
|
||||||
CurveType = curveType
|
CurveType = curveType
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Database;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using System;
|
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Modes.Osu.Objects
|
namespace osu.Game.Modes.Osu.Objects
|
||||||
@ -18,6 +16,8 @@ namespace osu.Game.Modes.Osu.Objects
|
|||||||
|
|
||||||
public override void SetDefaultsFromBeatmap(Beatmap beatmap)
|
public override void SetDefaultsFromBeatmap(Beatmap beatmap)
|
||||||
{
|
{
|
||||||
|
base.SetDefaultsFromBeatmap(beatmap);
|
||||||
|
|
||||||
Velocity = 100 / beatmap.BeatLengthAt(StartTime, true) * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier;
|
Velocity = 100 / beatmap.BeatLengthAt(StartTime, true) * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Diagnostics;
|
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using System;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace osu.Game.Modes.Osu.Objects
|
namespace osu.Game.Modes.Osu.Objects
|
||||||
{
|
{
|
||||||
@ -14,21 +13,39 @@ namespace osu.Game.Modes.Osu.Objects
|
|||||||
{
|
{
|
||||||
public double Length;
|
public double Length;
|
||||||
|
|
||||||
public List<Vector2> Path;
|
public List<Vector2> ControlPoints;
|
||||||
|
|
||||||
public CurveTypes CurveType;
|
public CurveTypes CurveType;
|
||||||
|
|
||||||
private List<Vector2> calculatedPath = new List<Vector2>();
|
private List<Vector2> calculatedPath = new List<Vector2>();
|
||||||
private List<double> cumulativeLength = new List<double>();
|
private List<double> cumulativeLength = new List<double>();
|
||||||
|
|
||||||
private List<Vector2> calculateSubpath(List<Vector2> subpath)
|
private List<Vector2> calculateSubpath(List<Vector2> subControlPoints)
|
||||||
{
|
{
|
||||||
switch (CurveType)
|
switch (CurveType)
|
||||||
{
|
{
|
||||||
case CurveTypes.Linear:
|
case CurveTypes.Linear:
|
||||||
|
return subControlPoints;
|
||||||
|
case CurveTypes.PerfectCurve:
|
||||||
|
// If we have a different amount than 3 control points, use bezier for perfect curves.
|
||||||
|
if (ControlPoints.Count != 3)
|
||||||
|
return new BezierApproximator(subControlPoints).CreateBezier();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Assert(subControlPoints.Count == 3);
|
||||||
|
|
||||||
|
// Here we have exactly 3 control points. Attempt to fit a circular arc.
|
||||||
|
List<Vector2> subpath = new CircularArcApproximator(subControlPoints[0], subControlPoints[1], subControlPoints[2]).CreateArc();
|
||||||
|
|
||||||
|
if (subpath.Count == 0)
|
||||||
|
// For some reason a circular arc could not be fit to the 3 given points. Fall back
|
||||||
|
// to a numerically stable bezier approximation.
|
||||||
|
subpath = new BezierApproximator(subControlPoints).CreateBezier();
|
||||||
|
|
||||||
return subpath;
|
return subpath;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return new BezierApproximator(subpath).CreateBezier();
|
return new BezierApproximator(subControlPoints).CreateBezier();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,21 +56,19 @@ namespace osu.Game.Modes.Osu.Objects
|
|||||||
// Sliders may consist of various subpaths separated by two consecutive vertices
|
// Sliders may consist of various subpaths separated by two consecutive vertices
|
||||||
// with the same position. The following loop parses these subpaths and computes
|
// with the same position. The following loop parses these subpaths and computes
|
||||||
// their shape independently, consecutively appending them to calculatedPath.
|
// their shape independently, consecutively appending them to calculatedPath.
|
||||||
List<Vector2> subpath = new List<Vector2>();
|
List<Vector2> subControlPoints = new List<Vector2>();
|
||||||
for (int i = 0; i < Path.Count; ++i)
|
for (int i = 0; i < ControlPoints.Count; ++i)
|
||||||
{
|
{
|
||||||
subpath.Add(Path[i]);
|
subControlPoints.Add(ControlPoints[i]);
|
||||||
if (i == Path.Count - 1 || Path[i] == Path[i + 1])
|
if (i == ControlPoints.Count - 1 || ControlPoints[i] == ControlPoints[i + 1])
|
||||||
{
|
{
|
||||||
// If we already constructed a subpath previously, then the new subpath
|
List<Vector2> subpath = calculateSubpath(subControlPoints);
|
||||||
// will have as starting position the end position of the previous subpath.
|
for (int j = 0; j < subpath.Count; ++j)
|
||||||
// Hence we can and should remove the previous endpoint to avoid a segment
|
// Only add those vertices that add a new segment to the path.
|
||||||
// with 0 length.
|
if (calculatedPath.Count == 0 || calculatedPath.Last() != subpath[j])
|
||||||
if (calculatedPath.Count > 0)
|
calculatedPath.Add(subpath[j]);
|
||||||
calculatedPath.RemoveAt(calculatedPath.Count - 1);
|
|
||||||
|
|
||||||
calculatedPath.AddRange(calculateSubpath(subpath));
|
subControlPoints.Clear();
|
||||||
subpath.Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Objects\BezierApproximator.cs" />
|
<Compile Include="Objects\BezierApproximator.cs" />
|
||||||
|
<Compile Include="Objects\CircularArcApproximator.cs" />
|
||||||
<Compile Include="Objects\Drawables\DrawableOsuHitObject.cs" />
|
<Compile Include="Objects\Drawables\DrawableOsuHitObject.cs" />
|
||||||
<Compile Include="Objects\Drawables\Pieces\ApproachCircle.cs" />
|
<Compile Include="Objects\Drawables\Pieces\ApproachCircle.cs" />
|
||||||
<Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
|
<Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
|
||||||
|
@ -35,6 +35,8 @@ namespace osu.Game.Modes.UI
|
|||||||
public class ScaledContainer : Container
|
public class ScaledContainer : Container
|
||||||
{
|
{
|
||||||
protected override Vector2 DrawScale => new Vector2(DrawSize.X / 512);
|
protected override Vector2 DrawScale => new Vector2(DrawSize.X / 512);
|
||||||
|
|
||||||
|
public override bool Contains(Vector2 screenSpacePos) => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HitObjectContainer : Container<DrawableHitObject>
|
public class HitObjectContainer : Container<DrawableHitObject>
|
||||||
|
@ -190,18 +190,45 @@ namespace osu.Game.Screens.Select
|
|||||||
return 125 + x;
|
return 125 + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update a panel's x position and multiplicative alpha based on its y position and
|
||||||
|
/// the current scroll position.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="p">The panel to be updated.</param>
|
||||||
|
/// <param name="halfHeight">Half the draw height of the carousel container.</param>
|
||||||
|
private void updatePanel(Panel p, float halfHeight)
|
||||||
|
{
|
||||||
|
float panelDrawY = p.Position.Y - Current + p.DrawHeight / 2;
|
||||||
|
float dist = Math.Abs(1f - panelDrawY / halfHeight);
|
||||||
|
|
||||||
|
// Setting the origin position serves as an additive position on top of potential
|
||||||
|
// local transformation we may want to apply (e.g. when a panel gets selected, we
|
||||||
|
// may want to smoothly transform it leftwards.)
|
||||||
|
p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0);
|
||||||
|
|
||||||
|
// We are applying a multiplicative alpha (which is internally done by nesting an
|
||||||
|
// additional container and setting that container's alpha) such that we can
|
||||||
|
// layer transformations on top, with a similar reasoning to the previous comment.
|
||||||
|
p.SetMultiplicativeAlpha(MathHelper.Clamp(1.75f - 1.5f * dist, 0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
|
// Determine which items stopped being on screen for future removal from the lifetimelist.
|
||||||
float drawHeight = DrawHeight;
|
float drawHeight = DrawHeight;
|
||||||
|
float halfHeight = drawHeight / 2;
|
||||||
|
|
||||||
Lifetime.AliveItems.ForEach(delegate (Panel p)
|
foreach (Panel p in Lifetime.AliveItems)
|
||||||
{
|
{
|
||||||
float panelPosY = p.Position.Y;
|
float panelPosY = p.Position.Y;
|
||||||
p.IsOnScreen = panelPosY >= Current - p.DrawHeight && panelPosY <= Current + drawHeight;
|
p.IsOnScreen = panelPosY >= Current - p.DrawHeight && panelPosY <= Current + drawHeight;
|
||||||
});
|
updatePanel(p, halfHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine range of indices for items that are now definitely on screen to be added
|
||||||
|
// to the lifetimelist in the future.
|
||||||
int firstIndex = yPositions.BinarySearch(Current - Panel.MAX_HEIGHT);
|
int firstIndex = yPositions.BinarySearch(Current - Panel.MAX_HEIGHT);
|
||||||
if (firstIndex < 0) firstIndex = ~firstIndex;
|
if (firstIndex < 0) firstIndex = ~firstIndex;
|
||||||
int lastIndex = yPositions.BinarySearch(Current + drawHeight);
|
int lastIndex = yPositions.BinarySearch(Current + drawHeight);
|
||||||
@ -210,26 +237,11 @@ namespace osu.Game.Screens.Select
|
|||||||
Lifetime.StartIndex = firstIndex;
|
Lifetime.StartIndex = firstIndex;
|
||||||
Lifetime.EndIndex = lastIndex;
|
Lifetime.EndIndex = lastIndex;
|
||||||
|
|
||||||
float halfHeight = drawHeight / 2;
|
|
||||||
|
|
||||||
for (int i = firstIndex; i < lastIndex; ++i)
|
for (int i = firstIndex; i < lastIndex; ++i)
|
||||||
{
|
{
|
||||||
var panel = Lifetime[i];
|
Panel p = Lifetime[i];
|
||||||
|
p.IsOnScreen = true;
|
||||||
panel.IsOnScreen = true;
|
updatePanel(p, halfHeight);
|
||||||
|
|
||||||
float panelDrawY = panel.Position.Y - Current + panel.DrawHeight / 2;
|
|
||||||
float dist = Math.Abs(1f - panelDrawY / halfHeight);
|
|
||||||
|
|
||||||
// Setting the origin position serves as an additive position on top of potential
|
|
||||||
// local transformation we may want to apply (e.g. when a panel gets selected, we
|
|
||||||
// may want to smoothly transform it leftwards.)
|
|
||||||
panel.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0);
|
|
||||||
|
|
||||||
// We are applying a multiplicative alpha (which is internally done by nesting an
|
|
||||||
// additional container and setting that container's alpha) such that we can
|
|
||||||
// layer transformations on top, with a similar reasoning to the previous comment.
|
|
||||||
panel.SetMultiplicativeAlpha(MathHelper.Clamp(1.75f - 1.5f * dist, 0, 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user