Como posso implementar um estado persistente para objetos definidos em um nível?

17

Estou desenvolvendo um Metroidvania 2D que consiste em uma série de níveis interconectados que podem ser revisitados.

Cada nível é representado por um arquivo Tiled TMX no qual especifiquei onde vários objetos de diferentes classes de sprites aparecem (por exemplo, inimigos, captadores, alavancas etc.). Ao iniciar um novo jogo, carregar um jogo salvo ou alterar um nível, meu loop de jogo é executado no arquivo TMX apropriado e gera todos os objetos nesse nível.

Trato de mudanças de nível da seguinte maneira: Se o Playerobjeto cruza um Portalobjeto, change_map()é chamado um método que carrega um novo mapa (o associado ao portal cruzado) e posiciona o jogador na posição apropriada no novo mapa.

Alguns dos meus objetos têm estados que eu gostaria de persistir com as mudanças de nível, salvando e saindo do jogo. Por exemplo, se um jogador desbloquear uma porta e o atributo de estado da porta estiver definido como "aberto", eu gostaria que a porta estivesse aberta quando o jogador retornar. Quero algo semelhante para alavancas, que podem ser definidas para esquerda ou direita, e vários outros objetos. Além disso, o jogador às vezes coleciona itens que eu não quero ressurgir quando o jogador revisitar a área.

Minha pergunta é, portanto, como posso lidar com esse tipo de persistência?

Estou trabalhando em Python, apesar de achar que você pode abstrair disso.

GoldenGremlin
fonte

Respostas:

27

Eu acho que não pensar demais nesse problema fornecerá os melhores resultados, então eu apenas implementaria um sistema simples de economia de valor-chave em seu jogo que você armazena junto com outros dados salvos e carrega sob demanda quando precisar acessar um estado anterior.

O fluxo pode ser algo como isto:

  1. Carregar nível do arquivo
  2. Antes de colocar um bloco / objeto, verifique se ele possui uma propriedade "persistente".
    1. Se sim: verifique o par de valores-chave salvo para a chave correspondente à propriedade e busque o valor apropriado.
    2. Se não: coloque o objeto normalmente
  3. Quando o jogador sai do nível / salva o ciclo do jogo em todos os objetos com uma propriedade "persistente" e os salva como um par de valores-chave.

Aqui está um exemplo de pseudo-código com base no que eu uso no meu jogo 2D simples:

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)

Então eu posso apenas verificar o estado usando esta propriedade:

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

Se você estiver usando um editor de mapas lado a lado como o Tiled, adicionar propriedades como esta é muito fácil:

adicionando propriedade

Espero que isso lhe dê uma idéia de como implementar o estado persistente da maneira mais simples possível!

Charanor
fonte
Isso é muito útil, embora eu esteja lutando para ver exatamente como aplicá-lo à minha situação. Vou pensar um pouco mais.
GoldenGremlin
Acho que estou tendo problemas para ver como posso salvar os valores para que funcionem. Quando estou salvando, não iterarei sobre blocos nos dados do TMX. Em vez disso, estarei iterando sobre objetos sprite no meu grupo all_sprites. Quando carrego os mapas, uso as propriedades TMX dos objetos TMX como parâmetros ao instanciar meus objetos sprite, mas depois disso não toco nessas propriedades, para que eles não rastreiem as alterações nos objetos sprite.
GoldenGremlin
1
@dietestus Você provavelmente deve apenas dar aos objetos do sprite um propertiescampo que modifique e usar apenas os blocos propertiescomo uma indicação de qual propriedade modificar (mas todos os dados são armazenados no sprite). Você também pode apenas passar o azulejo para seu sprite para que possa modificar a telha do Sprite :) se não está claro o que quer dizer, eu pode zombar até um pouco mais de pseudocódigo
Charanor
3
@dietestus Assim que você interage com uma entidade persistente (porta, alavanca), você salva o novo estado no mapa de valores-chave. Você não precisa percorrer os mapas ao salvar, já tem tudo no seu mapa.
Herr Derb
1
@dietestus Sim, você é :) é um dicionário simples, onde as chaves são nomes de propriedades e os valores são (bem ... valores). Ter vários objetos no mesmo bloco não mudará nada, desde que você tenha chaves exclusivas.
Charanor