Godot Version
4.2.2
Question
I’m working on a 2d platformer game. The game has many levels (each of them is a scene). Using an main scene as:
world
There are 3 coins in testlevel and I want to change level when player collected all of them. But I couldn’t (godot gives in/out error when using free). When it changes the level somehow, coin that was collected last disappears.
Could you please help me about this level changing mechanic. I’m also wondering is it a correct way to load levels.
Could you paste your script? I think you need to avoid queue_free()
on the last coin.
1 Like
I will share it immediately but just wondering why I have to avoid queue_free()?
Because using a change scene function will also free the coin, you could be double-freeing it
Hmm. Do you suggest don’t queue_free() for the last one. (Basic check for how many coins left and if it is one then don’t queue_free())
1 Like
I thought same. Because in coin script I printed log when the coin pickup and when I do change level stuff it printed. Just like signal keeps alive and emitting just after level changing stuff.
coin.gd
extends Node2D
@onready var level = $".."
func _ready():
level.coins_on_level += 1
func _on_collectable_area_body_entered(body):
queue_free()
SignalBus.coin_picked_up.emit()
level.gd
class_name Level
extends Node2D
@export var level_name := ""
var coins_on_level := 0
var start_position := Vector2.ZERO
func _ready():
get_node("/root/World/Player").position = Vector2.ZERO
LevelManager.assign_level(self)
SignalBus.coin_picked_up.connect(_on_coin_picked_up)
func _on_coin_picked_up():
coins_on_level -= 1
print(coins_on_level)
if coins_on_level == 0:
call_deferred("chg_scn")
func chg_scn():
get_node("/root/World/Player").position = Vector2.ZERO
get_node("/root/World").add_child(preload("res://scenes/test_level.tscn").instantiate())
print("asd ")
get_node("/root/World").remove_child(self)
I also had levelmanager and signalbus autoload scripts. But I toogle comment on levelmanager for clarity. I just used level.gd for that.
I simplified my script.
coin.gd
extends Node2D
@onready var collectable_area = $CollectableArea
@onready var animation_player = $AnimationPlayer
func _ready():
get_parent().coins_on_level += 1
func _on_collectable_area_body_entered(body):
get_parent().coins_on_level -= 1
animation_player.play("pick_up")
if get_parent().coins_on_level == 0:
Player.position = Vector2.ZERO
LevelManager.restart_level(self)
level.gd
class_name Level
extends Node2D
@export var level_name := ""
var coins_on_level := 0
var start_position := Vector2.ZERO
func _ready():
pass
level_manager.gd (autoload)
extends Node
func restart_level(coin):
await coin.tree_exited
get_tree().reload_current_scene()

It seems work. I also make Player scene autoload which is my player. I think the reason for the problem is not queue_free() or anything remains. The reason is player collect coin just after changing scene. There are 2 solutions that comes to my mind:
- When last coin collected, place player at (0,0) and player can’t collect new spawned coin.
- When level ready, wait certain time and then allow collecting coin.
Second solution is not working well, I think. I don’t want any timing issue or wait time.
The problem is solved but looking weird (player spawns immediately after collecting last coin). Also, I made Player autoload and add Camera2D to my player but it zooms out as I don’t want. And is it good practice to call up? I’m calling levelmanager’s function from coin.gd.
You’re doing the level change backwards. You also did not use the function. Godot does have a built in level changer. I get that most of the time there’s a need to run code that looks like yours. But, it will not make sense. Here’s a one page tutorial. I don’t want to watt and clattter this answer. There’s more;
https://plug-world.com/posts/godot-4-switching-levels-made-easy/
You’re also not using Godot flags. But, apparently not using flags right. Because operations are flags. I can’t describe anything. I can’t find the documentation. There’s a way to use simple flags for replacing variables like yours.
You can determine a way to manipulate constants. It’s on this page; https://docs.godotengine.org/en/4.1/contributing/development/core_and_modules/object_class.html With the use of enumerators. The enumerators use the flags. That’s the problem there. You can use variables that grow. Like you collected coin counters. But, the original coin value can be constant. Constants are easier to introduce to the level data. Constants apply the same integer values. They can be switched on and off. Just in case you want them to disappear.
Hmm, thanks. Calling deferred solved my problem. But I don’t understand what do you mean by flags. Can you give some examples how can I use flags in my project and what benefits they will bring.
Could disabled the player’s collision mask/layer for coins for a short while after spawn.
I would say it’s usually bad to call up, manipulating through get_parent()
is scary.
Yeah I’m just setting player’s position and get_tree().call_deferred(“reload_current_scene”).
I have a practical example. I’m sorry if going off topic. It’s data related. Because I seen a similar situation in protagonist duration during a story. Where the protagonist called for a rage of slaughter and confrontations because of some confusion. Because the protagonist was in detention to a computerised battle assistant. The assistant was generally making calls about the actions it performed by itself. And, this confronted the protagonist to ignore the assistant as the invisible inevitable enemy. You can also understand this made a lot of problems for the developer and the producer to separate and sparsely direct the dialog. For, the appropriate actor to play out the role. Because quite frankly as a regular Asian novelty the character is affidated as a young student. And, actually according to the story has little dialog. First of all, the original recordings of self. Like an audio diary. So basically the entire time protagonist is fighting itself. If the arguments in fact cause this conflict. And, because the dialog is very sparse. The rest of the time the protagonist just counts from one to infinity.
Here’s an example of flags.
extends Node
var is_game_over = false
func _ready():
print('The flag is ' + str(is_game_over))
I can’t grow an example right now. Other than the one in the documentation;
enum SomeMode {
MODE_FIRST,
MODE_SECOND
};
It would be compared to using the Godot groups. In samples if in group;
You make the flag, and set if up. During the game switch it and check it. If the example doesn’t make sense. It shouldn’t. For some reason this is a trap. And, the documentation is now stuck in C++. Sorry.
I don’t know how to relate to more subjects here. My programming application started with DirectX Xdict for audio in 1997. Most of this can be ready for packing into a sound system. It works simply as explained. The constant flag turns on for value set. Pushing the counters. Logically the set up should remove the counters to all zeros when the constant disappears. If not, then it must be set to replicate this behaviour. This gives you the ability to support mono gathering on the same variable. Used as var in godot. And, apply lots of constants. Used as const in Godot. Apply lots of constants to only one or the same variable.
I can complete this course in one comment. The maximum amount of display you can get out of a single speaker is 16hz per hour. That's how loud it's going to get. With simple math it's one push of a sound wave. Now. So it will come down to treble, bass, the mids, actuators, condensed echo, condensed reversed echo and condenser. That's seven object that have their own maximums. A push can only be a single push. Maximums can be in the numbers of 250,000(thousands) at once. So you try to seer it very slowly into a single push. It just starts to make sense after some time like algebra. When you multiply by decimals the amount gets smaller. That may be true for multiplying millions by fractions of 16.
I used this to make the items disappear after being collected.
end_level_check()
is the function to check if the level has been completed.
$AnimatedSprite2D.visible = false
$CollisionShape2D.set_deferred("disabled",true)
end_level_check()