¿Cómo puedo implementar el estado persistente para los objetos definidos en un nivel?

17

Estoy desarrollando un Metroidvania 2D que consiste en una serie de niveles interconectados que pueden revisarse.

Cada nivel está representado por un archivo TMX en mosaico en el que he especificado dónde se generan varios objetos de diferentes clases de sprites (por ejemplo, enemigos, pastillas, palancas, etc.). Al comenzar un nuevo juego, cargar un juego guardado o cambiar un nivel, mi ciclo de juego se ejecuta a través del archivo TMX apropiado y genera todos los objetos en ese nivel.

Manejo los cambios de nivel de la siguiente manera: si el Playerobjeto se cruza con un Portalobjeto, change_map()se llama a un método que carga un nuevo mapa (el asociado con el portal intersectado) y coloca al jugador en la posición adecuada en el nuevo mapa.

Algunos de mis objetos tienen estados que me gustaría ser persistente a través de cambios de nivel y guardar y salir del juego. Por ejemplo, si un jugador desbloquea una puerta y el atributo de estado de la puerta está "abierto", me gustaría que la puerta esté abierta cuando el jugador regrese. Quiero algo similar para las palancas, que se pueden configurar a izquierda o derecha, y varios otros objetos. Además, el jugador a veces habrá recolectado elementos que no quiero reaparecer cuando vuelva a visitar el área.

Mi pregunta es, entonces, ¿cómo puedo manejar este tipo de persistencia?

Estoy trabajando en Python, aunque creo que puedes abstraerte de eso.

GoldenGremlin
fuente

Respuestas:

27

Creo que no pensar demasiado en este problema dará los mejores resultados, por lo que simplemente implementaría un sistema simple de ahorro de valor clave en su juego que almacena junto con sus otros datos guardados y luego carga bajo demanda cuando necesita acceder a un estado anterior.

El flujo podría verse más o menos así:

  1. Nivel de carga del archivo
  2. Antes de colocar un mosaico / objeto, verifique si tiene una propiedad "persistente".
    1. En caso afirmativo: compruebe el par clave-valor guardado para la clave que coincida con la propiedad y obtenga el valor apropiado.
    2. En caso negativo: coloque el objeto normalmente
  3. Cuando el jugador sale del nivel / guarda el ciclo del juego a través de todos los objetos con una propiedad "persistente" y los guarda como un par clave-valor.

Aquí hay un ejemplo de pseudocódigo basado en lo que uso para mi juego 2D simple:

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)

Entonces puedo verificar el estado usando esta propiedad:

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

Si está utilizando un editor de mapas en mosaico como Mosaico, agregar propiedades como esta es muy fácil:

agregando propiedad

¡Con suerte, esto le dará una idea sobre cómo implementar el estado persistente lo más simple posible!

Charanor
fuente
Esto es muy útil, aunque estoy luchando por ver exactamente cómo aplicarlo a mi situación. Lo pensaré un poco más.
GoldenGremlin
Creo que tengo problemas para ver cómo puedo lograr que el ahorro de valores funcione. Cuando guarde, no iteraré sobre mosaicos en los datos TMX. Más bien, iteraré sobre objetos sprite en mi grupo all_sprites. Cuando cargo los mapas, uso las propiedades TMX de los objetos TMX como parámetros al crear instancias de mis objetos sprite, pero después de eso no toco esas propiedades, por lo que no están rastreando los cambios en los objetos sprite.
GoldenGremlin
1
@dietestus Probablemente solo deberías dar a tus objetos sprite un propertiescampo que modifiques en su lugar y solo usar los mosaicos propertiescomo una indicación de qué propiedad modificar (pero todos los datos se almacenan en tu sprite). También podría pasar el mosaico a su sprite para que pueda modificar el mosaico del sprite :) si no está claro, quiero decir que puedo simular un poco más de pseudocódigo
Charanor
3
@dietestus Tan pronto como interactúa con una entidad persistente (puerta, palanca) guarda el nuevo estado en el mapa de valores clave. No necesita iterar sobre los mapas cuando guarda, ya tiene todo en su mapa.
Herr Derb
1
@dietestus Yes you are :) es un diccionario simple donde las claves son nombres de propiedades y los valores son (bueno ... valores). Tener varios objetos en el mismo mosaico no cambiará nada siempre que tenga claves únicas.
Charanor