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

How should Gum handle circular dependencies between size and texture addresses? #396

Closed
vchelaru opened this issue Dec 23, 2024 · 3 comments · Fixed by #447
Closed

How should Gum handle circular dependencies between size and texture addresses? #396

vchelaru opened this issue Dec 23, 2024 · 3 comments · Fixed by #447

Comments

@vchelaru
Copy link
Owner

Summary

It is possible to have a Sprite where the width of the sprite depends on the texture coordinates, but the texture coordinates depend on width. How should Gum solve this problem. For brevity this topic only mentions width, although the same concepts apply to height.

Details

When working with sprites, the two most common setups are to have the width depend on texture coordinate (1) and to have the texture coordinate depend on the width (2).

1. Width Depends on Texture Coordinates

This is the most common setup for Sprites. This setup has:

  • Width = 100
  • Width Units = Percentage of File Width (this says file width but it really means texture coords)
  • Texture Address = Entire Texture or Custom

With this setup, the calculation for width is:

AbsoluteWidth = Width * Texture Width

Texture Width is a hard value that you can specify in the editor.

So if a sprite is using a source file that is 256 pixels wide, and its Width is 100, then the absolute width of the sprite is 256:

image

If you change the texture coordinates, then the absolute width of the sprite changes too

23_06 07 02

This is the most common setup because it lets users select regions from a sprite sheet (or animations in an AnimationChain) and the sprite always adjusts appropriately.

2. Texture Coordinates Depend on Width

This setup is not very common, but it can be used for things like health bars, effects where sprites are gradually revealed, and filling progress bars.

This setup has:

  • Width = Any value
  • Width Units = Absolute (or any value besides Percentage of File Width)
  • Texture Address = DimensionsBased

With this setup, the calculation for width (assuming Absolute Width Units) is:

AbsoluteWidth = Width

But in this case Texture Width is calculated like this:

TextureWidth = AbsoluteWidth

If you change the absolute width of the sprite (by changing its Width value), then the TextureWidth value adjusts in response.

23_06 30 18

Absolute Width and Texture Width depend on each other

It is possible to mix the two situations above by setting these values:

  • Width Units = Percentage of File Width (AbsoluteWidth depends on TextureWidth)
  • Texture Address = DimensionsBased (TextureWidth depends on AbsoluteWidth)

This creates a circular dependency which currently Gum resolves somewhat arbitrarily. In fact, I'm actually not 100% sure exactly how it is resolved and I don't know if it's worth researching the code to figure this out. All I know is that it "works" in that it doesn't cause infinite recursion, and the Sprites do not completely blow up or shrink to 0 width. It is at least stable.

When this happens, what should Gum do? Here are some options I've come up with to get the conversation started:

  1. Gum should continue to size the Sprite in a stable-yet-arbitrary way, and do nothing else. This is the current behavior and it isn't great because it requires the user to understand these relationships to identify the problem.
  2. Gum should continue to size the sprite in a stable-yet-arbitrary way, but it should show an error in the Error window. This could be paired with an error icon next to the Sprite and an error icon on the Output window tab (I have tasks to do this for other features). This would at least tell the user what's going on and help guide them to solve the issue.
  3. Gum should size the sprite not arbitrarily, but some fixed size. I suppose this could be paired with showing errors so this isn't a mutually exclusive option.
  4. Gum should prevent the selection of circular dependencies. Although this may seem like a good idea at the surface, it has some problems:
    1. if the user wants to switch from one set of values to another, the fact that they cannot get in a temporarily invalid state makes it hard to switch. They would have to switch to options that they don't really want to use just to re-enable the UI so they can eventually get to the state that they want.
    2. The user can always edit the XML file by hand to get around any limitations of the UI. Therefore even if we restrict these options in the UI, we have to still resolve them somehow since these can happen through manual edits, merges, or even other tools in the future.

Please let me know your thoughts.

@kaltinril
Copy link
Collaborator

I agree with profexorgeek (Item 1 below)

(These numbers are not aligned with your numbers)

  1. First, detect the situation and display it to the user (I like the icon change and error tab, you could also do what you do for the states and add a yellow/red/whatever bar across the variables that says "Error, depends on causing a circular reference. Change one to prevent this or undo last action."

The rest of these are me tossing out thoughts:
2. alternatively, you could do a popup explaining that they just caused a circular reference, and that they need to fix it.
3. In game, you would need to "STOP" attempting to adjust the positions once a circular reference is detected.
4. It would be great if you could prevent the action, or even give a warning "WARNING, this setting change will cause a circular reference that you need to resolve.

@vchelaru
Copy link
Owner Author

vchelaru commented Dec 30, 2024

After thinking through this, I have a solution that I really like. When Gum sees this circular reference, it could completely ignore Texture Width and instead base the width of the Sprite on the dimensions of the source PNG file (ignoring the Texture Width value). This has the following benefits:

  1. It is completely predictable - if the file is 200 pixels wide, Width Units = Percentage of File Width, and Width=100, then the Sprite will be 200 absolute units wide (200 * 100%)
  2. It allows the user to easily control the number of times that a Sprite loops. Normally you'd have to know the size of the source file and then multiply that by the number of times you want to loop the texture. By enabling this feature, if you set a width of 500 Percent of File Width, then the texture will loop exactly 5 times. I could see this being useful for users.

Of course, this would be considered a breaking change, and will be mentioned as such in the next release, but I think it's worth doing since it was previously an undefined behavior caused by circular dependencies.

Also it's worth noting that Gum does allow circular dependencies in lots of other cases, such as parent/child relationships. These types of relationships are encouraged in many cases and well defined, so this new proposal is not a philosophical break from how Gum normally works.

@vchelaru
Copy link
Owner Author

It turns out that the way I had it described is how I had already implemented it - I just didn't remember. At least now it's also documented:

https://docs.flatredball.com/gum/readme/gum-elements/sprite/texture-address#dimensionsbased

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants