Godot:如何从可能的场景列表中实例化一个场景
Godot : How to instantiate a scene from a list of possible scenes
我正在尝试创建一个游戏,其中的程序生成将类似于以撒的结合:连续的房间 select 从列表中编辑。我想我可以 link 他们在一起,但我有一个问题:我如何从列表中选择一个房间?
我的第一个想法是创建包含场景的文件夹,比如
- zone_1
- basic_rooms
- room_1.tscn
- room_2.tscn
- special_rooms
- ...
- zone_2
- ...
然后 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()
这会给你一个从 0
到 1
的值。其中 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)。
- 累积归一化权重。
- 生成一个从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]
一旦你选择了要生成的房间,你就可以加载它(例如使用后台加载方法),实例化它,并将它添加到场景树中。记得给个世界的位置。
还要记得更新网格(数组)信息。你从一组有特定门的房间中挑选了一个房间。您想存储它以考虑生成相邻的房间。
顺便说一句,如果你需要大规模寻路(从一个房间到另一个房间),你也可以使用那个网格。
我正在尝试创建一个游戏,其中的程序生成将类似于以撒的结合:连续的房间 select 从列表中编辑。我想我可以 link 他们在一起,但我有一个问题:我如何从列表中选择一个房间? 我的第一个想法是创建包含场景的文件夹,比如
- zone_1
- basic_rooms
- room_1.tscn
- room_2.tscn
- special_rooms
- ...
- basic_rooms
- zone_2
- ...
然后 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()
这会给你一个从 0
到 1
的值。其中 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)。
- 累积归一化权重。
- 生成一个从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]
一旦你选择了要生成的房间,你就可以加载它(例如使用后台加载方法),实例化它,并将它添加到场景树中。记得给个世界的位置。
还要记得更新网格(数组)信息。你从一组有特定门的房间中挑选了一个房间。您想存储它以考虑生成相邻的房间。
顺便说一句,如果你需要大规模寻路(从一个房间到另一个房间),你也可以使用那个网格。