godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.16k stars 97 forks source link

Replace margins and anchors with an alternative system #1296

Closed JotaFaD closed 10 months ago

JotaFaD commented 4 years ago

Describe the project you are working on: Multiplayer tabletop game

Describe the problem or limitation you are having in your project: While I find the container workflow in Godot very powerful and intuitive, I can't say the same thing about the anchors and margins workflow. The problem I had with my project that motivated me to thing an alternative system was the following: During a match in my game, there's a ItemList of all the players connected to the game on the top right corner of the screen. This list's width should vary depending on the length of the player name, such that it consumes the least amount of screen space, but does not clip the player name.

Setting the anchor of the list to the top right corner does move it to the correct place, but if you try increasing it's size in the editor or by code (to accommodate for larger player names), you'll see that it starts to grow off the screen. You could solve this by decreasing it's left margin instead, which works AND modifies the size of the control, but the conversely does not happen. I find it rather unintuitive that to increase the size of the control I should modify its margin, rather than its size.

Describe the feature / enhancement and how it helps to overcome the problem or limitation: This proposal replaces anchors and margins (8 properties) by two Vector2s (4 properties), which let's you specify how controls should position and grow in relation to their parents or the viewport. After I explain how the system works, I'll show how this easily solves the particular problem I had.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams: I created an example project if you want to follow along: GUI.zip

This project defines a class called AControl (It uses a Node2D as the base class just so I didn't have other properties that already exist in Control interfering in what I'm trying to demonstrate). AControl has four export variables, size (which already exists in Control), color (just to help us locate where each control is located), parent_anchor and self_anchor. These last two properties are the ones we're interested in.

parent_anchor is a Vector2 whose components vary between 0 and 1, and define where a control should attach itself in relation to its parent (another AControl or the Viewport). Vector2(0, 0) represents the parent's top-left corner, while Vector(1, 1) represents its bottom-right corner. By setting the parent_anchor, we can attach an AControl anywhere in relation to its parent, and since the values are normalized, resizing the window still keeps the AControls at the same relative position. parent_anchor does not solve all the problems though. Yes, we can attach a node anywhere, but it is always attached by its top-left corner. This is solved using self_anchor.

self_anchor, just like parent_anchor, is a Vector2 with components between 0 and 1, but it defines how an AControl should attach itself to its parent_anchor position. Vector2(0, 0) represents the top-left corner of the AControl, while Vector(1, 1) represents the bottom-right corner. Using self_anchor we can now attach the AControl not just anywhere, but any way we want. The most important part, though, is that, since the values are normalized, if we increase the size of the AControl, it will reposition itself in 2D space to keep its self_anchor constraint.

I'll now demonstrate some examples, you can also test them in the attached project:

Let's start by my use case, I want to attach a the top-right corner of an ItemList (self_anchor = Vector2(1, 0)) to the top-right corner of the screen (parent_anchor = Vector2(1, 0)).

image

If we increase the list's width, it should reposition itself to keep the anchor in place:

image

Let's now attach another AControl's top-middle position (self_anchor = Vector2(0.5, 0)), to the bottom-middle position of the list (parent_anchor = Vector2(0.5, 1)), this is where I use a HBoxContainer to put buttons that modify the list.

image

Again, changing the size of the list repositions the elements to keep the relative anchor positions.

image

This is more of a crazy example, but shows the possibilities:

image

If this enhancement will not be used often, can it be worked around with a few lines of script?: It would be used often, since UI is present in every game. It also does not exclude the container workflow, but gives more control of where each container could be in relation to each other.

Is there a reason why this should be core and not an add-on in the asset library?: This modifies important properties of the Control node, so I don't thing this could be an addon.

I Understand this would be a large change with consequences to existing games, but since godot has a major version in the horizon I didn't want to let this idea die without sharing it. Thanks for reading up to this point.

KoBeWi commented 10 months ago

Anchors and margins were reworked in Godot 4, so this is resovled.

Setting the anchor of the list to the top right corner does move it to the correct place, but if you try increasing it's size in the editor or by code (to accommodate for larger player names), you'll see that it starts to grow off the screen.

That's what grow direction is for. When you change minimum size of a Control, it will grow according to the grow direction. This property is set automatically in the new system based on anchors, so it should work as expected.