【转载】锚点、边距和容器,我的Godot!
多年来,我一直喜欢使用Godot,但最大的痛点之一是掌握UI节点的定位。我在白天的工作中是一名Web开发人员,因此我经常使用CSS。与此相比,Godot的UI并不直观,除了全屏定位之外,任何更复杂的操作都可能导致节点以我意料之外的方式放置,我感觉自己花了数小时来拼凑一个界面。我搜寻了所有能找到的文档,但最终似乎没有什么太大的帮助。
然而,最近我在理解Godot的UI定位系统方面取得了一些巨大的突破。结果是,我现在理解Godot的UI实际上非常简单易用,几乎可以说是非常精妙。突然间,我能够轻松地组合复杂的UI场景,并理解为什么我的UI看起来是这个样子。考虑到多年来我所感受到的挫折感,这些领悟几乎让我觉得是奇迹般的。
锚点
让我们从UI定位的基础元素之一开始:锚点。
锚点控制节点及其内容在屏幕上绘制的边界。单位是一个从0到1的数字。这个数字的参考是什么?它是父节点的大小,从枢轴开始(枢轴是节点在UI坐标空间中“定位”的二维坐标),沿水平和垂直轴延伸到父节点的完整大小。0表示您在轴的起点(左/右的水平轴,上/下的垂直轴),1表示您在轴的终点。
仅用文字解释有些棘手,因此让我们看一些图像来说明这个定位系统如何工作。

上面的图像是一个简单的Control节点,它是另一个填充整个视口(Viewport)的节点的子节点。请注意,所有锚点值都设置为0。相应地,节点的大小也被归零,因此您无法看到该节点内容的任何部分。
如果您使用Godot的编辑器界面创建Control节点,尽管锚点也被设置为0,但它看起来并不像这个样子。我将在本文后面解释原因。
让我们看看将右侧和底部值设置为1(或父节点的大小水平和垂直方向上的100%)时会发生什么。

突然间,我们的节点在可用的视口空间中完全伸展开来。通过将右侧设置为1,我们告诉Godot将节点的右边缘扩展到父节点的边界矩形上,跨越水平轴。将底部设置为1时,情况类似,但是在边界矩形的垂直轴上。
为了好玩,让我们将左侧和顶部值更改为0.5并看看会发生什么。

现在,我们的Control节点看起来像是在视口的右下角。基本上,我们告诉Godot将节点的左侧和顶部边缘从父节点的枢轴原点移动了50%。
我没有在截图中显示这个过程,但我在Control中放置了一个ColorRect,以使它更明显地占用了多少空间。它基本上只填充我们的Control节点所在的空间,没有任何实际功能。
锚点只是Godot UI定位方程式的一部分。该方程式的另一个关键部分是边距。
边距
边距控制从指定边缘使用的间距量。锚点使用百分比单位进行定位,而边距则使用像素单位。
让我们看另一个Control节点的示例,其中右侧和底部的锚点值设置为1。

当前,视口完全填充。让我们尝试在Control周围添加一些边距,以使其周围有16像素的空间。

等等,看起来不对。在Control的顶部和左侧有16像素的空间,正如我们所预期的那样,但底部和右侧被推到了视口的可见区域之外。
这是为什么?很简单:Godot不将边距视为边界矩形的距离。相反,Margin沿着一个方向应用;正边距在指定边缘的右侧/底部,而负边距在该边缘的左侧/顶部。
这与CSS中边距的工作方式不同,这也是我长期以来误解Godot UI的一个重要原因。
为了获得我们想要的间距效果,我们需要将底部和右侧边距应用为负值。

这就更好了。
不过,只是为了好玩,如果我们想要让Control的宽度超出包含节点的边界矩形怎么办?很简单,我们只需要使左侧和顶部边距为负值,右侧和底部边距为正值。

早些时候,我忽略了一个事实,即在Godot的编辑器中创建一个新的Control节点实际上并没有创建一个没有大小的节点,尽管锚点设置为0。这是因为Godot默认将新的Control的右侧和底部边距设置为40像素,这导致它们的默认rect_size为Vector2(40,40)。我不知道官方文档解释为什么这样做,但我的猜测是,这是为了尽量减少新控件没有大小的混淆。

等等,什么是rect_size?它与边距值有什么关系?好问题!
锚点和边距如何影响其他控件属性
虽然锚点和边距是确定节点位置和大小的核心方面,但它们与其他节点属性协调工作。更改节点的锚点和边距通常会自动调整这些相邻属性,反之亦然。
让我们看看这些属性是什么。
RECT SIZE
rect_size属性是控件节点的实际大小。它可以直接设置,但通常会根据锚点和边距的设置进行重新调整。需要记住的重要事情是,这个值始终代表节点在游戏中的实际大小。
RECT POSITION
rect_position是控件在游戏UI空间中的“所在”点(也就是它的原点)。与rect_size一样,这可以手动设置,并且还可以根据与锚点和边距的交互自动调整。

RECT MIN SIZE
rect_min_size属性强制Godot永远不会将这个特定节点缩小到指定的大小以下。与rect_size和rect_position不同,这从不由Godot自动调整。当您绝对需要控制不缩小到某个大小时很有用,但要小心:滥用这个属性来绕过错误设置的UI属性很容易。(我肯定曾经这样使用过它!)
布局预设
到这一步,如果您之前使用过Godot的UI,您可能已经意识到了一些东西:“这感觉非常类似于我使用布局按钮时发生的事情!”
那是因为这些布局只是锚点和边距值的常见预设。例如,“全屏矩形”与将顶部和左侧锚点设置为0,将右侧和底部锚点设置为1,将所有边距设置为0完全相同。同时,“居中”预设将所有锚点设置为0.5(即50%),然后自动计算边距值,使它们等于节点的最小大小的一半,从而得到一个居中的节点。

