Skip to content

Commit 4bed784

Browse files
authored
Merge pull request #34592 from Hiviexd/feature/argon-unstable-rate-counter
Add "Argon" unstable rate counter
2 parents 9e6d253 + e3f2ffe commit 4bed784

File tree

7 files changed

+185
-99
lines changed

7 files changed

+185
-99
lines changed
1.71 KB
Binary file not shown.

osu.Game.Tests/Skins/SkinDeserialisationTest.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
using osu.Game.IO;
1414
using osu.Game.IO.Archives;
1515
using osu.Game.Screens.Menu;
16-
using osu.Game.Screens.Play.HUD;
1716
using osu.Game.Screens.Play.HUD.HitErrorMeters;
1817
using osu.Game.Skinning;
1918
using osu.Game.Skinning.Components;
19+
using osu.Game.Skinning.Triangles;
2020
using osu.Game.Tests.Resources;
2121

2222
namespace osu.Game.Tests.Skins
@@ -77,6 +77,8 @@ public class SkinDeserialisationTest
7777
"Archives/modified-argon-20250214.osk",
7878
// Covers skinnable leaderboard
7979
"Archives/modified-argon-20250424.osk",
80+
// Covers "Argon" unstable rate counter
81+
"Archives/modified-argon-20250809.osk",
8082
};
8183

8284
/// <summary>
@@ -170,7 +172,7 @@ public void TestDeserialiseModifiedClassic()
170172
{
171173
var skin = new TestSkin(new SkinInfo(), null, storage);
172174
Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.ToArray(), Has.Length.EqualTo(8));
173-
Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(UnstableRateCounter)));
175+
Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(TrianglesUnstableRateCounter)));
174176
Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(ColourHitErrorMeter)));
175177
Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(LegacySongProgress)));
176178
}

osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
#nullable disable
55

66
using System;
7+
using System.Linq;
78
using NUnit.Framework;
89
using osu.Framework.Allocation;
10+
using osu.Framework.Extensions.IEnumerableExtensions;
911
using osu.Framework.Graphics;
1012
using osu.Framework.Testing;
1113
using osu.Game.Rulesets.Judgements;
@@ -14,43 +16,50 @@
1416
using osu.Game.Rulesets.Osu.Scoring;
1517
using osu.Game.Rulesets.Scoring;
1618
using osu.Game.Screens.Play.HUD;
17-
using osuTK;
19+
using osu.Game.Skinning.Triangles;
1820

