How to make my player look in a specific direction, when transitioning between rooms?

Godot Version

4.4

Question

So i am making a 2d topdown game for fun ,and I can’t figure out how to make my player look in a specific direction, when transitioning between rooms.
I am using animation tree for directions and animation player for animations.

Here is the code i use for player movement:

extends CharacterBody2D


var direction : Vector2 = Vector2.ZERO
var speed = 80

@onready var anim_player = $AnimationPlayer
@onready var animation_tree : AnimationTree = $AnimationTree

func _ready() -> void:
	animation_tree.active = true

#Handle Input and directions
func get_input():
	if Global.can_move:
		direction = Input.get_vector("Left", "Right", "Up", "Down").normalized()
		if direction:
			velocity = direction * speed
		else:
			velocity = Vector2.ZERO
		
		
		if Input.is_action_pressed("Sprint"):
			speed = 100
		else:
			speed = 80
	

func _physics_process(_delta):
	update_animation_parameters()
	get_input()
	move_and_slide()
	
#Update animations
func update_animation_parameters():
	if Global.can_move == true:
		if (velocity == Vector2.ZERO):
			animation_tree["parameters/conditions/idle"] = true
			animation_tree["parameters/conditions/is_moving"] = false
			animation_tree["parameters/conditions/is_running"] = false
		elif Input.is_action_pressed("Sprint"):
			animation_tree["parameters/conditions/idle"] = false
			animation_tree["parameters/conditions/is_moving"] = false
			animation_tree["parameters/conditions/is_running"] = true
		else:
			animation_tree["parameters/conditions/idle"] = false
			animation_tree["parameters/conditions/is_moving"] = true
			animation_tree["parameters/conditions/is_running"] = false
			
		if direction != Vector2.ZERO:
			animation_tree["parameters/Idle/blend_position"] = direction
			animation_tree["parameters/Run/blend_position"] = direction
			animation_tree["parameters/Walk/blend_position"] = direction

And here is code i use for transitions (still gonna be adding transition animations):

extends Area2D


var current_scene = Global.current_scene

@export var scene_tp : PackedScene = null
#Player
@export_category("Player")
@export var position_y_tp : float = 0
@export var position_x_tp : float = 0
@export var can_move : bool = true
@export_enum("Up","Down","Left","Right") var facing = 0
#Camera
@export_category("Camera")
@export var camera_max_left : int = -160
@export var camera_max_right : int = 160
@export var camera_max_up : int = -90
@export var camera_max_down : int = 90
@onready var player = preload("res://Scenes/Things/Characters/player.tscn")




func _on_body_entered(body: Node2D) -> void:
	get_tree().change_scene_to_packed(scene_tp)
	player.instantiate().position = Vector2(position_x_tp, position_y_tp)
	**if facing == 0:**
**		player.direction = Vector2(-1, 0)**
**		player.direction = Vector2.ZERO**
**	elif facing == 1:**
**		player.direction = Vector2(1,0)**
**		player.direction = Vector2.ZERO**
**	elif facing == 2:**
**		player.direction = Vector2(0,1)**
**		player.direction = Vector2.ZERO**
**	elif facing == 3:**
**		player.direction = Vector2(0,-1)**
**		player.direction = Vector2.ZERO**
**	else:**
**		pass**
		
	Global.m_cam_bottom_max = camera_max_down
	Global.m_cam_right_max = camera_max_right
	Global.m_cam_left_max = camera_max_left
	Global.m_cam_top_max = camera_max_up

(The bold text is what is not working for me)
This is supposed to be reusable transition node.

Sorry for my spaghetti code, I come from game maker and and I am still a newbee in godot and gdscript.

Thanks

First a question: Why set up an enum and subsequently refer to it by integer indices? facing == 3 should be facing == RIGHT. Also, your case names don’t match with the vectors you’re setting (UP is not (-1, 0), LEFT is not (0, 1), etc).

In your code:

**		player.direction = Vector2(1,0)**
**		player.direction = Vector2.ZERO**

What do you suppose player_direction is after these two lines run?

