Godot:如何从可能的场景列表中实例化一个场景

Godot : How to instantiate a scene from a list of possible scenes

我正在尝试创建一个游戏,其中的程序生成将类似于以撒的结合:连续的房间 select 从列表中编辑。我想我可以 link 他们在一起,但我有一个问题:我如何从列表中选择一个房间? 我的第一个想法是创建包含场景的文件夹,比如

然后 select 我需要的文件夹中的随机场景,例如第一个区域中的随机基本房间将是 zone_1/basic_rooms 中的随机场景。

问题是我不知道这是否是一个好的解决方案,因为它会创建很多场景,而且我不知道如何正确地执行此操作。我是简单地使用包含文件夹路径的字符串,还是有更好的方法?然后我想我得到文件夹中的所有文件,随机选择一个,加载它并实例化它,但同样,我不确定。

我觉得我的解释有些迷茫,但总而言之,我正在寻找一种从列表中select房间布局的方法,但不知道该怎么做。

你的建议会奏效。

您可以通过以下模式实例化场景:

var room_scene = load("res://zone/room_type/room_1.tscn")
var room_instance = room_scene.instance()
parent.add_child(room_instance)

我也会提醒你给room_instance一个位置。

因此,正如您所说,您可以构建传递给 load 的字符串。

我建议将帽子逻辑放在自动加载中,并在需要的地方调用它。


但是,上面的代码会在加载场景时停止游戏。而是使用 ResourceLoader.

进行后台加载

首先你需要调用 load_interactive 这会给你一个 ResourceInteractiveLoader 对象:

loader = ResourceLoader.load_interactive(path)

然后你需要在加载器上调用poll。直到 returns ERR_FILE_EOF。在这种情况下,您可以使用 get_resource:

获取场景
if loader.poll() == ERR_FILE_EOF:
    scene = loader.get_resource()

否则,这意味着调用 poll 不足以完成加载。

想法是将对 poll 的调用分散到多个帧中(例如,通过从 _process 调用它)。

您可以调用get_stage_count来获取您需要调用poll的次数,get_stage会告诉您到目前为止调用了多少次。

因此,您可以使用它们来计算进度:

var progress = float(loader.get_stage()) / loader.get_stage_count()

这会给你一个从 01 的值。其中 0 根本没有加载,而 1 已完成。 乘以 100 得到要显示的百分比。您也可以将其用作进度条。


The problem is that I have no idea if this a good solution as it will create lots of scenes

这不是问题。

Do I simply use a string containing the folder path

是的。

Then I suppose I get all the files in the folder, choose one randomly

不一定。

您可以确保文件夹中的所有场景都具有相同的名称,除了一个编号,那么您只需要知道文件夹中有多少个场景,然后选择一个编号即可。

但是,您可能不希望完全随机。根据您生成房间的方法,您可能需要:

  • 根据人脉选择房间。确保它连接到相邻的房间。
  • 权衡一个房间应该有多普遍或多罕见。

因此,拥有包含该信息的文件(例如 json 或 csv 文件)会很有用。然后,负责加载场景的自动加载代码会将该文件加载到数据结构(例如字典或数组)中,从中可以选择要加载的场景,并考虑其中指定的任何权重或约束。


我假设您的房间存在于网格中,并且可以有北门、南门、东门、西门。我也会假设玩家可以回溯,所以布局必须是持久的。

我不知道你会产生多远。您可以选择一次生成所有地图,或者在玩家尝试进入时生成房间,或者在前面生成几个房间。

如果您打算在玩家尝试进入时生成,您将需要一个房间过渡动画,您可以在其中隐藏场景加载(使用背景加载方法)。

但是,您不应生成已经生成的房间。因此,保留一个文字网格(数组),如果已生成房间,您可以在其中存储。您将首先检查网格(数组),如果已生成,则无需执行任何操作。但如果没有,那么你需要随机选择一个房间。

但是等等!例如,如果您从南面进入,您选择的房间必须有南门才能返回。如果您按房间的门来组织房间,那么您可以从有南门的房间中挑选 - 在本例中。

事实上,您需要考虑您已经生成的所有相邻房间的门。因此,在网格(数组)中存储生成的房间有哪些门。因此您稍后可以从数组中读取以查看新房间需要哪些门。如果没有房间,请随意决定是否要在那里开门。然后从有这些门的集合中随机选择一个房间。

您的房间组合是 NORTH、SOUTH、EAST、WEST 的组合。生成列表的一种方法是给每个方向一个 2 的幂。例如:

  • 北方=1
  • 南 = 2
  • 东=4
  • 西 = 8

然后算出集合,可以数数,二进制表示就给出了门。例如 10 = 8 + 2 -> 西和南。

这些是你的套间。重申一下,查看已经生成的邻居,了解进入您要生成的房间的门。如果没有房间,请随意决定是否要在那里开门。这应该会告诉您需要从哪一组房间中选择生成。

这类似于自动平铺解决方案的使用方法。您可能想了解它是如何工作的。


现在假设集合中的房间有权重(所以有些房间更常见,有些更稀有),你需要随机选择。

这是通用算法:

  1. 对权重求和。
  2. 归一化权重(将权重除以总和,因此它们加起来为 1)。
  3. 累积归一化权重。
  4. 生成一个从0到1的随机数,最后累积的大于我们得到的随机数的归一化权重是多少

因为您可能会多次从同一组中挑选房间,您可以计算并存储累积的归一化权重(我们称它们为最终权重),因此您不必每次都计算它们。

您可以这样计算它们:

    var total_weight:float = 0.0
    for option in options:
        total_weight = total_weight + option.weight

    var final_weight:float = 0.0
    var final_weights:Array = []
    for option in options:
        var normalized_weight = option.weight / total_weight
        final_weight = final_weight + normalized_weight
        final_weights.append(final_weight)

那你可以这样选:

    var randomic:float = randf()
    for index in final_weights.size():
        if final_weights[index] > randomic:
            return options[index]

一旦你选择了要生成的房间,你就可以加载它(例如使用后台加载方法),实例化它,并将它添加到场景树中。记得给个世界的位置。

还要记得更新网格(数组)信息。你从一组有特定门的房间中挑选了一个房间。您想存储它以考虑生成相邻的房间。

顺便说一句,如果你需要大规模寻路(从一个房间到另一个房间),你也可以使用那个网格。