-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Add maximum dimensions limit to skinnable gameplay elements #24706
Add maximum dimensions limit to skinnable gameplay elements #24706
Conversation
I have several questions...
Did you try and see if they can be made
Kinda weird to PR this without reaching a consensus on that first... Basically I can't find a thread on this now, but I recall somebody mentioning somewhere that if you make a really small taiko circle texture, what will happen is that on maps with significant note overlap, the circles due to the shrinkage will overlap less, resulting in easier reading. I think that was the rationale (can we summon like a taiko player or something? maybe i'll try the difficulty channel on discord...) Other than that:
|
@@ -100,6 +105,20 @@ IEnumerable<Texture> getTextures(ISkin skin) | |||
string getFrameName(int frameIndex) => $"{componentName}{animationSeparator}{frameIndex}"; | |||
} | |||
|
|||
public static Texture? GetTextureWithMaxSize(this ISkin source, string componentName, Vector2 maxSize, WrapMode wrapModeS = WrapMode.None, WrapMode wrapModeT = WrapMode.None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More to the code side of things, why is this a separate method, but GetAnimation()
gets an optional maxSize
param? Should be one or the other all the way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done some code mashing and removed this overload / new method. See if it reads better to you.
Note that I did this as a prelim requirement. I haven't yet assessed whether the overall direction is correct yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just quickly going over source without getting too deep into it, definitely seems much closer to what I'd expect to see.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done some code mashing and removed this overload / new method. See if it reads better to you.
It would've been better presenting the change as a proposal first before pushing. I'm not in favour with the new code as it means every skin implementation must respect the maxSize
parameter & perform the same exact code of trimming the texture, which is better achieved with an extension method rather than providing that as part of the core method itself, especially since the "maximum size" logic can be executed outside of the core method. Not to mention that this massively increases the diff of this PR.
Both of the reasons above are precisely why I haven't considered doing this.
As an alternative, I wouldn't mind something like skin.GetTexture().WithMaximumSize()
or just keep everything as is and rename GetTextureWithMaxSize
to GetTexture
. Whichever feels better (I would prefer the first, because it's very explicit and reads well in code).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it means every skin implementation must respect the
maxSize
parameter & perform the same exact code of trimming the texture
I don't get why this is a concern... If maxSize
is null
then the method is a no-op, first of all, so there's no performance consideration there. Secondly, we probably want the max size logic in all implementations for future-proofing. (We were even musing briefly in twitch chat about what happens when user skins are not legacy-texture-based but markup-drawable-based in the future, but dismissed that as unimportant for now.) The diff size concern is like basically irrelevant to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the change has already been pushed, and if you're both on board with it then I wouldn't bother to mind it.
Asked about this on discord and the general response seems to be that yes it's a problem but probably not one that needs dealing with every urgently. So I guess this point can be dismissed as not hugely relevant. |
Probably needs to be added to more elements (for example |
One issue with doing this at the texture level is that in the edge case that a skin element is oversize (ie. due to empty transparent padding), it will be downscaled rather than just-work. That may have been one of the original reasons to do it the way I had it. |
I think a better approach to test this is to roll back all the resource changes, then create a standalone autoplay gameplay test which has a custom skin provider that upscales every element (except whitelisted ones) to 4x normal scale or something stupid. I'd explicitly avoid using 2x scaling, as that may trigger That way we can go through and find the elements easily which haven't had their max dimensions set yet, and we also have a whitelist of all elements we want to allow being larger than expected. I think this kind of testing is required regardless of the solution, and it would allow better experimentation with potential alternatives. @frenzibyte want to take a look at this if you agree with the direction? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As commented.
It's possible to replace each use case with a If anything, I would remove the
I believed the consensus has already been reached on peppy's message that "smaller is always allowed"? Well, I suppose the OP doesn't clarify that well.
I've opened this PR to discuss the direction first before making sure this covers all elements. I've already pointed this out in OP at the very end.
Sure, I can prepare one once this PR reaches the testing state, but the assets provided were meant to be enough to provide samples of how the logic for the direction works in practice. |
@@ -47,13 +48,13 @@ private void load(ISkinSource skin, OsuColour colours) | |||
Anchor = Anchor.CentreRight, | |||
Origin = Anchor.CentreLeft, | |||
RelativeSizeAxes = Axes.Both, | |||
Texture = skin.GetTexture("taiko-roll-end", WrapMode.ClampToEdge, WrapMode.ClampToEdge), | |||
Texture = skin.GetTexture("taiko-roll-end", new Vector2(128, 256), WrapMode.ClampToEdge, WrapMode.ClampToEdge), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I intentionally haven't included osu!taiko drum rolls because they're already being resized in the Sprite
definition (notice RSA = Both & FillMode = Fit).
@@ -100,6 +105,20 @@ IEnumerable<Texture> getTextures(ISkin skin) | |||
string getFrameName(int frameIndex) => $"{componentName}{animationSeparator}{frameIndex}"; | |||
} | |||
|
|||
public static Texture? GetTextureWithMaxSize(this ISkin source, string componentName, Vector2 maxSize, WrapMode wrapModeS = WrapMode.None, WrapMode wrapModeT = WrapMode.None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done some code mashing and removed this overload / new method. See if it reads better to you.
It would've been better presenting the change as a proposal first before pushing. I'm not in favour with the new code as it means every skin implementation must respect the maxSize
parameter & perform the same exact code of trimming the texture, which is better achieved with an extension method rather than providing that as part of the core method itself, especially since the "maximum size" logic can be executed outside of the core method. Not to mention that this massively increases the diff of this PR.
Both of the reasons above are precisely why I haven't considered doing this.
As an alternative, I wouldn't mind something like skin.GetTexture().WithMaximumSize()
or just keep everything as is and rename GetTextureWithMaxSize
to GetTexture
. Whichever feels better (I would prefer the first, because it's very explicit and reads well in code).
Definitely, I can look into that. It will get a bit funny with elements that are fine to be scaled (e.g. HUD elements), but I'll play with the test to exclude those and see how it goes. |
Sure, that works better and requires less code changes. (I was almost going to suggest a Feel free to undo my change and apply that, or just revert it.
That's what I meant by a whitelist. It means we will be sure we haven't missed any elements we care about. |
|
||
namespace osu.Game.Tests.Visual.Gameplay | ||
{ | ||
public partial class TestSceneGameplayElementDimensions : TestSceneAllRulesetPlayers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What on earth is this naming..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I...don't know?
This comment was marked as resolved.
This comment was marked as resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As commented.
This comment was marked as resolved.
This comment was marked as resolved.
I managed to fix the crash, it was something weird with my local caches. |
@@ -41,14 +42,14 @@ public OsuLegacySkinTransformer(ISkin skin) | |||
return this.GetAnimation("sliderscorepoint", false, false); | |||
|
|||
case OsuSkinComponents.SliderFollowCircle: | |||
var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true); | |||
var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true, maxSize: new Vector2(308f)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does this 308 come from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wiki, maximum effective follow circle size (https://osu.ppy.sh/wiki/en/Skinning/osu%21#slider, see sliderfollowcircle.png
).
// use scale adjust property for downscaling the texture in order to meet the specified maximum dimensions. | ||
texture.ScaleAdjust *= Math.Max(texture.DisplayWidth / maxSize.X, texture.DisplayHeight / maxSize.Y); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're going to have to be very conscious about this, to ensure we don't have any local usages overriding ScaleAdjust
themselves.
I've checked all usages in the project right now and it seems okay. For now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a second thought, an alternative way to achieve this could be to have something like a "maximum texture size" property in SkinnableSprite
, and if the sprite exceeds that property then the scale of it is adjusted accordingly.
That might be better as to avoid tinkering around with low properties like Texture.ScaleAdjust
. Also saves from having to add extra properties to GetTexture
/GetAnimation
. @peppy thoughts? would it be worth the effort?
I know for a fact that some of these limitations are going to cause skins to break, and will need to be relaxed. For instance, there are skins which add glows to hitobjects, which we previously allowed. In fact, I'm pretty sure there was a previous iteration in lazer where we were limiting things and I fixed such a case. The only way to figure out sane limits will be to push out these changes and wait for users to report breakage, then decide how much wiggle-room we give skins for exceeding the "correct" dimensions. |
@@ -47,13 +48,13 @@ private void load(ISkinSource skin, OsuColour colours) | |||
Anchor = Anchor.CentreRight, | |||
Origin = Anchor.CentreLeft, | |||
RelativeSizeAxes = Axes.Both, | |||
Texture = skin.GetTexture("taiko-roll-end", WrapMode.ClampToEdge, WrapMode.ClampToEdge), | |||
Texture = skin.GetTexture("taiko-roll-end", WrapMode.ClampToEdge, WrapMode.ClampToEdge)?.WithMaximumSize(new Vector2(128, 256)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these 256 based, not 128?
Also adjusting this doesn't affect the test scene at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uhh...9d17539
And yes, adjusting doesn't affect because the sprite is resized regardless of the texture, see #24706 (comment).
using osuTK; | ||
|
||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy | ||
{ | ||
public partial class LegacyDropletPiece : LegacyCatchHitObjectPiece | ||
{ | ||
private static readonly Vector2 droplet_max_size = new Vector2(100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does 100 come from? The template skin is 82 x 103 (@1x
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not to say it's correct, but I think I picked 100 as it's closest to 103...I'll change to 82x103
Already handled by the sprites themselves being resized.
Will reiterate that this will 100% cause some kind of user skin regressions, which we'll deal with based on feedback.
|
Turns out hot reload was not working, and my scale change in the test scene was not applying correctly in all cases. As a result, the test scene is quite broken. But in addition, I missed quite a few elements which weren't correctly limited by this PR. Things like follow points (which can be used to reveal object positions in flashlight) need special consideration. @frenzibyte did you want to look into these cases again? |
RFC. This applies to osu! hit circles, taiko hits (drum rolls are already limited), and catch hit objects (fruits, bananas, and droplets). Mania is not included as I'm not even sure how to go about it (if I should).
From the get go, I was planning on re-using
SkinnableDrawable
's "size confinement mode" logic, but it's not quite usable for the cases in here for two reasons:SkinnableDrawable
because they're already inside a per-skin component and they fetch the texture directly from the skin.SkinnableDrawable
's size confinement mode doesn't handle texture elements smaller than the standard size (there's a bit of an argument over that one).So I came up with the idea of a simple
Drawable
class that wraps around a sprite and downscales it if it exceeds the specified maximum dimensions or the draw size of a specified "target" drawable. However, that resulted in few issues with osu! hit circles, as they apply upscaling to the circle & overlay pieces on hit, which cannot work properly with the class in mind (see discord).Therefore, I switched to applying the maximum dimensions limit at a texture level instead, by a helper method that tinkers with
Texture.ScaleAdjust
to downscale the texture if it exceeds maximum dimensions.Opening this pull request as draft because it is mostly for feedback, and because I haven't thoroughly tested it or compared it with #23025 yet.