Level loading in 2d platformer game

Hi, I’m pretty new to GODOT coding an am working on my first game.
I am following a tutorial by MuddyWolf on youtube (https://www.youtube.com/watch?v=mKlKMBSM2Ss) to try and get started but have gotten stuck at a main menu option. I have made a main menu and I am trying to load my first level after pressing ‘play’


When I press play it completely crashes and comes up with this error message:

not all arguments converted during string formatting in operator ‘%’.
I’m pretty sure it has something to do with this line of code here:
image
However, I have absolutely no clue how to fix it.
Any help would be appreciated

i’m sorry, what’s that supposed to do? What are you hoping level_path will be?

% is a string interpolation operator - given a string with variables in it, replace them with these other things.

tooltip_text = "%s\n%s" % [data.name, data.description]

If you want string concatentation you use +. But your code doesn’t look like it needs that - isn’t the value (in yellow) all you need?

var level_fqn = "res://scenes/world.tscn"
get_tree().change_scene_to_file(level_fqn)

i don’t know where level_data.level_path fits in. Is it maybe supposed to be

get_tree().change_scene_to_file(level_data.level_path)
1 Like

Thanks for your help! I still am very new and was just following MuddyWolf’s youtube pixel platformer tutorial so I’m not really sure what level_path does to be honest.
I tried your suggestion with the
var level_fqn = “res://scenes/world.tscn”
get_tree().change_scene_to_file(level_fqn)
and it came up with a different error message in a separate script for my level being:

I also tried to add the
get_tree().change_scene_to_file(level_data.level_path)
to my original code and when I tried it out it came up with these errors:

Both result in the debug crashing and error messages in the ‘level’ script and the ‘LevelManager’ script. I might just be doing all of this incorrectly also, so I’ll attach photos of the levelManager script as well as the level script to see if there are any other errors.
Thanks for helping though!

level script (the error message line is highlighted):
image

Level Manager script 1/3:
image

Level Manager script 2/3 (the line the error message might be referring to is highlighted):
image

Level Manager script 3/3
image

i’m still new so don’t trust me too much but… Yellow isn’t an error, it’s a warning. In this case, Godot has this odd thing where if you have a method like

func _process(delta):
    pass

it wants you to know that you declared a parameter (delta) but never used it. If that’s cool, you truly don’t need it, Godot would like you to change the name to _delta, where _ means you’re purposely neglecting it.

1 Like

“Resource file not found: res://levels/world” is in red and is an error. It means that file doesn’t exist. Which is probably true since it looks like the file name extension is missing? Like res://levels/world.tscn?

As a note, if you added line 24 (get_tree() part) because of me, you probably don’t want to. That’s the code that loads levels but it sounds like dirty wolf has his level code somewhere else (possibly a line just like that) and you don’t want to mix and match.

i’m really not sure where level_path is coming from. It looks like the lines above have level_data and perhaps Senior Wolf has a file name in there? Line 23 hard codes it to always load world.tscn, which might be intended but i wanted to call that out.

As a note, i keep typing tcsn instead of tscn and that’s caused a lot of my errors. You’ll want to pay attention to those path and file names closely and compare them to what’s in your IDE file list.

1 Like

Look like you using a level system for changing your scene but it’s not what you need right now.

I explain as clearly as possible :

The current system you show us can change world node content without removing other stuff. For example if you have a scene with a tree like this :

 - Game
 | - Player
 | - HUD
 | - Level
 | - Other Stuff

You can configure the LevelManager by setting the main_scene to be the “Level” node in my tree and only the content of this node will change when changing level conserving all the other node (main_scene will be better called main_node)

But in your case to change from the main menu to the game it’s look totally useless. You can just use the line provide by baylorw in the button pressed function :
get_tree().change_scene_to_file("res://levels/world.tscn")
And use the other system to switch from a level to another.

By the way in your current LevelManager you need to remove line 23 and 24 and set line 25 to :
var level_res = load(level_data.level_path)

Other thing, if you want i can explain what your current code do by adding some comments.

You can use the copy path from the file system ( or do a Ctrl+Shift+C ) on your scene file to get the correct path name

Thanks for your help! I would find it very useful if you could add a few comments to my code. I put the following code in for the button pressed function in my main_menu script and it came up with another error in my level script.


Before I added it and just changed the LevelManager script it came up with a grey screen after pressing ‘play’. I will also attach my main menu script to see if maybe the bug is there. I really appreciate your help though.

MainMenu script 1/2
image

MainMenu script 2/2
image

Also sorry, I should mention that I’m using Godot 4.1.3 I don’t think there have been many changes, but just in case I thought I should mention it

Here is the full thing. I modified a bit your code (tagged with # ADDED in the comment) and fix your current error. I also indicate for the 2 first variable in LevelManager than I don’t know how you initialise them because it’s done outside of this script.

Level.gd
class_name Level
extends Node2D

# Set level id (need a resource with the same id)
@export var level_id : int
var level_data : LevelData

func _ready():
	# Get level data from level manager
	level_data = LevelManager.get_level_data_by_id(level_id) # ADDED Error in the function name
LevelManager.gd
extends Node2D

# List of all level data (I don't know how you initialise it)
var levels : Array[LevelData]

# Node containing the level scene (I don't know how you initialise it)
var main_scene : Node2D = null
# Actual loaded level
var loaded_level : Level = null

# Unload the current level (called when you load a level)
func unload_level():
	# If the current level node is valid (not already freeing)
	if is_instance_valid(loaded_level):
		# free the current level
		loaded_level.queue_free()
	# clear the variable
	loaded_level = null

# Load a level from it's id
func load_level(level_id : int):
	# First unload current level
	unload_level()
	# Get level data from the id
	var level_data = get_level_data_by_id(level_id)
	# If there is no level data with the sepecified id, return
	if not level_data:
		printerr("No data level exist with the id ", level_id) # ADDED show an error as level must have a level data
		return
	
	# Load the new level scene
	var level_res = load(level_data.level_path)

	# If level is loaded
	if level_res:
		# current level is set to en instance of the new one
		loaded_level = level_res.instantiate()
		# the instance is added to the node containing level
		main_scene.add_child(loaded_level)
	else:
		# If it's not loaded, print an error in console
		printerr("Level does not exist at path ", level_data.level_path) # ADDED changed to be more precise


# Get level data from the level id
func get_level_data_by_id(id : int) -> LevelData:
	# initialise found level to null (no level found yet)
	var level_to_return : LevelData = null

	# For each level data in the levels list
	for lvl in levels:
		# If the level id match the search one
		if lvl.level_id == id:
			# define the level to return as this level
			level_to_return = lvl
			break # ADDED to stop the loop as we already found the scene
	
	# Return the found level (or null if none)
	return level_to_return 

And I assume your level data look like this :

LevelData.gd
class_name LevelData
extends Resource

# level id
@export var level_id : int = 0
# level scene path
@export var level_path : String = ""