这是与前一个图像相同的100×60矩形,但应用了中心预设。注意不仅锚点和边距值的差异,还有rect_position的差异。
这些预设非常常见,以至于Godot的开发人员决定提供一种方便的方法来设置它们,但是如果您不了解底层系统的工作原理,使用它们可能会很困惑。我肯定曾经对为什么之前“居中”的控件在我做了一些改变节点大小的事情后没有自动更新而感到困惑。原因是预设不会自动响应更改而自动更新;它们只是在使用它们的时候对您在此时构建的任何内容进行操作。因此,如果我更改了影响节点大小的内容,我需要重新应用“居中”预设来使节点再次看起来居中。

子控件节点
如果您更改子节点的锚点和边距,会发生什么?与更改其父节点的值完全相同!到目前为止,我使用的所有示例都将节点作为子节点添加到与视口大小匹配的根节点中,但视口本身与节点的大小没有任何关系。如果您有一个大小控件,并调整其子项的锚点和边距值,则它们将适合该父控件的空间内。


...而它的子节点的锚点和边距相对于其自身的边界矩形。
对于Godot中的每个基于Control的节点,拥有一个功能相同的UI系统是非常强大和可预测的。
嗯,几乎所有的节点...
容器节点是例外
在Godot中有一类节点称为容器。容器节点本身可以使用锚点和边距进行大小调整,就像任何其他节点一样。然而,容器节点的子节点会自动按照该容器的内部逻辑进行调整大小和定位,忽略(并覆盖)任何手动设置的大小和位置值。

您可以通过编辑器在控件节点中定位和大小调整容器节点...

...然而,如果您尝试更改容器节点内部节点的锚点、边距或rect_size值,这些值将被容器节点自动调整。
有多种类型的容器节点,每种容器节点都有自己的内部逻辑来处理子节点的大小和位置。举几个例子:
- HBoxContainer使其子项在水平方向上对齐。
- VBoxContainer使其子项垂直对齐。
- GridContainer使其子项在一组列中对齐。
- CenterContainer将所有直接子项居中到其自身的中心。
Godot的文档确实强调了这种行为,并且在容器节点内部工作时禁用了布局预设(后者的行为是为了防止开发人员在根本不起作用的地方使用它们)。如果您之前不了解UI系统的整体工作方式,就像我一样,那么这些解释和行为可能会感觉更像是描述而不是阐明。
大小标志
通过大小标志,您仍然可以在容器内部控制节点的定位和大小。大小标志有四种类型的行为,适用于水平和垂直轴(每个轴的所有标志都可以单独打开或关闭)。
fill使节点填充给定的空间。该空间由父容器的大小和节点自身计算出的大小控制。
expand使节点填充父容器中尚未被其他节点使用的任何空间。如果相邻节点没有设置为扩展,它们将被设置为扩展的节点推开。如果相邻节点都扩展,它们将在它们之间分配空间。
shrink center使节点仅占用其最小可能的大小,同时相对于其邻居和父容器居中。
shrink end与shrink center相同,但位置位于可用空间的末端而不是中心。
不设置上述任何标志会使节点表现得好像存在一个shrink begin属性。

在这里,第一个和最后一个子节点只设置了fill标志。第二个和第三个子节点在水平方向上都会扩展,而第三个子节点也会在垂直方向上缩小到中心。(由于该第三个子节点没有固定大小的子节点,需要设置最小高度为10px)
定位节点在容器中的重要事项是不要担心将节点放置在特定的位置或大小,而是根据您想要实现的任何比例将它们对齐。这种方法的优点是,您可以将容器节点放置在UI中的任何位置,它们将自动处理将其子节点放置和调整大小以匹配相同的比例。
为什么我的控件节点不在容器中运行?
您是否曾经尝试将Control节点放置在Container节点中,但它的行为像这样?

您可能需要眯起眼睛才能看到不合适的白色文本。
乍一看,似乎容器没有管理控件的大小,但实际并非如此。事实上,默认情况下,控件节点不会根据其自身子节点的大小调整自身大小。(换句话说,控件节点不是容器!)实际上,控件节点由容器调整大小,但由于控件没有扩展到其子节点的大小,因此它自己的大小被设置为0。
有两种方法可以获得更符合预期的行为。一种方法是在控件节点上放置rect_min_size,以便容器可以进行调整大小。

另一种方法是使用控件节点的大小标志。

哪种方法应该使用?这将取决于您要实现的效果。如果您只需要节点占用空间,则rect_min_size应该可以解决问题。对于更动态的大小调整,更改大小标志效果最佳。
结论
这就是Godot的UI大小调整和定位系统的工作方式:锚点、边距和容器。现在我理解了这一点,我制作UI的时间要容易得多,它可以被放置在我想要的位置。这个系统很简单,但在我理解它如何工作之前,它感觉很混乱和不直观。
希望这篇文章也能帮助您更好地理解Godot的UI!

我在示例中使用了这个调色板来为ColorRect节点进行可视化。它也可以作为一个漂亮的展示,展示了你可以用Godot的UI实现什么!
【原文地址】 Anchors and Margins and Containers, Godot My! – Josh Anthony
浙公网安备 33010602011771号