Well for the enum i never really used enums in godot and the documentation for it didn’t make sense to me, but now it makes a little bit more sense. The Vectors were supposed to be figured out as trial and error, but the errors were not what i expected. For the player.direction I thought that if i am gonna set the vector2 to the direction (which was wrong) and then set it to Vector2.ZERO (for idle animation), it would work, but well it didn’t. Btw thanks for response

I’m not sure I answered your question - sorry, I assumed you were a bit more familiar with scripting. Let’s go through it in a bit more detail. First of all, though, it’s not clear from your question what the failure mode is. When the player enters a new room, is he always facing the same direction (always facing up, for example), or is it different directions based on the exported enum value, but always the wrong direction, or some other thing?

When you set up @export_enum("Up","Down","Left","Right") var facing = 0, you’re setting up a variable called facing which takes possible values 0, 1, 2, or 3. These correspond in the order you defined, so “Up” ↔ 0, “Down” ↔ 1, “Left” ↔ 2, “Right” ↔ 3. You’re setting the default value to 0, which corresponds to “Up”.

Later on, in your _on_body_entered handler:

	**if facing == 0:**
**		player.direction = Vector2(-1, 0)**
**		player.direction = Vector2.ZERO**

So, if facing is “Up”, you are setting player.direction to (-1, 0), which is actually LEFT. But then directly afterwards you’re setting player.direction to (0, 0). This second line happens immediately after the first, meaning nothing actually happens when you set the player direction to LEFT. It’s as if the first line never happened.

Looking at your player script, I notice a few things. In get_input(), since direction is ZERO, you will set velocity to ZERO:

		if direction:
			velocity = direction * speed
		else:
			velocity = Vector2.ZERO

Further down, in your update_animation_parameters(), since your velocity is zero, you end up running all this code:

		if (velocity == Vector2.ZERO):
			animation_tree["parameters/conditions/idle"] = true
			animation_tree["parameters/conditions/is_moving"] = false
			animation_tree["parameters/conditions/is_running"] = false

Even further down, since your direction is ZERO, you end up not running the following animations:

		if direction != Vector2.ZERO:
			animation_tree["parameters/Idle/blend_position"] = direction
			animation_tree["parameters/Run/blend_position"] = direction
			animation_tree["parameters/Walk/blend_position"] = direction

I’d recommend cleaning this up and seeing if it helps at all. If not, I’m happy to look into it more.

1 Like

I’ll look into it. So if I enter the room the player is facing to the right, everything like moving and running is working.
CleanShot 2025-03-11 at 23.18.19

I think I just dont have enough knowledge to make this feature. I am going to try a different approach

Make sure to include any error messages you recieve, and what you expect to happen versus what really happens. I could only tell from the last few frames of that .gif you sent, which is a poor medium for reading text.


Your player is a packed scene, you create a new player on body entered here, and throw away the reference, this is wasted memory, it is not added to the next scene, and your player is still a packed scene reference.

Since you are changing scenes you may want to add your player to the root or as a gobal when the game starts, this will prevent them from being deleted when calling change_scene_to_*.

You could also store the map scenes in a seperate node, manually managing it’s state while controling the player elsewhere. For example this script will handle scene changes as a child node

@export var player: Node2D

func change_map(new_scene_path: String, new_player_position: Vector2) -> void:
    var current_map: Node2d = $MapContainer.get_child(0)
    current_map.queue_free()

    var new_map: Node2D = load(new_scene_path).instantiate()
    $MapContainer.add_child(new_map)

    player.global_position = new_player_position

Then your Area2D can call this function, even modifying the same player during or after it’s call

@export var level_manager: Node

@export_file("*.tscn") var scene_tp_path: String
@export var position_tp: Vector2 = Vector2.ZERO

func _on_body_entered(body: Node2D) -> void:
    if body is Player:
        level_manager.change_map(scene_tp_path, position_tp)

        if facing == 0:
            body.direction = Vector2.UP

You can read a little more about this, and some other techniques on the docs.

2 Likes

I have a question about the first code segment. Where should i put it in my code? From what i see, i should create a new node called Level Manager and put the code in, right? Where should i create this node? And what type should it be? Sorry for my little to no knowledge about gdscript. And thank you for your response