Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update "Argon" skin gameplay counters to new design #25226

Merged
merged 58 commits into from
Nov 12, 2023
Merged
Changes from 1 commit
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
12bb4b6
Support retrieving textures in argon skin from game resources
frenzibyte Oct 22, 2023
8ed660d
Remove incorrect logic
frenzibyte Oct 22, 2023
9ca672d
Add "Argon" total score counter
frenzibyte Oct 22, 2023
1f36acc
Add "Argon" accuracy counter
frenzibyte Oct 26, 2023
0aa0562
Add "Argon" combo counter
frenzibyte Oct 26, 2023
daf4a03
Abstractify PP counter logic from the "Triangles" implementation
frenzibyte Oct 26, 2023
56eeb11
Add "Argon" performance points counter
frenzibyte Oct 26, 2023
50af157
Add "Argon" background wedges
frenzibyte Oct 26, 2023
df80b4d
Update "Argon" health display specifications to match design
frenzibyte Oct 26, 2023
e119e04
Update "Argon" skin components layout and use the new components
frenzibyte Oct 26, 2023
07e7d53
Bake external scale factors into glyph scale
frenzibyte Oct 26, 2023
841c5bb
Add modified skin for serialisation test coverage
frenzibyte Oct 26, 2023
caddbac
Merge branch 'master' into gameplay-hud-redesign/counters
peppy Oct 30, 2023
4ae9b40
Update resources
peppy Oct 30, 2023
91cf237
Revert health display settings changes
frenzibyte Nov 3, 2023
1c844a1
Remove health line detail
frenzibyte Nov 4, 2023
58a830f
Revert "Add "Argon" performance points counter"
frenzibyte Nov 4, 2023
a23dfbe
Revert "Abstractify PP counter logic from the "Triangles" implementat…
frenzibyte Nov 4, 2023
6c3169a
Remove PP wedge and logic for gameplay layout
frenzibyte Nov 4, 2023
77f5a4c
Update skin deserialisation archive
frenzibyte Nov 4, 2023
1d4f4cf
Merge branch 'master' into gameplay-hud-redesign/counters
frenzibyte Nov 4, 2023
634795e
Adjust failing test scenes
frenzibyte Nov 4, 2023
01e59d1
Adjust health bar settings on default components initialiser to match…
frenzibyte Nov 6, 2023
754e052
Update argon score wedge design
frenzibyte Nov 6, 2023
4c7db4c
Make score counter right-aligned
frenzibyte Nov 6, 2023
ce36884
Make score wireframes display up to required digits count
frenzibyte Nov 6, 2023
7c1c62b
Remove argon combo wedge and update combo counter position
frenzibyte Nov 6, 2023
0dbba13
Split argon score sprite text and update combo counter design
frenzibyte Nov 6, 2023
cbea2db
Support absolute-sized health bar and use it for default layout
frenzibyte Nov 6, 2023
e6d3085
Update accuracy counter design
frenzibyte Nov 7, 2023
d30bac3
Move "required display digits" feature to reside in argon score counter
frenzibyte Nov 7, 2023
fdc714a
Support percentages and ignore dot characters in wireframes part
frenzibyte Nov 7, 2023
4de5454
Bring back left-side line next to health display
frenzibyte Nov 7, 2023
07b7e13
Place health display in front of the score wedge
frenzibyte Nov 7, 2023
18c71c9
Merge branch 'master' into gameplay-hud-redesign/counters
frenzibyte Nov 7, 2023
d0fea38
Update skin deserialisation archives
frenzibyte Nov 7, 2023
2ef17c5
Remove unused local
frenzibyte Nov 7, 2023
6f5d905
Update argon accuracy counter design
frenzibyte Nov 9, 2023
d3af3c6
Merge branch 'master' into gameplay-hud-redesign/counters
frenzibyte Nov 9, 2023
f31c1c9
Rename and move skinnable line component to a more commomn place
peppy Nov 10, 2023
99d9db5
Use a better default size for line
peppy Nov 10, 2023
6c1d48d
Remove unused function
peppy Nov 10, 2023
7c3a626
Add basic animation for combo counter
peppy Nov 10, 2023
e861681
Adjust argon rolling easings
peppy Nov 10, 2023
7e0b412
Change animation to only affect number portion, add miss animation
peppy Nov 10, 2023
4f90ac1
Reduce the default wireframe opacity a bit
peppy Nov 10, 2023
60df272
Rename `RoundedLine` to `BoxElement` and make more generically useful
peppy Nov 10, 2023
7db14ba
Update resources
peppy Nov 10, 2023
fa59218
Update deserialising test
peppy Nov 10, 2023
b4ec624
Remove width/height bindables from `ArgonWedgePiece`
peppy Nov 10, 2023
67312a2
Remove `ArgonScoreWedge` and use `ArgonWedgePiece` directly
peppy Nov 10, 2023
a02aeed
Adjust combo animation slightly
peppy Nov 10, 2023
b7972e3
Merge branch 'skin-size-editing' into gameplay-hud-redesign/counters
bdach Nov 11, 2023
870e4ce
Fix argon health display not handling invalidation correctly
bdach Nov 11, 2023
ea556ab
Merge branch 'skin-size-editing' into gameplay-hud-redesign/counters
bdach Nov 11, 2023
04a16b3
Merge branch 'master' into gameplay-hud-redesign/counters
peppy Nov 11, 2023
c3201d5
Merge branch 'master' into gameplay-hud-redesign/counters
bdach Nov 11, 2023
3e8c89e
Fix one more reference to removed setting
bdach Nov 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Update argon accuracy counter design
  • Loading branch information
frenzibyte committed Nov 9, 2023
commit 6f5d905ce766da9b2e3753585a41298118e9c6b9
59 changes: 58 additions & 1 deletion osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs
Original file line number Diff line number Diff line change
@@ -3,7 +3,9 @@

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.Skinning;
using osuTK;
@@ -22,9 +24,64 @@ public partial class ArgonAccuracyCounter : GameplayAccuracyCounter, ISerialisab

public bool UsesFixedAnchor { get; set; }

protected override IHasText CreateText() => new ArgonCounterTextComponent(Anchor.TopLeft, "ACCURACY", new Vector2(-4, 0))
protected override IHasText CreateText() => new ArgonAccuracyTextComponent
{
WireframeOpacity = { BindTarget = WireframeOpacity },
};

private partial class ArgonAccuracyTextComponent : CompositeDrawable, IHasText
{
private readonly ArgonCounterTextComponent wholePart;
private readonly ArgonCounterTextComponent fractionPart;

public IBindable<float> WireframeOpacity { get; } = new BindableFloat();

public LocalisableString Text
{
get => wholePart.Text;
set
{
string[] split = value.ToString().Replace("%", string.Empty).Split(".");

wholePart.Text = split[0];
fractionPart.Text = "." + split[1];
Comment on lines +47 to +50
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record this only kinda works due to PercentageCounter.FormatCount() / FormatUtils.FormatAccuracy(). You generally cannot and should not be doing such shenanigans with stringed numbers because you might not necessarily know which locale they're formatted in and assuming things about where the dot or percent sign are will end badly.

}
}

public ArgonAccuracyTextComponent()
{
AutoSizeAxes = Axes.Both;

InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new Container
{
AutoSizeAxes = Axes.Both,
Child = wholePart = new ArgonCounterTextComponent(Anchor.TopRight, "ACCURACY")
{
RequiredDisplayDigits = { Value = 3 },
WireframeOpacity = { BindTarget = WireframeOpacity }
}
},
fractionPart = new ArgonCounterTextComponent(Anchor.TopLeft)
{
Margin = new MarginPadding { Top = 12f * 2f + 4f }, // +4 to account for the extra spaces above the digits.
WireframeOpacity = { BindTarget = WireframeOpacity },
Scale = new Vector2(0.5f),
},
new ArgonCounterTextComponent(Anchor.TopLeft)
{
Text = @"%",
Margin = new MarginPadding { Top = 12f },
WireframeOpacity = { BindTarget = WireframeOpacity }
},
}
};
}
}
}
}
94 changes: 49 additions & 45 deletions osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -19,62 +20,30 @@ namespace osu.Game.Screens.Play.HUD
{
public partial class ArgonCounterTextComponent : CompositeDrawable, IHasText
{
private readonly LocalisableString? label;

private readonly ArgonCounterSpriteText wireframesPart;
private readonly ArgonCounterSpriteText textPart;
private readonly OsuSpriteText labelText;

public IBindable<float> WireframeOpacity { get; } = new BindableFloat();
public Bindable<int> RequiredDisplayDigits { get; } = new BindableInt();

public LocalisableString Text
{
get => textPart.Text;
set
{
wireframesPart.Text = FormatWireframes(value);
int remainingCount = RequiredDisplayDigits.Value - value.ToString().Count(char.IsDigit);
string remainingText = remainingCount > 0 ? new string('#', remainingCount) : string.Empty;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to reconsider these implementation from a performance perspective in the next profiling round. These string functions can be quite expensive when this text is being updated every update frame.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've spent some time benchmarking this with various methods of allocating a string, and ended up with the following results:

Method Count Mean Error StdDev Gen0 Gen1 Allocated
AllocateWithStringConstructor 1 198.7 ns 2.44 ns 2.28 ns 0.2487 - 520 B
AllocateWithStringBuilder 1 200.3 ns 3.64 ns 3.04 ns 0.2487 - 520 B
AllocateWithStringCreateMethod 1 290.3 ns 2.06 ns 1.72 ns 0.1988 - 416 B
AllocateAndCache 1 378.5 ns 7.45 ns 8.28 ns 0.6733 1.38 KB
AllocateWithStringConstructor 10 1,667.8 ns 33.02 ns 46.30 ns 1.9341 - 4048 B
AllocateWithStringBuilder 10 1,629.6 ns 16.52 ns 14.64 ns 1.9341 - 4048 B
AllocateWithStringCreateMethod 10 2,790.3 ns 25.84 ns 24.17 ns 1.8845 - 3944 B
AllocateAndCache 10 920.1 ns 17.87 ns 16.71 ns 1.0166 2.08 KB
AllocateWithStringConstructor 100 16,720.6 ns 320.20 ns 393.23 ns 18.7988 - 39328 B
AllocateWithStringBuilder 100 16,475.0 ns 249.08 ns 286.85 ns 18.7988 - 39328 B
AllocateWithStringCreateMethod 100 29,170.3 ns 362.28 ns 302.52 ns 18.7378 - 39224 B
AllocateAndCache 100 6,215.7 ns 124.16 ns 174.06 ns 4.4479 9.11 KB
AllocateWithStringConstructor 1000 175,346.1 ns 2,218.49 ns 2,075.18 ns 133.5449 35.1563 392128 B
AllocateWithStringBuilder 1000 172,832.6 ns 1,270.34 ns 991.80 ns 133.0566 34.9121 392128 B
AllocateWithStringCreateMethod 1000 291,833.8 ns 5,300.95 ns 4,958.51 ns 82.0313 33.6914 392024 B
AllocateAndCache 1000 60,663.7 ns 875.24 ns 730.86 ns 38.4521 79.42 KB
code
// 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.Text;
using BenchmarkDotNet.Attributes;

namespace osu.Game.Benchmarks
{
    public class BenchmarkStrings : BenchmarkTest
    {
        [Params(1, 10, 100, 1000)]
        public int Count { get; set; }

        [Benchmark]
        public string[] AllocateWithStringConstructor()
        {
            StringBuilder builder = new StringBuilder();
            string[] array = new string[Count * 10];

            for (int i = 0; i < Count; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    builder.Clear();
                    builder.Append('#', j);
                    array[i * 10 + j] = builder.ToString();
                }
            }

            return array;
        }

        [Benchmark]
        public string[] AllocateWithStringBuilder()
        {
            StringBuilder builder = new StringBuilder();
            string[] array = new string[Count * 10];

            for (int i = 0; i < Count; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    builder.Clear();
                    builder.Append('#', j);
                    array[i * 10 + j] = builder.ToString();
                }
            }

            return array;
        }

        [Benchmark]
        public string[] AllocateWithStringCreateMethod()
        {
            string[] array = new string[Count * 10];

            for (int i = 0; i < Count; i++)
            {
                for (int j = 0; j < 10; j++)
                    array[i * 10 + j] = string.Create(j, '#', (span, state) => span.Fill(state));
            }

            return array;
        }

        [Benchmark]
        public string[] AllocateAndCache()
        {
            Dictionary<int, string> dictionary = new Dictionary<int, string>();
            string[] array = new string[Count * 10];

            for (int i = 0; i < Count; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    if (!dictionary.TryGetValue(j, out string? value))
                        dictionary[j] = value = new string('#', j);

                    array[i * 10 + j] = value;
                }
            }

            return array;
        }
    }
}

With allocating strings of length 0-10 constant characters over thousand iterations, time sits around 0.175ms, so as far as I understand, it takes around ~0.000175ms on average to allocate a single string per frame, which should be safe enough to not have to add some sort of caching for, but you're more knowledgable in this area than I do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not cpu but memory concerns (which leads to GC etc.) so look at the "allocated" column if anything.


wireframesPart.Text = remainingText + value;
textPart.Text = value;
}
}

