diff --git a/osu.Android.props b/osu.Android.props
index dc3e14c141..afc5d4ec52 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
index f7909071ea..9e78185272 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs
@@ -7,7 +7,6 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
-using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing;
using osu.Framework.Timing;
@@ -194,13 +193,7 @@ namespace osu.Game.Rulesets.Osu.Tests
addSeekStep(0);
- AddStep("adjust track rate", () => MusicController.CurrentTrack.AddAdjustment(AdjustableProperty.Tempo, new BindableDouble(rate)));
- // autoplay replay frames use track time;
- // if a spin takes 1000ms in track time and we're playing with a 2x rate adjustment, the spin will take 500ms of *real* time.
- // therefore we need to apply the rate adjustment to the replay itself to change from track time to real time,
- // as real time is what we care about for spinners
- // (so we're making the spin take 1000ms in real time *always*, regardless of the track clock's rate).
- transformReplay(replay => applyRateAdjustment(replay, rate));
+ AddStep("adjust track rate", () => Player.GameplayClockContainer.UserPlaybackRate.Value = rate);
addSeekStep(1000);
AddAssert("progress almost same", () => Precision.AlmostEquals(expectedProgress, drawableSpinner.Progress, 0.05));
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
index f1a782cbb5..05ed38d241 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
@@ -2,11 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
+using osu.Game.Screens.Play;
using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@@ -77,6 +79,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private bool rotationTransferred;
+ [Resolved(canBeNull: true)]
+ private GameplayClock gameplayClock { get; set; }
+
protected override void Update()
{
base.Update();
@@ -126,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
currentRotation += angle;
// 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);
+ RateAdjustedRotation += (float)(Math.Abs(angle) * (gameplayClock?.TrueGameplayRate ?? Clock.Rate));
}
}
}
diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
index a4af92749f..55c4edfbd1 100644
--- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
+++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
@@ -2,7 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
@@ -59,7 +62,7 @@ namespace osu.Game.Rulesets.UI
{
if (clock != null)
{
- stabilityGameplayClock.ParentGameplayClock = parentGameplayClock = clock;
+ parentGameplayClock = stabilityGameplayClock.ParentGameplayClock = clock;
GameplayClock.IsPaused.BindTo(clock.IsPaused);
}
}
@@ -215,7 +218,9 @@ namespace osu.Game.Rulesets.UI
private class StabilityGameplayClock : GameplayClock
{
- public IFrameBasedClock ParentGameplayClock;
+ public GameplayClock ParentGameplayClock;
+
+ public override IEnumerable> NonGameplayAdjustments => ParentGameplayClock?.NonGameplayAdjustments ?? Enumerable.Empty>();
public StabilityGameplayClock(FramedClock underlyingClock)
: base(underlyingClock)
diff --git a/osu.Game/Screens/Play/GameplayClock.cs b/osu.Game/Screens/Play/GameplayClock.cs
index 4f2cf5005c..45da8816d6 100644
--- a/osu.Game/Screens/Play/GameplayClock.cs
+++ b/osu.Game/Screens/Play/GameplayClock.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Timing;
@@ -20,6 +22,11 @@ namespace osu.Game.Screens.Play
public readonly BindableBool IsPaused = new BindableBool();
+ ///
+ /// All adjustments applied to this clock which don't come from gameplay or mods.
+ ///
+ public virtual IEnumerable> NonGameplayAdjustments => Enumerable.Empty>();
+
public GameplayClock(IFrameBasedClock underlyingClock)
{
this.underlyingClock = underlyingClock;
@@ -29,6 +36,23 @@ namespace osu.Game.Screens.Play
public double Rate => underlyingClock.Rate;
+ ///
+ /// The rate of gameplay when playback is at 100%.
+ /// This excludes any seeking / user adjustments.
+ ///
+ public double TrueGameplayRate
+ {
+ get
+ {
+ double baseRate = Rate;
+
+ foreach (var adjustment in NonGameplayAdjustments)
+ baseRate /= adjustment.Value;
+
+ return baseRate;
+ }
+ }
+
public bool IsRunning => underlyingClock.IsRunning;
///
diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs
index 7a9cb3dddd..6679e56871 100644
--- a/osu.Game/Screens/Play/GameplayClockContainer.cs
+++ b/osu.Game/Screens/Play/GameplayClockContainer.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
@@ -50,8 +51,10 @@ namespace osu.Game.Screens.Play
///
/// The final clock which is exposed to underlying components.
///
- [Cached]
- public readonly GameplayClock GameplayClock;
+ public GameplayClock GameplayClock => localGameplayClock;
+
+ [Cached(typeof(GameplayClock))]
+ private readonly LocalGameplayClock localGameplayClock;
private Bindable userAudioOffset;
@@ -79,7 +82,7 @@ namespace osu.Game.Screens.Play
userOffsetClock = new HardwareCorrectionOffsetClock(platformOffsetClock);
// the clock to be exposed via DI to children.
- GameplayClock = new GameplayClock(userOffsetClock);
+ localGameplayClock = new LocalGameplayClock(userOffsetClock);
GameplayClock.IsPaused.BindTo(IsPaused);
}
@@ -200,7 +203,9 @@ namespace osu.Game.Screens.Play
protected override void Update()
{
if (!IsPaused.Value)
+ {
userOffsetClock.ProcessFrame();
+ }
base.Update();
}
@@ -215,6 +220,9 @@ namespace osu.Game.Screens.Play
track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
+ localGameplayClock.MutableNonGameplayAdjustments.Add(pauseFreqAdjust);
+ localGameplayClock.MutableNonGameplayAdjustments.Add(UserPlaybackRate);
+
speedAdjustmentsApplied = true;
}
@@ -231,9 +239,24 @@ namespace osu.Game.Screens.Play
track.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
track.RemoveAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
+ localGameplayClock.MutableNonGameplayAdjustments.Remove(pauseFreqAdjust);
+ localGameplayClock.MutableNonGameplayAdjustments.Remove(UserPlaybackRate);
+
speedAdjustmentsApplied = false;
}
+ private class LocalGameplayClock : GameplayClock
+ {
+ public readonly List> MutableNonGameplayAdjustments = new List>();
+
+ public override IEnumerable> NonGameplayAdjustments => MutableNonGameplayAdjustments;
+
+ public LocalGameplayClock(FramedOffsetClock underlyingClock)
+ : base(underlyingClock)
+ {
+ }
+ }
+
private class HardwareCorrectionOffsetClock : FramedOffsetClock
{
// we always want to apply the same real-time offset, so it should be adjusted by the difference in playback rate (from realtime) to achieve this.
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 6412f707d0..5fa1685d9b 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index f1e13169a5..60708a39e2 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+
@@ -80,7 +80,7 @@
-
+