Refactor hit object clamping

This commit is contained in:
Henry Lin 2021-07-11 22:01:28 +08:00
parent e10b7867c1
commit c181a724c6

View File

@ -67,18 +67,17 @@ namespace osu.Game.Rulesets.Osu.Mods
applyRandomisation(rateOfChangeMultiplier, previous, current); applyRandomisation(rateOfChangeMultiplier, previous, current);
// Move hit objects back into the playfield if they are outside of it, // Move hit objects back into the playfield if they are outside of it
// which would sometimes happen during big jumps otherwise. Vector2 shift = Vector2.Zero;
current.PositionRandomised = clampToPlayfield(current.PositionRandomised, (float)hitObject.Radius);
hitObject.Position = current.PositionRandomised; if (hitObject is HitCircle circle)
// update end position as it may have changed as a result of the position update.
current.EndPositionRandomised = current.PositionRandomised;
if (hitObject is Slider slider)
{ {
Vector2 shift = moveSliderIntoPlayfield(slider, current); shift = clampHitCircleToPlayfield(circle, current);
}
else if (hitObject is Slider slider)
{
shift = clampSliderToPlayfield(slider, current);
}
if (shift != Vector2.Zero) if (shift != Vector2.Zero)
{ {
@ -95,7 +94,6 @@ namespace osu.Game.Rulesets.Osu.Mods
if (toBeShifted.Count > 0) if (toBeShifted.Count > 0)
applyDecreasingShift(toBeShifted, shift); applyDecreasingShift(toBeShifted, shift);
} }
}
previous = current; previous = current;
} }
@ -145,31 +143,29 @@ namespace osu.Game.Rulesets.Osu.Mods
/// <summary> /// <summary>
/// Moves the <see cref="Slider"/> and all necessary nested <see cref="OsuHitObject"/>s into the <see cref="OsuPlayfield"/> if they aren't already. /// Moves the <see cref="Slider"/> and all necessary nested <see cref="OsuHitObject"/>s into the <see cref="OsuPlayfield"/> if they aren't already.
/// </summary> /// </summary>
/// <returns>The <see cref="Vector2"/> that this slider has been shifted by.</returns> /// <returns>The deviation from the original randomised position in order to fit within the playfield.</returns>
private Vector2 moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) private Vector2 clampSliderToPlayfield(Slider slider, RandomObjectInfo objectInfo)
{ {
var area = calculatePossibleMovementBounds(slider); var area = calculatePossibleMovementBounds(slider);
var prevPosition = slider.Position; var previousPosition = objectInfo.PositionRandomised;
// Clamp slider position to the placement area // Clamp slider position to the placement area
// If the slider is larger than the playfield, force it to stay at the original position // If the slider is larger than the playfield, force it to stay at the original position
var newX = area.Width < 0 var newX = area.Width < 0
? currentObjectInfo.PositionOriginal.X ? objectInfo.PositionOriginal.X
: Math.Clamp(slider.Position.X, area.Left, area.Right); : Math.Clamp(previousPosition.X, area.Left, area.Right);
var newY = area.Height < 0 var newY = area.Height < 0
? currentObjectInfo.PositionOriginal.Y ? objectInfo.PositionOriginal.Y
: Math.Clamp(slider.Position.Y, area.Top, area.Bottom); : Math.Clamp(previousPosition.Y, area.Top, area.Bottom);
slider.Position = new Vector2(newX, newY); slider.Position = objectInfo.PositionRandomised = new Vector2(newX, newY);
objectInfo.EndPositionRandomised = slider.EndPosition;
currentObjectInfo.PositionRandomised = slider.Position; shiftNestedObjects(slider, objectInfo.PositionRandomised - objectInfo.PositionOriginal);
currentObjectInfo.EndPositionRandomised = slider.EndPosition;
shiftNestedObjects(slider, currentObjectInfo.PositionRandomised - currentObjectInfo.PositionOriginal); return objectInfo.PositionRandomised - previousPosition;
return slider.Position - prevPosition;
} }
/// <summary> /// <summary>
@ -187,7 +183,7 @@ namespace osu.Game.Rulesets.Osu.Mods
// The last object is shifted by a vector slightly larger than zero // The last object is shifted by a vector slightly larger than zero
Vector2 position = hitObject.Position + shift * ((hitObjects.Count - i) / (float)(hitObjects.Count + 1)); Vector2 position = hitObject.Position + shift * ((hitObjects.Count - i) / (float)(hitObjects.Count + 1));
hitObject.Position = clampToPlayfield(position, (float)hitObject.Radius); hitObject.Position = clampToPlayfieldWithPadding(position, (float)hitObject.Radius);
} }
} }
@ -256,12 +252,35 @@ namespace osu.Game.Rulesets.Osu.Mods
} }
} }
private Vector2 clampToPlayfield(Vector2 position, float radius) /// <summary>
/// Move the randomised position of a hit circle so that it fits inside the playfield.
/// </summary>
/// <returns>The deviation from the original randomised position in order to fit within the playfield.</returns>
private Vector2 clampHitCircleToPlayfield(HitCircle circle, RandomObjectInfo objectInfo)
{ {
position.X = Math.Clamp(position.X, radius, OsuPlayfield.BASE_SIZE.X - radius); var previousPosition = objectInfo.PositionRandomised;
position.Y = Math.Clamp(position.Y, radius, OsuPlayfield.BASE_SIZE.Y - radius); objectInfo.EndPositionRandomised = objectInfo.PositionRandomised = clampToPlayfieldWithPadding(
objectInfo.PositionRandomised,
(float)circle.Radius
);
return position; circle.Position = objectInfo.PositionRandomised;
return objectInfo.PositionRandomised - previousPosition;
}
/// <summary>
/// Clamp a position to playfield, keeping a specified distance from the edges.
/// </summary>
/// <param name="position">The position to be clamped.</param>
/// <param name="padding">The minimum distance allowed from playfield edges.</param>
/// <returns>The clamped position.</returns>
private Vector2 clampToPlayfieldWithPadding(Vector2 position, float padding)
{
return new Vector2(
Math.Clamp(position.X, padding, OsuPlayfield.BASE_SIZE.X - padding),
Math.Clamp(position.Y, padding, OsuPlayfield.BASE_SIZE.Y - padding)
);
} }
private class RandomObjectInfo private class RandomObjectInfo