public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null, Vector2? spacing = null)
public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null)
{
Anchor = anchor;
Origin = anchor;

this.label = label;

wireframesPart = new ArgonCounterSpriteText(c =>
{
if (c == '.')
return @"dot";

return @"wireframes";
})
{
Anchor = anchor,
Origin = anchor,
Spacing = spacing ?? new Vector2(-2, 0),
};
textPart = new ArgonCounterSpriteText(c =>
{
if (c == '.')
return @"dot";

if (c == '%')
return @"percentage";

return c.ToString();
})
{
Anchor = anchor,
Origin = anchor,
Spacing = spacing ?? new Vector2(-2, 0),
};
}

[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AutoSizeAxes = Axes.Both;

InternalChild = new FillFlowContainer
@@ -83,27 +52,62 @@ private void load(OsuColour colours)
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
labelText = new OsuSpriteText
{
Alpha = label != null ? 1 : 0,
Text = label.GetValueOrDefault(),
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold),
Colour = colours.Blue0,
Margin = new MarginPadding { Left = 2.5f },
},
new Container
{
AutoSizeAxes = Axes.Both,
Children = new[]
{
wireframesPart,
textPart,
wireframesPart = new ArgonCounterSpriteText(wireframesLookup)
{
Anchor = anchor,
Origin = anchor,
},
textPart = new ArgonCounterSpriteText(textLookup)
{
Anchor = anchor,
Origin = anchor,
},
}
}
}
};
}

