diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
index b6f4efc24c..69857f8ef9 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
@@ -70,11 +70,11 @@ namespace osu.Game.Rulesets.Osu.Tests
trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f);
});
AddAssert("is disc rotation not almost 0", () => !Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, 0, 100));
- AddAssert("is disc rotation absolute not almost 0", () => !Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, 0, 100));
+ AddAssert("is disc rotation absolute not almost 0", () => !Precision.AlmostEquals(drawableSpinner.RotationTracker.RateAdjustedRotation, 0, 100));
addSeekStep(0);
AddAssert("is disc rotation almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, 0, trackerRotationTolerance));
- AddAssert("is disc rotation absolute almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, 0, 100));
+ AddAssert("is disc rotation absolute almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.RateAdjustedRotation, 0, 100));
}
[Test]
@@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Tests
finalSpinnerSymbolRotation = spinnerSymbol.Rotation;
spinnerSymbolRotationTolerance = Math.Abs(finalSpinnerSymbolRotation * 0.05f);
});
- AddStep("retrieve cumulative disc rotation", () => finalCumulativeTrackerRotation = drawableSpinner.RotationTracker.CumulativeRotation);
+ AddStep("retrieve cumulative disc rotation", () => finalCumulativeTrackerRotation = drawableSpinner.RotationTracker.RateAdjustedRotation);
addSeekStep(2500);
AddAssert("disc rotation rewound",
@@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Tests
() => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation / 2, spinnerSymbolRotationTolerance));
AddAssert("is cumulative rotation rewound",
// cumulative rotation is not damped, so we're treating it as the "ground truth" and allowing a comparatively smaller margin of error.
- () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, finalCumulativeTrackerRotation / 2, 100));
+ () => Precision.AlmostEquals(drawableSpinner.RotationTracker.RateAdjustedRotation, finalCumulativeTrackerRotation / 2, 100));
addSeekStep(5000);
AddAssert("is disc rotation almost same",
@@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("is symbol rotation almost same",
() => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation, spinnerSymbolRotationTolerance));
AddAssert("is cumulative rotation almost same",
- () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, finalCumulativeTrackerRotation, 100));
+ () => Precision.AlmostEquals(drawableSpinner.RotationTracker.RateAdjustedRotation, finalCumulativeTrackerRotation, 100));
}
[Test]
@@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
// multipled by 2 to nullify the score multiplier. (autoplay mod selected)
var totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2;
- return totalScore == (int)(drawableSpinner.RotationTracker.CumulativeRotation / 360) * SpinnerTick.SCORE_PER_TICK;
+ return totalScore == (int)(drawableSpinner.RotationTracker.RateAdjustedRotation / 360) * SpinnerTick.SCORE_PER_TICK;
});
addSeekStep(0);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index a2a49b5c42..f10d11827b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// these become implicitly hit.
return 1;
- return Math.Clamp(RotationTracker.CumulativeRotation / 360 / Spinner.SpinsRequired, 0, 1);
+ return Math.Clamp(RotationTracker.RateAdjustedRotation / 360 / Spinner.SpinsRequired, 0, 1);
}
}
@@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!SpmCounter.IsPresent && RotationTracker.Tracking)
SpmCounter.FadeIn(HitObject.TimeFadeIn);
- SpmCounter.SetRotation(RotationTracker.CumulativeRotation);
+ SpmCounter.SetRotation(RotationTracker.RateAdjustedRotation);
updateBonusScore();
}
@@ -245,7 +245,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (ticks.Count == 0)
return;
- int spins = (int)(RotationTracker.CumulativeRotation / 360);
+ int spins = (int)(RotationTracker.RateAdjustedRotation / 360);
if (spins < wholeSpins)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs
index dfb692eba9..1476fe6010 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs
@@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
get
{
- int rotations = (int)(drawableSpinner.RotationTracker.CumulativeRotation / 360);
+ int rotations = (int)(drawableSpinner.RotationTracker.RateAdjustedRotation / 360);
if (wholeRotationCount == rotations) return false;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
index 0cc6c842f4..f1a782cbb5 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
@@ -31,17 +31,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public readonly BindableBool Complete = new BindableBool();
///
- /// The total rotation performed on the spinner disc, disregarding the spin direction.
+ /// The total rotation performed on the spinner disc, disregarding the spin direction,
+ /// adjusted for the track's playback rate.
///
///
+ ///
/// This value is always non-negative and is monotonically increasing with time
/// (i.e. will only increase if time is passing forward, but can decrease during rewind).
+ ///
+ ///
+ /// The rotation from each frame is multiplied by the clock's current playback rate.
+ /// The reason this is done is to ensure that spinners give the same score and require the same number of spins
+ /// regardless of whether speed-modifying mods are applied.
+ ///
///
///
- /// If the spinner is spun 360 degrees clockwise and then 360 degrees counter-clockwise,
+ /// Assuming no speed-modifying mods are active,
+ /// if the spinner is spun 360 degrees clockwise and then 360 degrees counter-clockwise,
/// this property will return the value of 720 (as opposed to 0 for ).
+ /// If Double Time is active instead (with a speed multiplier of 1.5x),
+ /// in the same scenario the property will return 720 * 1.5 = 1080.
///
- public float CumulativeRotation { get; private set; }
+ public float RateAdjustedRotation { get; private set; }
///
/// Whether the spinning is spinning at a reasonable speed to be considered visually spinning.
@@ -113,7 +124,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
currentRotation += angle;
- CumulativeRotation += Math.Abs(angle) * Math.Sign(Clock.ElapsedFrameTime);
+ // rate has to be applied each frame, because it's not guaranteed to be constant throughout playback
+ // (see: ModTimeRamp)
+ RateAdjustedRotation += (float)(Math.Abs(angle) * Clock.Rate);
}
}
}