Good ways to pass data through many scenes

Godot Version

4.3 stable

Question

Consider the following examples:

1 - In the Super Mario World map/overworld screen, the player can enter different levels and keep their power-ups, lives and coins between levels. In this case, imagine the map screen and each level are different scenes.

When the player exits a level to the map screen and then enter another level, those values would need to carry over to the new scenes.

2 - In a roguelike game “run settings” screen (e.g. Risk of Rain, Slay the Spire or Brotato character selection screens, or Enter the Gungeon main lobby) the player can set some configs for how the “level scenes” would behave, like spawning only certain types of enemies or items, the character they want to play as or the game mode.

Those setting would need to be carried over through different levels and alter their behavior.

I am new to game development and would like to know the common approaches for this kind of problem.
I’ve read about autoloads/singletons, like making a “Player” global to store player data and a “Settings” data store… well… settings… but are they the best option when you could have multiple players (in this case, having a “Player_X” global for each possible one) or a “MapConfig” global which would only be used to instance a single map with the right amount of enemies?

Sorrry for the long post by the way, i am quite bad at expressing myself in english

autoloads work. You can also use resource files.

This uses it for game saves, but I’m pretty sure you can use them between scenes without saving.

3 Likes

I would not use a singleton for that.
Following the example of the super mario world, consider having 3 save game slots, then also each one needs to have this information. And it is not shared between those different games.
In my case I have a root node (lets call it GameWorld) were I have the PlayerNode (with its model, and all data to pass between levels) and a placeholder node to load a level.
Then at the start a Just remove the Player form the GameWorld node tree, and have it in a interal variable of GameWorld class.
After that I load the level into the level placeholder node, then I add the player into the Level (inside an Entrance node, which will depend where the player is comming from, you can have many entrances) then the player already has all data needed. When the player exists the level, I remove the player from the level.
In my case it is more a hollow knight style where you move from level to level, but for mario style with a worldmap and levels it should be the same.

2 Likes

This doesn’t exactly answer my question of how to pass the data around, but sound extremely useful to know nonetheless, thank you

So if i understand this correctly, you would have a scene tree like this:

  • (root)
    • [Project autoloads]
    • [Persistent data (e.g. Player node)]
    • GameWorld
      • [Level Scene or Placeholder]

And pass the Player node into the level scene, then when the player exits the level pass the Player node back into the “persistent data section”, swap the level scene inside the GameWorld to the new level/map screen and pass the player back into the new scene, is that right?

I actually really like this approach, but could you give me a code example of how you would go about switching levels in this case?

1 Like

Godot Demos “loading”: godot-demo-projects/loading at master · godotengine/godot-demo-projects · GitHub

Maybe it has everything you need and more.

2 Likes

Is like that, but the GameWorld is the Root, and a node as a level place holder

  • [GameWorld]
    • [Other World Common Nodes] (Like Map, WorldEnvironment, CanvasModulate, …)
    • [Player] (to be removed by code or you can also instantiate it in code)
    • [Level Placeholder] (a simple Node2D)
      • [Level Instance] (this is loaded and inserted in the placeholder by code)

To change the level this is a sample code I use:

func change_level(zone: Zone.ZoneId, level: int, scene_entrance_id: int) -> void:
	current_level.remove_character_from_scene_place_holder(character)
	fade_transition.fade_out()
	remove_current_level()
	load_level(zone, level)
	_update_map()
	fade_transition.fade_in()
	current_level.put_character_to_entrance(scene_entrance_id, character)
	current_level.put_character_at_scene_place_holder(character)

Then, each Level is a Node2D with class_name Level with this methods, you can also have this methos directly in the GameWrold class though:

func remove_character_from_scene_place_holder(character: CharacterView) -> void:
	var place_holder: Node2D = get_node("%CharacterPlaceHolder") as Node2D
	place_holder.remove_child(character)

func put_character_at_scene_place_holder(character: CharacterView) -> void:
	var place_holder: Node2D = get_node("%CharacterPlaceHolder") as Node2D
	place_holder.add_child(character)

Each level needs to have the Unique Node %CharacterPlaceHolder…
CharacterView is just the CharacterBody2D but I have a class_name for the player.

1 Like

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