Fix gameplay skipping forward during resume operation

This commit is contained in:
Dean Herbert 2022-08-29 19:51:16 +09:00
parent ad5ef52922
commit 75531d2d62
2 changed files with 56 additions and 6 deletions

View File

@ -88,9 +88,7 @@ namespace osu.Game.Screens.Play
ensureSourceClockSet();
// 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);
PrepareStart();
// The case which caused this to be added is FrameStabilityContainer, which manages its own current and elapsed time.
// Because we generally update our own current time quicker than children can query it (via Start/Seek/Update),
@ -111,11 +109,22 @@ namespace osu.Game.Screens.Play
});
}
/// <summary>
/// When <see cref="Start"/> is called, this will be run to give an opportunity to prepare the clock at the correct
/// start location.
/// </summary>
protected virtual void PrepareStart()
{
// 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);
}
/// <summary>
/// Seek to a specific time in gameplay.
/// </summary>
/// <param name="time">The destination time to seek to.</param>
public void Seek(double time)
public virtual void Seek(double time)
{
Logger.Log($"{nameof(GameplayClockContainer)} seeking to {time}");

View File

@ -8,6 +8,7 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
@ -45,6 +46,17 @@ namespace osu.Game.Screens.Play
private readonly List<Bindable<double>> nonGameplayAdjustments = new List<Bindable<double>>();
/// <summary>
/// Stores the time at which the last <see cref="StopGameplayClock"/> call was triggered.
/// This is used to ensure we resume from that precise point in time, ignoring the proceeding frequency ramp.
///
/// Optimally, we'd have gameplay ramp down with the frequency, but I believe this was intentionally disabled
/// to avoid fails occurring after the pause screen has been shown.
///
/// In the future I want to change this.
/// </summary>
private double? actualStopTime;
public override IEnumerable<double> NonGameplayAdjustments => nonGameplayAdjustments.Select(b => b.Value);
/// <summary>
@ -86,10 +98,12 @@ namespace osu.Game.Screens.Play
protected override void StopGameplayClock()
{
actualStopTime = GameplayClock.CurrentTime;
if (IsLoaded)
{
// During normal operation, the source is stopped after performing a frequency ramp.
this.TransformBindableTo(GameplayClock.ExternalPauseFrequencyAdjust, 0, 200, Easing.Out).OnComplete(_ =>
this.TransformBindableTo(GameplayClock.ExternalPauseFrequencyAdjust, 0, 2000, Easing.Out).OnComplete(_ =>
{
if (IsPaused.Value)
base.StopGameplayClock();
@ -108,6 +122,25 @@ namespace osu.Game.Screens.Play
}
}
public override void Seek(double time)
{
// Safety in case the clock is seeked while stopped.
actualStopTime = null;
base.Seek(time);
}
protected override void PrepareStart()
{
if (actualStopTime != null)
{
Seek(actualStopTime.Value);
actualStopTime = null;
}
else
base.PrepareStart();
}
protected override void StartGameplayClock()
{
addSourceClockAdjustments();
@ -116,7 +149,7 @@ namespace osu.Game.Screens.Play
if (IsLoaded)
{
this.TransformBindableTo(GameplayClock.ExternalPauseFrequencyAdjust, 1, 200, Easing.In);
this.TransformBindableTo(GameplayClock.ExternalPauseFrequencyAdjust, 1, 2000, Easing.In);
}
else
{
@ -158,6 +191,14 @@ namespace osu.Game.Screens.Play
private bool speedAdjustmentsApplied;
protected override void Update()
{
base.Update();
if (GameplayClock.ExternalPauseFrequencyAdjust.Value < 1)
Logger.Log($"{GameplayClock.CurrentTime}");
}
private void addSourceClockAdjustments()
{
if (speedAdjustmentsApplied)