1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-06 06:17:23 +08:00

Fix SPM changing incorrectly with playback rate changes

This commit is contained in:
Dean Herbert 2020-09-21 19:39:54 +09:00
parent 0cecb2bba3
commit 3f788da06d
4 changed files with 75 additions and 6 deletions

View File

@ -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]
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);
}
}
}

View File

@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
@ -59,7 +61,7 @@ namespace osu.Game.Rulesets.UI
{
if (clock != null)
{
stabilityGameplayClock.ParentGameplayClock = parentGameplayClock = clock;
parentGameplayClock = stabilityGameplayClock.ParentGameplayClock = clock;
GameplayClock.IsPaused.BindTo(clock.IsPaused);
}
}
@ -191,7 +193,9 @@ namespace osu.Game.Rulesets.UI
private class StabilityGameplayClock : GameplayClock
{
public IFrameBasedClock ParentGameplayClock;
public GameplayClock ParentGameplayClock;
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => ParentGameplayClock.NonGameplayAdjustments;
public StabilityGameplayClock(FramedClock underlyingClock)
: base(underlyingClock)

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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();
/// <summary>
/// All adjustments applied to this clock which don't come from gameplay or mods.
/// </summary>
public virtual IEnumerable<Bindable<double>> NonGameplayAdjustments => Enumerable.Empty<Bindable<double>>();
public GameplayClock(IFrameBasedClock underlyingClock)
{
this.underlyingClock = underlyingClock;
@ -29,6 +36,23 @@ namespace osu.Game.Screens.Play
public double Rate => underlyingClock.Rate;
/// <summary>
/// The rate of gameplay when playback is at 100%.
/// This excludes any seeking / user adjustments.
/// </summary>
public double TrueGameplayRate
{
get
{
double baseRate = Rate;
foreach (var adjustment in NonGameplayAdjustments)
baseRate /= adjustment.Value;
return baseRate;
}
}
public bool IsRunning => underlyingClock.IsRunning;
/// <summary>

View File

@ -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
/// <summary>
/// The final clock which is exposed to underlying components.
/// </summary>
[Cached]
public readonly GameplayClock GameplayClock;
public GameplayClock GameplayClock => localGameplayClock;
[Cached(typeof(GameplayClock))]
private readonly LocalGameplayClock localGameplayClock;
private Bindable<double> 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,11 +203,26 @@ namespace osu.Game.Screens.Play
protected override void Update()
{
if (!IsPaused.Value)
{
userOffsetClock.ProcessFrame();
}
base.Update();
}
private double getTrueGameplayRate()
{
double baseRate = track.Rate;
if (speedAdjustmentsApplied)
{
baseRate /= UserPlaybackRate.Value;
baseRate /= pauseFreqAdjust.Value;
}
return baseRate;
}
private bool speedAdjustmentsApplied;
private void updateRate()
@ -215,6 +233,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 +252,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;
}
public class LocalGameplayClock : GameplayClock
{
public readonly List<Bindable<double>> MutableNonGameplayAdjustments = new List<Bindable<double>>();
public override IEnumerable<Bindable<double>> 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.