Godot World Manager
Published on: Saturday, November 30, 2024 at 1:00 PM PST
Written by Josh Bright
Intro
Today post is a bit less about coding and more about planning. I will “map” out (excuse the pun!) how I normally setup a way to let the player move between various maps. A map system needs to be able to load maps, as well as unload and switch to a new map. It flso needs to know where things go, npcs and enemies to load, as well as make it easy to for the creator to define that. How I like to think about maps is that it is like a game cartridge, where you can unplug one, and plug in another.
Map Parts
First, its probably good that we define what makes up a map. The first thing that people think of, is the actual visual representation of the map. Normally, for the games I like to tinker with, this is a TileMap. A TileMap is a grid of squares, where you place tiles in each square. This makes it easy to have a small set of tiles, but with those, you can create huge varied maps. These tiles may even have a sort of depth to them, where a tile may also be a wall, that prevents the player from walking across it.
Another part of a map is how we can move between maps. Normally you would move your character down a path, reach the edge of a map, and you would then load into a new map. You might also walk up to a house, open a door, go through it, and a house “map” would load up. I like to call these things teleporters, which I can attach some information, mainly the map name that should be loaded when a player walks on it. This is mainly what our World Manager is going to be managing.
To know where a player should go when they go from one map to another, you need to know a position, and be able to find that position when loading up a map. Normally, I use the old map name as a way to find that position. So, lets say we have a Lake map, and I want to move to a House map. On the House map, I might define a marker called “FromLake”. This makes it pretty easy to say, when going from the Lake, to the House, find the position called FromLake, and put the player there.
There are a few other parts of map, that are unrelated to moving between maps, but I think are good to explain. I normally have a place that enemies go, which makes it easy for a system to know, “how can I get to the list of enemies on this map”. I also have an NPC node, which all NPCs will sit in. There might also be an “Objects” node, which old things like treasure chests. One part related to these types of things is keeping track of the state of those items. If I open a chest, leave the map, and then go back to it, the chest should still appear to be open. If I kill an enemy in a map, how do you track that we shouldn’t load that enemy back later on? I do have some ideas on how to make that work, but, that stuff will come later on. These nodes that hold the various types of things on the map can be thought of as folders that you can use to organize everything.
Map Structure
Here is an example of a map from an earlier game I was working on:
I normally start out with a Godot tree looking like that. This gives me a predictable way of finding things, no matter what actual map is loaded.
Saving and Loading
One issue when working with maps is how do we know what map to load when the game loads, and how we can save what map the player is on when they save the game, so that when they load the game later, they are back to where they were at earlier. In cases where a player is starting a brand new game, we can set a default map and position to get things started, and then later, we rely upon our saved games to keep track of this.
I normally give each map a unique name, which makes it easy to save what map the player is at just by storing that name. Next, we now need to store the global position of the player, and with that we can re-position the player.
The “World Manager”
To make all this work, I normally have a node in the game that handles map loading. This is just a Node2D that has a script attached to it, and that is where the magic happens. Within that script, I define the list of maps in the game, and what scene they point to. I have a default map, in cases where the player is starting a new game, as well as keeping track of what the current, and last map that the player was in. This gives me all the data needed to move between maps, and position the player in the correct spots. When I need to switch from one map to the next, we can remove the current map, find the new map, and add that as a child to the world manager. There’s a few more details here that also need to happen, like positioning the player in the right spot, and if there is any map state that needs to be updated. To get things started, just loading the map and getting the player in the right spot will be our goal.
Last Thoughts
I’ve found that making a map loading system can be pretty satisfying, especially once you get things working. Being able to make new maps, plug in a few names here and there, and then getting it to work in your game can be a lot of fun in itself! My next dev day I will implement this system with a concrete example that I hope others might be able to take inspiration from.