game-development - 玩家无法在程序生成的地图中正确生成
问题描述
我关注了 KidsCanCode @https://www.youtube.com/watch?v=o3fwlk1NI-w 的“Godot 中的程序生成:地牢生成”,发现自己无法调试当前问题。
这个特定的提交有代码,但我会在下面尝试更详细地解释。
我的主要场景有一个 Camera2D 节点,一个通用的 Node2D 调用 Rooms 和一个 TileMap,一切都是空的。
当脚本启动时,它运行一个
func make_room(_pos, _size):
position = _pos
size = _size
var s = RectangleShape2D.new()
s.custom_solver_bias = 0.5
s.extents = size
$CollisionShape2D.shape = s
几次,它$Rooms
使用.add_child(r)
wherer
是具有该make_room()
功能的节点的一个实例来填充。然后它将迭代$Rooms.get_children()
几次以创建一个AStar
节点来链接所有房间:
之后被调用时,魔法就来make_map()
了,它用不可行走的块填充地图,然后雕刻空白空间,这也很好用:
有一个find_start_room()
被调用来找到初始房间,它还为主脚本设置了一个全局变量start_room
,用于在地图上写“开始”使用draw_string(font, start_room.position - Vector2(125,0),"start",Color(3,4,8))
当我点击“esc”时,它运行这个简单的代码来实例化播放器:
player = Player.instance()
add_child(player)
player.position = start_room.position + Vector2(start_room.size.x/2, start_room.size.y/2)
play_mode = true
产生播放器时出现问题。我尝试进行一些“盲目”修复,例如添加或减去Vector2(start_room.size.x/2, start_room.size.y/2)
player.position 以查看是否可以使其落在房间内,但无济于事。
转向调试器并没有帮助,因为变量检查器表达的位置似乎没有任何意义。
我尝试实现一个简单的“鼠标点击打印位置”:
print("Mouse Click/Unclick at: ", event.position)
print("Node thing",get_node("/root/Main/TileMap").world_to_map(event.position))
还有一个“start_room”打印位置:
print(get_node("/root/Main/TileMap").world_to_map(start_room.position))
还有一个当玩家移动打印位置时,直接写入角色脚本:
print(get_node("/root/Main/TileMap").world_to_map(self.position))
得到如下结果:
Mouse Click/Unclick at: (518, 293)
Node thing(16, 9)
(-142, 0)
(-147, -3)
因此,玩家不会在与 start_room 相同的位置生成,并且鼠标位置信息与其他任何信息都不相同。
为什么玩家现在正确生成?我该如何调试这种情况?
EDIT1:用户 Theraot 提到了 RigidBody2D 如何进行一些奇怪的碰撞,据我所知,改变它们的碰撞行为应该可以解决整个问题。
代码中有一段 - 在生成随机房间后 - 它会删除一些这样的房间:
for room in $Rooms.get_children():
if randf() < cull:
room.queue_free()
else:
room.mode = RigidBody2D.MODE_STATIC
room_positions.append(Vector3(room.position.x, room.position.y, 0))
据我了解,如果房间是随机选择的,它将使用queue_free()
OR 删除,它将被附加到 aroom_positions
以进行进一步处理。这意味着如果我将所有房间转移到不同的碰撞层,玩家/角色实例将与同一碰撞层上的 TileMap 单独存在。
所以我只是添加了一个简单room.collision_layer = 3
的将这部分代码更改为
for room in $Rooms.get_children():
if randf() < cull:
room.queue_free()
else:
room.mode = RigidBody2D.MODE_STATIC
room.collision_layer = 3
room_positions.append(Vector3(room.position.x, room.position.y, 0))
它似乎没有改变任何东西,玩家仍然在房间外产生。
解决方案
Do you see the rooms spread outwards?
You didn't write code to move the rooms. Sure, the code gives them a random position. But even if you set their position to Vector2.ZERO
they move outwards, avoiding overlaps.
Why? Because these rooms are RigidBody2D
, and they do push other physics objects. Such as other rooms or the player character.
That's the problem: These rooms are RigidBody2D
, and you put your KinematicBody2D
player character on top of one of them. The RigidBody2D
pushes it out.
The tutorial you followed is exploiting this behavior of RigidBody2D
s to spread the rooms. However you don't need these RigidBody2D
after you are done populating your TileMap
.
Instead, you can store the start position in a variable for later placing the player character (you don't need offsets - by the way - the position of the room is the center of the room), and then remove the RigidBody2D
s. If you want to keep the code that writes the text, you would also have to modify it, so it does not fail when the room no longer exists.
Alternatively, you can edit their collision layer and mask so they don't collide with the player character (or anything for that matter, but why would you want these RigidBody2D
s that collide with nothing?).
Addendum post edit: Collision layers and mask don't work as you expect.
First of all, the collision layer and mask are flags. The values of the layers are powers of two (1, 2, 4, 8...). So, when you set it to 3, it is the layer 1 plus the layer 2. So it still collides with a collision mask of 1.
And second, even if you changed the collision layer of the rooms to 2 (so it does not match the collision mask of 1 that the player character has). The player character still has a layer 1 which match the collision mask of the rooms.
See also the proposal Make physics layers and masks logic simple and consistent.
Thus, you would need to change the layer and mask. Both. in such way that they don't collide. For example, you can set layer and mask to 0 (which disable all collisions). The algorithm that populates the TileMap
does not use the layer and mask.
推荐阅读
- visual-studio - 如何在 Xamarin Visual Studio 2019 中分析应用程序性能
- python - 我的 Cython 代码与 Python 交互过多
- react-native - 换行(多行)按钮
- python - 为什么我的数据框在连接时会创建新行?
- azure - 在运行 az cosmosdb create 命令创建 Cosmos DB 帐户时如何指定 MongoDB API 版本?
- angular - 我们可以从Angular 8中的孙子组件访问数据吗
- react-router-dom - 防止复制的 url 重定向到 /
- javascript - 如何使用 axios 获取多个 api?
- rest - 从 Netsuite Web 服务 (Suitetalk) 请求销售订单数据
- android - [Android][Retrofit] 结果总是返回 503 Service Temporarily Unavailable