Player goes an extra room in transition

Godot Version

4.2.1

Question

I am working on a binding of isaac-like game. The rooms are transitioned between using a ladder of signals going from a Door → Wall → Room → DungeonFloor → GameScript as shown below:

Door:
func _on_body_entered(body) → void:
if body is Player:
print(“Door signal”)
body = body as Player
emit_signal(“door_entered”,body,self)

Wall:
func on_door_entered(player:Player,entered_door:Door) → void:
print(“Wall signal”)
emit_signal(“door_entered”,player,entered_door,wall_direction)

Room:
func on_door_entered(player:Player,door:Door,direction:Vector2i) → void:
emit_signal(“door_entered”,player,door,direction,coords)

DungeonFloor:

This function will be called by a child room when a door is entered.

On the door being entered, the dungeon floor needs to figure out where to send the player

And relay this information back to the game engine.

func on_door_entered(player:Player,door:Door,entry_direction:Vector2i,room_coord:Vector2i):
print(“Dungeon signal”)
assert(dungeon.has(entry_direction+room_coord),
“Tried to move into a room that doesnt exist. Check doors.”)
assert(entry_direction.length() == 1,“Direction is not a unit vector.”)
match entry_direction:
Vector2i.UP:
assign_new_room(next_room_up)
Vector2i.DOWN:
assign_new_room(next_room_down)
Vector2i.LEFT:
assign_new_room(next_room_left)
Vector2i.RIGHT:
assign_new_room(next_room_right)
emit_signal(“door_entered”,player,current_room,-entry_direction)

This function will add the current room into the

func assign_new_room(new_room) → void:
add_child(new_room)
if current_room != null:
call_deferred(“remove_child”,current_room)
current_room = new_room
current_room_coords = new_room.coords
assign_adjacent_rooms(new_room)

func assign_adjacent_rooms(room:Room) → void:
next_room_down = null
next_room_left = null
next_room_right = null
next_room_up = null
if room.has_bottom_door:
next_room_down = dungeon.get(room.coords + Vector2i.DOWN)
if room.has_left_door:
next_room_left = dungeon.get(room.coords + Vector2i.LEFT)
if room.has_right_door:
next_room_right = dungeon.get(room.coords + Vector2i.RIGHT)
if room.has_top_door:
next_room_up = dungeon.get(room.coords + Vector2i.UP)

GameScript:

This function will be called upon a room being entered by the dungeon floor

This function will be responsible for moving the player to that new room as well as updating the

minimap.

func on_door_entered(player:Player,new_room:Room,exit_direction:Vector2i) → void:
assert(exit_direction.length() == 1,“Exit direction vector passed is not a unit vector.”)
#TODO fix this. It works, but is extremely error-prone.
player.global_position = new_room.get_wall_in_direction(exit_direction).get_node(“door”).global_position - (exit_direction as Vector2 * 30)
in_game_ui.minimap.update_map(new_room)
print(“Game State reached”)

I also have a minimap that displays what room the player is in:

When the player tries to go through a door that has at least two rooms in that direction, they consistently end up in the second room. They do enter the first room for a brief moment but then skip right to the next one.

For example, when I tried to go to the room to the right, I entered the room before the C-room for a brief moment before ending up in the C-room.

Any thoughts on how to solve this would be greatly appreciated :sweat:

1 Like

i think it’s just need to put delay or a check state if the player is moving to other room.
other than that, you can disable the collision of that specific door you are in then, make sure to pull the player to the opposite direction after then, reenabling the collision.

Here’s the weird part: First, I tried by just making a timer using await and create_timer. It worked, but only partially. With that, the player would only sometimes skip a room if it entered vertically or horizontally but if the player entered the door at a diagonal it would skip a room without fail.
I also tried disabling and then re-enabling collision but it didn’t end up working either.
I can try putting in a check state though, thank you for the idea

What solutions have you tried?

I tried the ones I listed beforehand and the suggested check state

did it print twice for the issue?

Yep, all the print statements run twice. The player definitely goes through two rooms; I can see it on the minimap for a brief moment and with the print statements

UPDATE: I think I figured something out!

This is what I’m doing now:

  1. Player enters door collision
  2. I update room
  3. I move player to out of the door collision (Door > Wall > Room > DungeonFloor > GameController)

The theory is that the game works this way:

  1. Player enters the door collision, game recognizes that the player has entered it and updates the room
  2. At this point, we have entered the next room and the player is still inside the door’s collision
  3. Then, when we move the player out of the door collision, the game recognizes that the player has left the collision and triggers again once the body leaves.
  4. Now, the new door from the room we just entered sees that we triggered the collision again and sends us one more room than we intended
  5. We don’t continue going through the rooms because at this point the player has exited the collision
    This would also explain why moving the player to the center of the room before the room transition wouldn’t work because we would be triggering the collision in the beginning room twice anyways

I found that by using “body.set_collision_layer_value(2, false)” in the door right before I emit the signal, the player only goes through one room like it should
The problem now is when I try to re-enable this collision in the GameController it ends up skipping a room.

I FOUND THE SOLUTION:

Godot has an error with collision detection where the on_body_entered for an Area2D will trigger when a body enters AND exits it.
So, the solution was:

  1. When the player enters the door collision, disable the player’s collision
  2. Change the room the player is in
  3. Reposition the player outside of the door’s collision
  4. WAIT 2 PHYSICS FRAMES (In Godot 4, this is: await get_tree().physics_frame to wait 1 frame. You need two consecutive lines of this code to wait 2 physics frames)
  5. Re-enable player collision

If you don’t wait AT LEAST 2 physics frames, it will not work and will trigger the collision again. Apparently, this has been a bug since the early days of Godot: Changing node parent produces Area2D/3D signal duplicates · Issue #14578 · godotengine/godot · GitHub

This is the most optimal way I found to solve this.

Hope this helps anyone in the future!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.