From 6301111fa3c1d274c68766d3c1456ebd74d95138 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Apr 2021 20:15:42 +0900 Subject: [PATCH 1/3] Remove ClockToProcess, always process underlying clock --- osu.Game/Screens/Play/GameplayClock.cs | 18 +++++++++--------- .../Screens/Play/GameplayClockContainer.cs | 4 +--- .../Play/MasterGameplayClockContainer.cs | 2 -- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClock.cs b/osu.Game/Screens/Play/GameplayClock.cs index db4b5d300b..54aa395f5f 100644 --- a/osu.Game/Screens/Play/GameplayClock.cs +++ b/osu.Game/Screens/Play/GameplayClock.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Play /// public class GameplayClock : IFrameBasedClock { - private readonly IFrameBasedClock underlyingClock; + internal readonly IFrameBasedClock UnderlyingClock; public readonly BindableBool IsPaused = new BindableBool(); @@ -30,12 +30,12 @@ namespace osu.Game.Screens.Play public GameplayClock(IFrameBasedClock underlyingClock) { - this.underlyingClock = underlyingClock; + UnderlyingClock = underlyingClock; } - public double CurrentTime => underlyingClock.CurrentTime; + public double CurrentTime => UnderlyingClock.CurrentTime; - public double Rate => underlyingClock.Rate; + public double Rate => UnderlyingClock.Rate; /// /// The rate of gameplay when playback is at 100%. @@ -59,19 +59,19 @@ namespace osu.Game.Screens.Play } } - public bool IsRunning => underlyingClock.IsRunning; + public bool IsRunning => UnderlyingClock.IsRunning; public void ProcessFrame() { // intentionally not updating the underlying clock (handled externally). } - public double ElapsedFrameTime => underlyingClock.ElapsedFrameTime; + public double ElapsedFrameTime => UnderlyingClock.ElapsedFrameTime; - public double FramesPerSecond => underlyingClock.FramesPerSecond; + public double FramesPerSecond => UnderlyingClock.FramesPerSecond; - public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo; + public FrameTimeInfo TimeInfo => UnderlyingClock.TimeInfo; - public IClock Source => underlyingClock; + public IClock Source => UnderlyingClock; } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 6d863f0094..b7dc55277f 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -80,15 +80,13 @@ namespace osu.Game.Screens.Play protected override void Update() { if (!IsPaused.Value) - ClockToProcess.ProcessFrame(); + GameplayClock.UnderlyingClock.ProcessFrame(); base.Update(); } protected abstract void OnIsPausedChanged(ValueChangedEvent isPaused); - protected virtual IFrameBasedClock ClockToProcess => AdjustableClock; - protected abstract GameplayClock CreateGameplayClock(IFrameBasedClock source); } } diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 83e21f3c1d..5eb82bf0fa 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -128,8 +128,6 @@ namespace osu.Game.Screens.Play Seek(skipTarget); } - protected override IFrameBasedClock ClockToProcess => userOffsetClock; - protected override GameplayClock CreateGameplayClock(IFrameBasedClock source) { // Lazer's audio timings in general doesn't match stable. This is the result of user testing, albeit limited. From 3a78c19f9695ad5519e8494e8e888f25d50998b9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Apr 2021 20:33:29 +0900 Subject: [PATCH 2/3] More refactoring/xmldocs --- .../Screens/Play/GameplayClockContainer.cs | 64 ++++++++++++++----- .../Play/MasterGameplayClockContainer.cs | 16 +++-- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index b7dc55277f..642ede5f1c 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -9,26 +9,36 @@ using osu.Framework.Timing; namespace osu.Game.Screens.Play { + /// + /// Encapsulates gameplay timing logic and provides a via DI for gameplay components to use. + /// public abstract class GameplayClockContainer : Container { /// - /// The final clock which is exposed to underlying components. + /// The final clock which is exposed to gameplay components. /// public GameplayClock GameplayClock { get; private set; } + /// + /// Whether gameplay is paused. + /// public readonly BindableBool IsPaused = new BindableBool(); /// - /// The decoupled clock used for gameplay. Should be used for seeks and clock control. + /// The adjustable source clock used for gameplay. Should be used for seeks and clock control. /// - protected readonly DecoupleableInterpolatingFramedClock AdjustableClock; + protected readonly DecoupleableInterpolatingFramedClock AdjustableSource; + /// + /// Creates a new . + /// + /// The source used for timing. protected GameplayClockContainer(IClock sourceClock) { RelativeSizeAxes = Axes.Both; - AdjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - AdjustableClock.ChangeSource(sourceClock); + AdjustableSource = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; + AdjustableSource.ChangeSource(sourceClock); IsPaused.BindValueChanged(OnIsPausedChanged); } @@ -37,21 +47,24 @@ namespace osu.Game.Screens.Play { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(GameplayClock = CreateGameplayClock(AdjustableClock)); + dependencies.CacheAs(GameplayClock = CreateGameplayClock(AdjustableSource)); GameplayClock.IsPaused.BindTo(IsPaused); return dependencies; } + /// + /// Starts gameplay. + /// public virtual void Start() { - if (!AdjustableClock.IsRunning) + if (!AdjustableSource.IsRunning) { // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time // This accounts for the clock source potentially taking time to enter a completely stopped state Seek(GameplayClock.CurrentTime); - AdjustableClock.Start(); + AdjustableSource.Start(); } IsPaused.Value = false; @@ -59,19 +72,22 @@ namespace osu.Game.Screens.Play /// /// Seek to a specific time in gameplay. - /// - /// Adjusts for any offsets which have been applied (so the seek may not be the expected point in time on the underlying audio track). - /// /// /// The destination time to seek to. - public virtual void Seek(double time) => AdjustableClock.Seek(time); + public virtual void Seek(double time) => AdjustableSource.Seek(time); + /// + /// Stops gameplay. + /// public virtual void Stop() => IsPaused.Value = true; + /// + /// Restarts gameplay. + /// public virtual void Restart() { - AdjustableClock.Seek(0); - AdjustableClock.Stop(); + AdjustableSource.Seek(0); + AdjustableSource.Stop(); if (!IsPaused.Value) Start(); @@ -85,8 +101,26 @@ namespace osu.Game.Screens.Play base.Update(); } - protected abstract void OnIsPausedChanged(ValueChangedEvent isPaused); + /// + /// Invoked when the value of is changed to start or stop the clock. + /// + /// Whether the clock should now be paused. + protected virtual void OnIsPausedChanged(ValueChangedEvent isPaused) + { + if (isPaused.NewValue) + AdjustableSource.Stop(); + else + AdjustableSource.Start(); + } + /// + /// Creates the final which is exposed via DI to be used by gameplay components. + /// + /// + /// Any intermediate clocks such as platform offsets should be applied here. + /// + /// The providing the source time. + /// The final . protected abstract GameplayClock CreateGameplayClock(IFrameBasedClock source); } } diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 5eb82bf0fa..e7b4645734 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play /// public const double MINIMUM_SKIP_TIME = 1000; - protected Track Track => (Track)AdjustableClock.Source; + protected Track Track => (Track)AdjustableSource.Source; public readonly BindableNumber UserPlaybackRate = new BindableDouble(1) { @@ -84,17 +84,25 @@ namespace osu.Game.Screens.Play Seek(startTime); - AdjustableClock.ProcessFrame(); + AdjustableSource.ProcessFrame(); } protected override void OnIsPausedChanged(ValueChangedEvent isPaused) { + // The source is stopped by a frequency fade first. if (isPaused.NewValue) - this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => AdjustableClock.Stop()); + this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => AdjustableSource.Stop()); else this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); } + /// + /// Seek to a specific time in gameplay. + /// + /// + /// Adjusts for any offsets which have been applied (so the seek may not be the expected point in time on the underlying audio track). + /// + /// The destination time to seek to. public override void Seek(double time) { // remove the offset component here because most of the time we want the seek to be aligned to gameplay, not the audio track. @@ -146,7 +154,7 @@ namespace osu.Game.Screens.Play public void StopUsingBeatmapClock() { removeSourceClockAdjustments(); - AdjustableClock.ChangeSource(new TrackVirtual(beatmap.Track.Length)); + AdjustableSource.ChangeSource(new TrackVirtual(beatmap.Track.Length)); } private bool speedAdjustmentsApplied; From 314b1646bd22db171ecf35c805860dd578394b41 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Apr 2021 20:47:09 +0900 Subject: [PATCH 3/3] Add xmldoc to MasterGameplayClockContainer --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index e7b4645734..db0aa23001 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -16,6 +16,16 @@ using osu.Game.Configuration; namespace osu.Game.Screens.Play { + /// + /// A which uses a as a source. + /// + /// This is the most complete which takes into account all user and platform offsets, + /// and provides implementations for user actions such as skipping or adjusting playback rates that may occur during gameplay. + /// + /// + /// + /// This is intended to be used as a single controller for gameplay, or as a reference source for other s. + /// public class MasterGameplayClockContainer : GameplayClockContainer { ///