1921
namespace osu.Game.Tests.Visual.Gameplay
2022
{
21-
public partial class TestSceneUnstableRateCounter : OsuTestScene
23+
public partial class TestSceneUnstableRateCounter : SkinnableHUDComponentTestScene
2224
{
2325
[Cached(typeof(ScoreProcessor))]
2426
private TestScoreProcessor scoreProcessor = new TestScoreProcessor();
2527

2628
private readonly OsuHitWindows hitWindows;
2729

28-
private UnstableRateCounter counter;
29-
3030
private double prev;
3131

32+
protected override Drawable CreateDefaultImplementation() => new TrianglesUnstableRateCounter();
33+
protected override Drawable CreateArgonImplementation() => new ArgonUnstableRateCounter();
34+
protected override Drawable CreateLegacyImplementation() => Empty();
35+
3236
public TestSceneUnstableRateCounter()
3337
{
3438
hitWindows = new OsuHitWindows();
3539
hitWindows.SetDifficulty(5);
3640
}
3741

38-
[SetUpSteps]
39-
public void SetUp()
42+
public override void SetUpSteps()
4043
{
4144
AddStep("Reset Score Processor", () => scoreProcessor.Reset());
45+
base.SetUpSteps();
4246
}
4347

4448
[Test]
45-
public void TestBasic()
49+
public void TestDisplay()
4650
{
47-
AddStep("Create Display", recreateDisplay);
51+
AddSliderStep("UR", 0, 2000, 0, v => this.ChildrenOfType<UnstableRateCounter>().ForEach(c => c.Current.Value = v));
52+
AddToggleStep("toggle validity", v => this.ChildrenOfType<UnstableRateCounter>().ForEach(c => c.IsValid.Value = v));
53+
}
4854

55+
[Test]
56+
public void TestBasic()
57+
{
4958
// Needs multiples 2 by the nature of UR, and went for 4 to be safe.
5059
// Creates a 250 UR by placing a +25ms then a -25ms judgement, which then results in a 250 UR
5160
AddRepeatStep("Set UR to 250", () => applyJudgement(25, true), 4);
5261

53-
AddUntilStep("UR = 250", () => counter.Current.Value == 250.0);
62+
AddUntilStep("UR = 250", () => this.ChildrenOfType<UnstableRateCounter>().All(c => c.Current.Value == 250));
5463

5564
AddRepeatStep("Revert UR", () =>
5665
{
@@ -63,8 +72,8 @@ public void TestBasic()
6372
});
6473
}, 4);
6574

66-
AddUntilStep("UR is 0", () => counter.Current.Value == 0.0);
67-
AddUntilStep("Counter is invalid", () => counter.Child.Alpha == 0.3f);
75+
AddUntilStep("UR is 0", () => this.ChildrenOfType<UnstableRateCounter>().All(c => c.Current.Value == 0));
76+
AddUntilStep("Counter is invalid", () => this.ChildrenOfType<UnstableRateCounter>().All(c => !c.IsValid.Value));
6877

6978
//Sets a UR of 0 by creating 10 10ms offset judgements. Since average = offset, UR = 0
7079
AddRepeatStep("Set UR to 0", () => applyJudgement(10, false), 10);
@@ -77,42 +86,24 @@ public void TestCounterReceivesJudgementsBeforeCreation()
7786
{
7887
AddRepeatStep("Set UR to 250", () => applyJudgement(25, true), 4);
7988

80-
AddStep("Create Display", recreateDisplay);
81-
82-
AddUntilStep("UR = 250", () => counter.Current.Value == 250.0);
89+
AddUntilStep("UR = 250", () => this.ChildrenOfType<UnstableRateCounter>().All(c => c.Current.Value == 250));
8390
}
8491

8592
[Test]
8693
public void TestStaticRateChange()
8794
{
88-
AddStep("Create Display", recreateDisplay);
89-
9095
AddRepeatStep("Set UR to 250 at 1.5x", () => applyJudgement(25, true, 1.5), 4);
9196

92-
AddUntilStep("UR = 250/1.5", () => counter.Current.Value == Math.Round(250.0 / 1.5));
97+
AddUntilStep("UR = 250/1.5", () => this.ChildrenOfType<UnstableRateCounter>().All(c => c.Current.Value == (int)Math.Round(250.0 / 1.5)));
9398
}
9499

95100
[Test]
96101
public void TestDynamicRateChange()
97102
{
98-
AddStep("Create Display", recreateDisplay);
99-
100103
AddRepeatStep("Set UR to 100 at 1.0x", () => applyJudgement(10, true, 1.0), 4);
101104
AddRepeatStep("Bring UR to 100 at 1.5x", () => applyJudgement(15, true, 1.5), 4);
102105

103-
AddUntilStep("UR = 100", () => counter.Current.Value == 100.0);
104-
}
105-
106-
private void recreateDisplay()
107-
{
108-
Clear();
109-
110-
Add(counter = new UnstableRateCounter
111-
{
112-
Anchor = Anchor.Centre,
113-
Origin = Anchor.Centre,
114-
Scale = new Vector2(5),
115-
});
106+
AddUntilStep("UR = 100", () => this.ChildrenOfType<UnstableRateCounter>().All(c => c.Current.Value == 100));
116107
}
117108

118109
private void applyJudgement(double offsetMs, bool alt, double gameplayRate = 1.0)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
2+
// See the LICENCE file in the repository root for full licence text.
3+
4+
using System;
5+
using osu.Framework.Bindables;
6+
using osu.Framework.Graphics;
7+
using osu.Framework.Graphics.Sprites;
8+
using osu.Game.Configuration;
9+
using osu.Game.Localisation.SkinComponents;
10+
using osu.Game.Skinning;
11+
12+
namespace osu.Game.Screens.Play.HUD
13+
{
14+
public partial class ArgonUnstableRateCounter : UnstableRateCounter, ISerialisableDrawable
15+
{
16+
private ArgonCounterTextComponent text = null!;
17+
18+
protected override double RollingDuration => 250;
19+
20+
private const float alpha_when_invalid = 0.3f;
21+
22+
[SettingSource("Wireframe opacity", "Controls the opacity of the wireframes behind the digits.")]
23+
public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f)
24+
{
25+
Precision = 0.01f,
26+
MinValue = 0,
27+
MaxValue = 1,
28+
};
29+
30+
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.ShowLabel))]
31+
public Bindable<bool> ShowLabel { get; } = new BindableBool(true);
32+
33+
protected override void LoadComplete()
34+
{
35+
base.LoadComplete();
36+
37+
IsValid.BindValueChanged(v => text.FadeTo(v.NewValue ? 1 : alpha_when_invalid, 1000, Easing.OutQuint), true);
38+
FinishTransforms(true);
39+
}
40+
41+
public override int DisplayedCount
42+
{
43+
get => base.DisplayedCount;
44+
set
45+
{
46+
base.DisplayedCount = value;
47+
updateWireframe();
48+
}
49+
}
50+
51+
private void updateWireframe()
52+
{
53+
int digitsRequiredForDisplayCount = Math.Max(3, getDigitsRequiredForDisplayCount());
54+
55+
if (digitsRequiredForDisplayCount != text.WireframeTemplate.Length)
56+
text.WireframeTemplate = new string('#', digitsRequiredForDisplayCount);
57+
}
58+
59+
private int getDigitsRequiredForDisplayCount()
60+
{
61+
int digitsRequired = 1;
62+
long c = DisplayedCount;
63+
while ((c /= 10) > 0)
64+
digitsRequired++;
65+
return digitsRequired;
66+
}
67+
68+
protected override IHasText CreateText() => text = new ArgonCounterTextComponent(Anchor.TopRight, "UR")
69+
{
70+
WireframeOpacity = { BindTarget = WireframeOpacity },
71+
ShowLabel = { BindTarget = ShowLabel },
72+
};
73+
}
74+
}

