mirror of
https://github.com/osukey/osukey.git
synced 2025-06-23 12:18:03 +09: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.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -12,59 +10,93 @@ using osu.Game.Rulesets.UI;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD.KPSCounter
|
namespace osu.Game.Screens.Play.HUD.KPSCounter
|
||||||
{
|
{
|
||||||
public class KeysPerSecondCalculator : IDisposable
|
public class KeysPerSecondCalculator
|
||||||
{
|
{
|
||||||
private static KeysPerSecondCalculator instance;
|
|
||||||
|
|
||||||
public static void AddInput()
|
public static void AddInput()
|
||||||
{
|
{
|
||||||
instance?.onNewInput?.Invoke();
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<double> timestamps;
|
private readonly List<double> timestamps;
|
||||||
private readonly GameplayClock gameplayClock;
|
private GameplayClock? gameplayClock;
|
||||||
private readonly DrawableRuleset drawableRuleset;
|
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()
|
public DrawableRuleset? DrawableRuleset
|
||||||
// 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
|
get => drawableRuleset;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
onResetRequested?.Invoke();
|
||||||
|
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
drawableRuleset = value;
|
||||||
|
baseRate = (drawableRuleset.Mods.FirstOrDefault(m => m is ModRateAdjust) as ModRateAdjust)?.SpeedChange.Value
|
||||||
?? 1;
|
?? 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;
|
private double maxTime = double.NegativeInfinity;
|
||||||
|
|
||||||
public bool Ready => workingClock != null && gameplayClock != null;
|
public bool Ready => workingClock != null && gameplayClock != null;
|
||||||
public int Value => timestamps.Count(isTimestampWithinSpan);
|
public int Value => timestamps.Count(isTimestampWithinSpan);
|
||||||
|
|
||||||
private KeysPerSecondCalculator(GameplayClock gameplayClock, DrawableRuleset drawableRuleset)
|
public KeysPerSecondCalculator()
|
||||||
{
|
{
|
||||||
instance = this;
|
|
||||||
timestamps = new List<double>();
|
timestamps = new List<double>();
|
||||||
this.gameplayClock = gameplayClock ?? throw new ArgumentNullException(nameof(gameplayClock));
|
|
||||||
this.drawableRuleset = drawableRuleset;
|
|
||||||
onNewInput += addTimestamp;
|
onNewInput += addTimestamp;
|
||||||
|
onResetRequested += cleanUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanUp()
|
||||||
|
{
|
||||||
|
timestamps.Clear();
|
||||||
|
maxTime = double.NegativeInfinity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTimestamp()
|
private void addTimestamp()
|
||||||
{
|
{
|
||||||
if (Ready && workingClock.CurrentTime >= maxTime && gameplayClock.TrueGameplayRate > 0)
|
if (workingClock == null) return;
|
||||||
|
|
||||||
|
if (workingClock.CurrentTime >= maxTime)
|
||||||
{
|
{
|
||||||
timestamps.Add(workingClock.CurrentTime);
|
timestamps.Add(workingClock.CurrentTime);
|
||||||
maxTime = workingClock.CurrentTime;
|
maxTime = workingClock.CurrentTime;
|
||||||
@ -73,19 +105,11 @@ namespace osu.Game.Screens.Play.HUD.KPSCounter
|
|||||||
|
|
||||||
private bool isTimestampWithinSpan(double timestamp)
|
private bool isTimestampWithinSpan(double timestamp)
|
||||||
{
|
{
|
||||||
if (!Ready)
|
if (workingClock == null) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
double span = 1000 * rate;
|
double span = 1000 * rate;
|
||||||
double relativeTime = workingClock.CurrentTime - timestamp;
|
double relativeTime = workingClock.CurrentTime - timestamp;
|
||||||
return relativeTime >= 0 && relativeTime <= span;
|
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.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
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 const float alpha_when_invalid = 0.3f;
|
||||||
|
|
||||||
private readonly Bindable<bool> valid = new Bindable<bool>();
|
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)]
|
[Resolved(canBeNull: true)]
|
||||||
private DrawableRuleset drawableRuleset { get; set; }
|
private DrawableRuleset? drawableRuleset
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
MaxValue = 1000,
|
get => calculator.DrawableRuleset;
|
||||||
MinValue = 0
|
set => calculator.DrawableRuleset = value;
|
||||||
};
|
}
|
||||||
|
|
||||||
protected override double RollingDuration => SmoothingTime.Value;
|
protected override double RollingDuration => 350;
|
||||||
|
|
||||||
public bool UsesFixedAnchor { get; set; }
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
@ -48,9 +48,8 @@ namespace osu.Game.Screens.Play.HUD.KPSCounter
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, GameplayClock clock, DrawableRuleset ruleset)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
gameplayClock = clock;
|
|
||||||
Colour = colours.BlueLighter;
|
Colour = colours.BlueLighter;
|
||||||
valid.BindValueChanged(e =>
|
valid.BindValueChanged(e =>
|
||||||
DrawableCount.FadeTo(e.NewValue ? 1 : alpha_when_invalid, 1000, Easing.OutQuint));
|
DrawableCount.FadeTo(e.NewValue ? 1 : alpha_when_invalid, 1000, Easing.OutQuint));
|
||||||
@ -61,7 +60,7 @@ namespace osu.Game.Screens.Play.HUD.KPSCounter
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
valid.Value = calculator.Ready;
|
valid.Value = calculator.Ready;
|
||||||
Current.Value = calculator.Value;
|
Current.Value = calculator.Ready ? calculator.Value : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IHasText CreateText() => new TextComponent
|
protected override IHasText CreateText() => new TextComponent
|
||||||
|
@ -34,7 +34,6 @@ using osu.Game.Rulesets.Scoring;
|
|||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Scoring.Legacy;
|
using osu.Game.Scoring.Legacy;
|
||||||
using osu.Game.Screens.Play.HUD.KPSCounter;
|
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -1046,8 +1045,6 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
fadeOut();
|
fadeOut();
|
||||||
|
|
||||||
KeysPerSecondCalculator.GetInstance()?.Dispose();
|
|
||||||
|
|
||||||
return base.OnExiting(e);
|
return base.OnExiting(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user