17
\$\begingroup\$

I am developing a 2D Metroidvania which consists of a series of interconnected levels that can be revisited.

Each level is represented by a Tiled TMX file in which I have specified where various objects of different sprite classes spawn (for example, enemies, pickups, levers, etc.). When starting a new game, loading a saved game, or changing a level, my game loop runs through the appropriate TMX file and generates all the objects in that level.

I handle level changes in the following way: If the Player object intersects a Portal object, a change_map() method is called which loads a new map (the one associated with the intersected portal) and positions the player at the appropriate position on the new map.

Some of my objects have states which I would like to be persistent through level changes and saving and quitting the game. For example, if a player unlocks a door and the state attribute of the door is set to "open," I would like the door to be open when the player returns. I want something similar for levers, which can be set to left or right, and various other objects. Further, the player will sometimes have collected items which I do not want to respawn when the player revisits the area.

My question is thus how can I handle this kind of persistence?

I am working in Python, although I think you can abstract away from that.

\$\endgroup\$
27
\$\begingroup\$

I think not overthinking this issue will give the best results so I would just implement a simple key-value saving system into your game that you store along your other save data and then load on-demand when you need to access a previous state.

The flow could look something like this:

  1. Load level from file
  2. Before placing a tile / object check if it has a "persistent" property.
    1. If yes: Check the saved key-value pair for the key matching the property and fetch the approprtiate value.
    2. If no: Place the object as normal
  3. When the player exits the level / saves the game loop through all objects with a "persistent" property and save them as a key-value pair.

Here's a pseudo-code example based on what I use for my simple 2D game:

def load_map(map):
    for y in range(0, height):
        for x in range(0, width):
            tile = map[x, y]

            for property in tile.properties:
                if is_persistent(property.name):
                    // Name prefixed with "persistent" means that it's persistent
                    // so we load the value from out persistent storage
                    property.value = persistent_values[property.name]

def save_map(map):
    ... everything in load_map ...
    if (property.name.matches("persistent_*")):
        // Name prefixed with "persistent" means that it's persistent
        // so we save the value to persistent storage
        persistent_values[property.name] = property.value

def is_persistent(name):
    return name.matches("persistent_*") and persistent_values.contains(name)

Then I can just check state using this property:

def draw():
    if properties["persistent_is_pressed"].value:
        draw_sprite(button_pressed)
    else:
        draw_sprite(button_unpressed)

def on_pressed():
    properties["persistent_is_pressed"].value = not properties["persistent_is_pressed"].value

If you're using a tiled map editor like Tiled adding properties like this is very easy:

adding property

Hopefully this will give you an idea on how to implement persistent state as simple as possible!

\$\endgroup\$
  • \$\begingroup\$ This is very helpful, though I am struggling to see exactly how to apply it to my situation. I will think about it some more. \$\endgroup\$ – GoldenGremlin Jan 4 '18 at 1:38
  • \$\begingroup\$ I think I'm having trouble seeing how I can get the saving of the values to work. When I'm saving, I won't be iterating over tiles in the TMX data. Rather, I'll be iterating over sprite objects in my all_sprites group. When I load the maps, I use the TMX properties of the TMX objects as parameters when instantiating my sprite objects, but after that I don't touch those properties, so they're not tracking changes in the sprite objects. \$\endgroup\$ – GoldenGremlin Jan 4 '18 at 1:56
  • 1
    \$\begingroup\$ @dietestus You should probably just give your sprite objects a properties field that you modify instead and only use the tiles properties as an indication of what property to modify (but all data is stored in your sprite). You could also just pass the tile to your sprite so you can modify the tile from the sprite :) if it's not clear what I mean I can mock up some more pseudocode \$\endgroup\$ – Charanor Jan 4 '18 at 8:06
  • 3
    \$\begingroup\$ @dietestus As soon as you interact with a persistent entity (door,lever) you save the new state to the key-value map. You don't need to iterate over the maps when you save, you've already got everything in your map. \$\endgroup\$ – Herr Derb Jan 4 '18 at 8:52
  • 1
    \$\begingroup\$ @dietestus Yes you are :) it is a simple dictionary where the keys are property names and the values are (well... values). Having multiple objects on the same tile will not change anything as long as you have unique keys. \$\endgroup\$ – Charanor Jan 4 '18 at 15:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.