osu.Game/Screens/Play/HUD/UnstableRateCounter.cs

Lines changed: 4 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,29 @@
55
using osu.Framework.Allocation;
66
using osu.Framework.Bindables;
77
using osu.Framework.Extensions.ObjectExtensions;
8-
using osu.Framework.Graphics;
9-
using osu.Framework.Graphics.Containers;
10-
using osu.Framework.Graphics.Sprites;
11-
using osu.Framework.Localisation;
12-
using osu.Game.Graphics;
13-
using osu.Game.Graphics.Sprites;
148
using osu.Game.Graphics.UserInterface;
159
using osu.Game.Rulesets.Judgements;
1610
using osu.Game.Rulesets.Scoring;
17-
using osu.Game.Skinning;
18-
using osuTK;
1911

2012
namespace osu.Game.Screens.Play.HUD
2113
{
22-
public partial class UnstableRateCounter : RollingCounter<int>, ISerialisableDrawable
14+
public abstract partial class UnstableRateCounter : RollingCounter<int>
2315
{
2416
public bool UsesFixedAnchor { get; set; }
2517

2618
protected override double RollingDuration => 375;
2719

28-
private const float alpha_when_invalid = 0.3f;
29-
private readonly Bindable<bool> valid = new Bindable<bool>();
30-
3120
private HitEventExtensions.UnstableRateCalculationResult? unstableRateResult;
3221

3322
[Resolved]
3423
private ScoreProcessor scoreProcessor { get; set; } = null!;
3524

36-
public UnstableRateCounter()
25+
protected UnstableRateCounter()
3726
{
3827
Current.Value = 0;
3928
}
4029

41-
[BackgroundDependencyLoader]
42-
private void load(OsuColour colours)
43-
{
44-
Colour = colours.BlueLighter;
45-
valid.BindValueChanged(e =>
46-
DrawableCount.FadeTo(e.NewValue ? 1 : alpha_when_invalid, 1000, Easing.OutQuint));
47-
}
30+
public Bindable<bool> IsValid { get; } = new Bindable<bool>();
4831

4932
protected override void LoadComplete()
5033
{
@@ -67,17 +50,12 @@ private void updateDisplay()
6750

6851
double? unstableRate = unstableRateResult?.Result;
6952

70-
valid.Value = unstableRate != null;
53+
IsValid.Value = unstableRate != null;
7154

7255
if (unstableRate != null)
7356
Current.Value = (int)Math.Round(unstableRate.Value);
7457
}
7558

76-
protected override IHasText CreateText() => new TextComponent
77-
{
78-
Alpha = alpha_when_invalid,
79-
};
80-
8159
protected override void Dispose(bool isDisposing)
8260
{
8361
base.Dispose(isDisposing);
@@ -88,44 +66,5 @@ protected override void Dispose(bool isDisposing)
8866
scoreProcessor.JudgementReverted -= updateDisplay;
8967
}
9068
}
91-
92-
private partial class TextComponent : CompositeDrawable, IHasText
93-
{
94-
public LocalisableString Text
95-
{
96-
get => text.Text;
97-
set => text.Text = value;
98-
}
99-
100-
private readonly OsuSpriteText text;
101-
102-
public TextComponent()
103-
{
104-
AutoSizeAxes = Axes.Both;
105-
106-
InternalChild = new FillFlowContainer
107-
{
108-
AutoSizeAxes = Axes.Both,
109-
Spacing = new Vector2(2),
110-
Children = new Drawable[]
111-
{
112-
text = new OsuSpriteText
113-
{
114-
Anchor = Anchor.BottomLeft,
115-
Origin = Anchor.BottomLeft,
116-
Font = OsuFont.Numeric.With(size: 16, fixedWidth: true)
117-
},
118-
new OsuSpriteText
119-
{
120-
Anchor = Anchor.BottomLeft,
121-
Origin = Anchor.BottomLeft,
122-
Font = OsuFont.Numeric.With(size: 8, fixedWidth: true),
123-
Text = @"UR",
124-
Padding = new MarginPadding { Bottom = 1.5f }, // align baseline better
125-
}
126-
}
127-
};
128-
}
129-
}
13069
}
13170
}

osu.Game/Skinning/Skin.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ public void UpdateDrawableTarget(SkinnableContainer targetContainer)
221221
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.HUD.LegacyComboCounter", @"osu.Game.Skinning.LegacyComboCounter");
222222
jsonContent = jsonContent.Replace(@"osu.Game.Skinning.LegacyComboCounter", @"osu.Game.Skinning.LegacyDefaultComboCounter");
223223
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.HUD.PerformancePointsCounter", @"osu.Game.Skinning.Triangles.TrianglesPerformancePointsCounter");
224+
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.HUD.UnstableRateCounter", @"osu.Game.Skinning.Triangles.TrianglesUnstableRateCounter");
224225

225226
try
226227
{

0 commit comments

Comments
 (0)