private string textLookup(char c)
{
switch (c)
{
case '.':
return @"dot";

case '%':
return @"percentage";

default:
return c.ToString();
}
}

private string wireframesLookup(char c)
{
if (c == '.') return @"dot";

return @"wireframes";
}

[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
labelText.Colour = colours.Blue0;
}

protected virtual LocalisableString FormatWireframes(LocalisableString text) => text;

protected override void LoadComplete()
@@ -131,8 +135,8 @@ public ArgonCounterSpriteText(Func<char, string> getLookup)
[BackgroundDependencyLoader]
private void load(ISkinSource skin)
{
// todo: rename font
Font = new FontUsage(@"argon-score", 1);
Spacing = new Vector2(-2f, 0f);
Font = new FontUsage(@"argon-counter", 1);
glyphStore = new GlyphStore(skin, getLookup);
}

5 changes: 0 additions & 5 deletions osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs
Original file line number Diff line number Diff line change
@@ -1,7 +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.

using System;
using osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
@@ -34,14 +33,10 @@ public partial class ArgonScoreCounter : GameplayScoreCounter, ISerialisableDraw

private partial class ArgonScoreTextComponent : ArgonCounterTextComponent
{
public IBindable<int> RequiredDisplayDigits { get; } = new BindableInt();

public ArgonScoreTextComponent(Anchor anchor, LocalisableString? label = null)
: base(anchor, label)
{
}

protected override LocalisableString FormatWireframes(LocalisableString text) => new string('#', Math.Max(text.ToString().Length, RequiredDisplayDigits.Value));
}
}
}