mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 04:03:22 +08:00
Refactor KPS
- Remove '#nullable disable' in KeysPerSecondCalculator and KeysPerSecondCounter - Remove KeysPerSecondCalculator IDisposable implementation - Make KeysPerSecondCalculator static instance initialized once by KeysPerSecondCounters - Auto transfer dependencies from KeysPerSecondCounter to KeysPerSecondCalculator using Resolved properties - Add internal reset logic to KeysPerSecondCalculator and make it independent from Player - Use GameplayClock.TrueGameplayRate to get real-time rate. If 0 then it defaults to the last non 0 rate if no such mod is enabled
This commit is contained in:
parent
0886137e39
commit
b2557a8d2d
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -12,59 +10,93 @@ using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Screens.Play.HUD.KPSCounter
|
||||
{
|
||||
public class KeysPerSecondCalculator : IDisposable
|
||||
public class KeysPerSecondCalculator
|
||||
{
|
||||
private static KeysPerSecondCalculator instance;
|
||||
|
||||
public static void AddInput()
|
||||
{
|
||||
instance?.onNewInput?.Invoke();
|
||||
}
|
||||
|
||||
public static KeysPerSecondCalculator GetInstance(GameplayClock gameplayClock = null, DrawableRuleset drawableRuleset = null)
|
||||
{
|
||||
if (instance != null) return instance;
|
||||
|
||||
try
|
||||
{
|
||||
return new KeysPerSecondCalculator(gameplayClock, drawableRuleset);
|
||||
}
|
||||
catch (ArgumentNullException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
onNewInput?.Invoke();
|
||||
}
|
||||
|
||||
private readonly List<double> timestamps;
|
||||
private readonly GameplayClock gameplayClock;
|
||||
private readonly DrawableRuleset drawableRuleset;
|
||||
private GameplayClock? gameplayClock;
|
||||
private DrawableRuleset? drawableRuleset;
|
||||
|
||||
private event Action onNewInput;
|
||||
public GameplayClock? GameplayClock
|
||||
{
|
||||
get => gameplayClock;
|
||||
set
|
||||
{
|
||||
onResetRequested?.Invoke();
|
||||
|
||||
private IClock workingClock => (IClock)drawableRuleset?.FrameStableClock ?? gameplayClock;
|
||||
if (value != null)
|
||||
{
|
||||
gameplayClock = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Having the rate from mods is preferred to using GameplayClock.TrueGameplayRate()
|
||||
// as it returns 0 when paused in replays, not useful for players who want to "analyze" a replay.
|
||||
private double rate => (drawableRuleset.Mods.FirstOrDefault(m => m is ModRateAdjust) as ModRateAdjust)?.SpeedChange.Value
|
||||
public DrawableRuleset? DrawableRuleset
|
||||
{
|
||||
get => drawableRuleset;
|
||||
set
|
||||
{
|
||||
onResetRequested?.Invoke();
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
drawableRuleset = value;
|
||||
baseRate = (drawableRuleset.Mods.FirstOrDefault(m => m is ModRateAdjust) as ModRateAdjust)?.SpeedChange.Value
|
||||
?? 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static event Action? onNewInput;
|
||||
private static event Action? onResetRequested;
|
||||
|
||||
private IClock? workingClock => drawableRuleset?.FrameStableClock;
|
||||
|
||||
private double baseRate;
|
||||
|
||||
private double rate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (gameplayClock != null)
|
||||
{
|
||||
if (gameplayClock.TrueGameplayRate > 0)
|
||||
{
|
||||
baseRate = gameplayClock.TrueGameplayRate;
|
||||
}
|
||||
}
|
||||
|
||||
return baseRate;
|
||||
}
|
||||
}
|
||||
|
||||
private double maxTime = double.NegativeInfinity;
|
||||
|
||||
public bool Ready => workingClock != null && gameplayClock != null;
|
||||
public int Value => timestamps.Count(isTimestampWithinSpan);
|
||||
|
||||
private KeysPerSecondCalculator(GameplayClock gameplayClock, DrawableRuleset drawableRuleset)
|
||||
public KeysPerSecondCalculator()
|
||||
{
|
||||
instance = this;
|
||||
timestamps = new List<double>();
|
||||
this.gameplayClock = gameplayClock ?? throw new ArgumentNullException(nameof(gameplayClock));
|
||||
this.drawableRuleset = drawableRuleset;
|
||||
onNewInput += addTimestamp;
|
||||
onResetRequested += cleanUp;
|
||||
}
|
||||
|
||||
private void cleanUp()
|
||||
{
|
||||
timestamps.Clear();
|
||||
maxTime = double.NegativeInfinity;
|
||||
}
|
||||
|
||||
private void addTimestamp()
|
||||
{
|
||||
if (Ready && workingClock.CurrentTime >= maxTime && gameplayClock.TrueGameplayRate > 0)
|
||||
if (workingClock == null) return;
|
||||
|
||||
if (workingClock.CurrentTime >= maxTime)
|
||||
{
|
||||
timestamps.Add(workingClock.CurrentTime);
|
||||
maxTime = workingClock.CurrentTime;
|
||||
@ -73,19 +105,11 @@ namespace osu.Game.Screens.Play.HUD.KPSCounter
|
||||
|
||||
private bool isTimestampWithinSpan(double timestamp)
|
||||
{
|
||||
if (!Ready)
|
||||
return false;
|
||||
if (workingClock == null) return false;
|
||||
|
||||
double span = 1000 * rate;
|
||||
double relativeTime = workingClock.CurrentTime - timestamp;
|
||||
return relativeTime >= 0 && relativeTime <= span;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
instance = null;
|
||||
}
|
||||
|
||||
~KeysPerSecondCalculator() => Dispose();
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,12 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -24,21 +21,24 @@ namespace osu.Game.Screens.Play.HUD.KPSCounter
|
||||
private const float alpha_when_invalid = 0.3f;
|
||||
|
||||
private readonly Bindable<bool> valid = new Bindable<bool>();
|
||||
private GameplayClock gameplayClock;
|
||||
|
||||
private static readonly KeysPerSecondCalculator calculator = new KeysPerSecondCalculator();
|
||||
|
||||
[Resolved]
|
||||
private GameplayClock? gameplayClock
|
||||
{
|
||||
get => calculator.GameplayClock;
|
||||
set => calculator.GameplayClock = value;
|
||||
}
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private DrawableRuleset drawableRuleset { get; set; }
|
||||
|
||||
private KeysPerSecondCalculator calculator => KeysPerSecondCalculator.GetInstance(gameplayClock, drawableRuleset);
|
||||
|
||||
[SettingSource("Smoothing time", "How smooth the counter should change\nThe more it is smooth, the less it's accurate.")]
|
||||
public BindableNumber<double> SmoothingTime { get; } = new BindableNumber<double>(350)
|
||||
private DrawableRuleset? drawableRuleset
|
||||
{
|
||||
MaxValue = 1000,
|
||||
MinValue = 0
|
||||
};
|
||||
get => calculator.DrawableRuleset;
|
||||
set => calculator.DrawableRuleset = value;
|
||||
}
|
||||
|
||||
protected override double RollingDuration => SmoothingTime.Value;
|
||||
protected override double RollingDuration => 350;
|
||||
|
||||
public bool UsesFixedAnchor { get; set; }
|
||||
|
||||
@ -48,9 +48,8 @@ namespace osu.Game.Screens.Play.HUD.KPSCounter
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, GameplayClock clock, DrawableRuleset ruleset)
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
gameplayClock = clock;
|
||||
Colour = colours.BlueLighter;
|
||||
valid.BindValueChanged(e =>
|
||||
DrawableCount.FadeTo(e.NewValue ? 1 : alpha_when_invalid, 1000, Easing.OutQuint));
|
||||
@ -61,7 +60,7 @@ namespace osu.Game.Screens.Play.HUD.KPSCounter
|
||||
base.Update();
|
||||
|
||||
valid.Value = calculator.Ready;
|
||||
Current.Value = calculator.Value;
|
||||
Current.Value = calculator.Ready ? calculator.Value : 0;
|
||||
}
|
||||
|
||||
protected override IHasText CreateText() => new TextComponent
|
||||
|
@ -34,7 +34,6 @@ using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Scoring.Legacy;
|
||||
using osu.Game.Screens.Play.HUD.KPSCounter;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Users;
|
||||
@ -1046,8 +1045,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
fadeOut();
|
||||
|
||||
KeysPerSecondCalculator.GetInstance()?.Dispose();
|
||||
|
||||
return base.OnExiting(e);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user