1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 01:52:55 +08:00

Rotate sliders in random mod

This commit is contained in:
Henry Lin 2022-04-01 11:36:20 +08:00
parent 338a21f4f0
commit cabbc486e9
2 changed files with 103 additions and 4 deletions

View File

@ -146,5 +146,40 @@ namespace osu.Game.Rulesets.Osu.Utils
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
}
/// <summary>
/// Rotate a slider about its start position by the specified angle.
/// </summary>
/// <param name="slider">The slider to be rotated.</param>
/// <param name="rotation">The angle to rotate the slider by.</param>
public static void RotateSlider(Slider slider, float rotation)
{
void rotateNestedObject(OsuHitObject nested) => nested.Position = rotateVector(nested.Position - slider.Position, rotation) + slider.Position;
slider.NestedHitObjects.OfType<SliderTick>().ForEach(rotateNestedObject);
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(rotateNestedObject);
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray();
foreach (var point in controlPoints)
point.Position = rotateVector(point.Position, rotation);
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
}
/// <summary>
/// Rotate a vector by the specified angle.
/// </summary>
/// <param name="vector">The vector to be rotated.</param>
/// <param name="rotation">The angle to rotate the vector by.</param>
/// <returns>The rotated vector.</returns>
private static Vector2 rotateVector(Vector2 vector, float rotation)
{
float angle = (float)Math.Atan2(vector.Y, vector.X) + rotation;
float length = vector.Length;
return new Vector2(
length * (float)Math.Cos(angle),
length * (float)Math.Sin(angle)
);
}
}
}

View File

@ -40,12 +40,21 @@ namespace osu.Game.Rulesets.Osu.Utils
float absoluteAngle = (float)Math.Atan2(relativePosition.Y, relativePosition.X);
float relativeAngle = absoluteAngle - previousAngle;
positionInfos.Add(new ObjectPositionInfo(hitObject)
ObjectPositionInfo positionInfo;
positionInfos.Add(positionInfo = new ObjectPositionInfo(hitObject)
{
RelativeAngle = relativeAngle,
DistanceFromPrevious = relativePosition.Length
});
if (hitObject is Slider)
{
var endPositionVector = hitObject.EndPosition - hitObject.Position;
float absoluteRotation = (float)Math.Atan2(endPositionVector.Y, endPositionVector.X);
positionInfo.Rotation = absoluteRotation - absoluteAngle;
absoluteAngle = absoluteRotation;
}
previousPosition = hitObject.EndPosition;
previousAngle = absoluteAngle;
}
@ -123,11 +132,18 @@ namespace osu.Game.Rulesets.Osu.Utils
float previousAbsoluteAngle = 0f;
if (previous != null)
{
if (previous.HitObject is Slider s)
{
previousAbsoluteAngle = getSliderRotation(s);
}
else
{
Vector2 earliestPosition = beforePrevious?.HitObject.EndPosition ?? playfield_centre;
Vector2 relativePosition = previous.HitObject.Position - earliestPosition;
previousAbsoluteAngle = (float)Math.Atan2(relativePosition.Y, relativePosition.X);
}
}
float absoluteAngle = previousAbsoluteAngle + current.PositionInfo.RelativeAngle;
@ -141,6 +157,16 @@ namespace osu.Game.Rulesets.Osu.Utils
posRelativeToPrev = RotateAwayFromEdge(lastEndPosition, posRelativeToPrev);
current.PositionModified = lastEndPosition + posRelativeToPrev;
if (!(current.HitObject is Slider slider))
return;
Vector2 centreOfMassOriginal = calculateCentreOfMass(slider);
Vector2 centreOfMassModified = rotateVector(centreOfMassOriginal, current.PositionInfo.Rotation - current.RotationOriginal);
centreOfMassModified = RotateAwayFromEdge(current.PositionModified, centreOfMassModified);
float relativeRotation = (float)Math.Atan2(centreOfMassModified.Y, centreOfMassModified.X) - (float)Math.Atan2(centreOfMassOriginal.Y, centreOfMassOriginal.X);
RotateSlider(slider, relativeRotation);
}
/// <summary>
@ -287,6 +313,27 @@ namespace osu.Game.Rulesets.Osu.Utils
);
}
private static Vector2 calculateCentreOfMass(Slider slider)
{
int count = 0;
Vector2 sum = Vector2.Zero;
double pathDistance = slider.Distance;
for (double i = 0; i < pathDistance; i++)
{
sum += slider.Path.PositionAt(i / pathDistance);
count++;
}
return sum / count;
}
private static float getSliderRotation(Slider slider)
{
var endPositionVector = slider.EndPosition - slider.Position;
return (float)Math.Atan2(endPositionVector.Y, endPositionVector.X);
}
public class ObjectPositionInfo
{
/// <summary>
@ -309,6 +356,13 @@ namespace osu.Game.Rulesets.Osu.Utils
/// </remarks>
public float DistanceFromPrevious { get; set; }
/// <summary>
/// The rotation of the hit object, relative to its jump angle.
/// For sliders, this is defined as the angle from the slider's start position to its end position, relative to its jump angle.
/// For hit circles and spinners, this property is ignored.
/// </summary>
public float Rotation { get; set; }
/// <summary>
/// The hit object associated with this <see cref="ObjectPositionInfo"/>.
/// </summary>
@ -325,6 +379,7 @@ namespace osu.Game.Rulesets.Osu.Utils
public Vector2 PositionOriginal { get; }
public Vector2 PositionModified { get; set; }
public Vector2 EndPositionModified { get; set; }
public float RotationOriginal { get; }
public ObjectPositionInfo PositionInfo { get; }
public OsuHitObject HitObject => PositionInfo.HitObject;
@ -334,6 +389,15 @@ namespace osu.Game.Rulesets.Osu.Utils
PositionInfo = positionInfo;
PositionModified = PositionOriginal = HitObject.Position;
EndPositionModified = HitObject.EndPosition;
if (HitObject is Slider slider)
{
RotationOriginal = getSliderRotation(slider);
}
else
{
RotationOriginal = 0;
}
}
}
}