Godot 有 HVBoxContainer 吗?

Is there a `HVBoxContainer` for Godot?

想一想您可能需要显示按钮列表的角色扮演游戏。用户可能会进入一个房间,在那里他们必须通过一系列选项(按钮)select。是否有一种 container/panel 可以水平显示可点击按钮,但需要时换行?

我能想到的最好的比喻是,想象一下需要点击背包中的物品,但每个物品的宽度可能不同。 (我可以让它们高度相同,但宽度会有所不同)

.---[My Backpack]------.
| aaa  bbb  cccc ddd   |
| ee  fff  g           |
|                      |
|                      |
`----------------------'

(选项来自数据库,因此在编译时不知道房间中可能有多少个选项,因此我需要以编程方式添加选项。)

this godot document 的最底部介绍了自定义容器布局,但我不清楚这在实践中如何运作

Godot 3.5 或更新版本的流容器

Godot 3.5(目前处于测试阶段)引入了 HFlowContainerVFlowContainer,它们将为所描述的目的服务。

HFlowContainer 将填满一行,当它们溢出时,它将添加一个新行并在那里继续。 VFlowContainer 将以类似的方式工作,但有列。


Godot 3.5 之前的流容器

对于旧版本的 Godot,您可以使用 HFlowContainer 插件,您可以在资产库 (here) 中找到它。 请注意,没有 VFlowContainer 对应项。

由于资产库中的所有内容都是免费和开源的,因此请随意阅读和修改代码,如果您想进行自定义 Container,可以作为起点。


制作你自己的定制Container

制作自定义 Container 的要点是它必须定位其子项。

为了达到这种效果,您需要在 _notification 方法中对 NOTIFICATION_SORT_CHILDREN 做出反应。 您可能还想对 NOTIFICATION_RESIZED 做出反应。

你可以有一个方法 - 我称之为 layout - 当你收到通知时调用它:

func _notification(what):
    if what == NOTIFICATION_SORT_CHILDREN:
        layout()

并且还从定义 Container 必须如何组织其子项的属性的设置器 (setget) 调用 layout要从 _notification 以外的任何地方调用 layout,您可能需要使用 call_deferred("layout") 来防止任何可能的 re-layout 循环挂起或使游戏崩溃。


layout 方法将遍历可见子项 Control 并使用 get_combined_minimum_size 计算出它们的大小。

像这样:

func layout() -> void:
    # …
    for child in get_children():
        var control := child as Control
        if not is_instance_valid(control) or not control.visible:
            continue

        var size := control.get_combined_minimum_size()
    # …

然后使用该信息计算子 Control 的位置和大小。当 Control 有增长空间时,您可能希望根据他们的 size_flags_stretch_ratio.

将其分配给他们

一旦你确定了 Control 的位置和大小,使用 fit_child_in_rect 来定位它们,这将考虑增长和大小标志。

因此 - 除了最简单的 Containers - 你将需要迭代子 Controls 两次。为此,您可能会发现使用辅助数据结构来临时存储它们很有用。