coding

Escondendo partes do mapa com o Phaser JS - Game Devlog #21

Escrito em 6 de julho de 2021 - 🕒 3 min. de leitura

Para aqueles que não sabem, Metroidvania é provavelmente o meu gênero de jogo favorito, então decidi adicionar um recurso clássico de jogos Metroidvania ao Super Ollie, partes ocultas da fase que aparecem quando você se aproxima delas. Veja o gif abaixo.

Parede oculta

Para isso, vou precisar de 2 novas propriedades de layer no Tiled, uma chamada hideOrShowOnContact para sinalizar se essa layer deve ser escondida/exibida quando o jogador interage com ela, e uma chamada isHidden para sinalizar se essa layer deve começar escondida ou não.

Esconder ou exibir layer no Tiled
Esconder ou exibir layer no Tiled

Agora preciso percorrer todas as layers do meu mapa para verificar se alguma delas contém essas propriedades, algo tipo:

// dynamicLayers is an array of Phaser.GameObjects.Group
const { dynamicLayers } = this.mapData;
Object.values(dynamicLayers).forEach((dynamicLayerGroup) => {
    const layers = dynamicLayerGroup.getChildren();
    layers.forEach((layer) => {
        // Check layers that should be hidden/shown on contact
        if (layer.hideOrShowOnContact) {
            // TODO
        }
    });
});

Com esse código pronto, eu preciso criar um gatilho para ocultar / mostrar essa layer, e a melhor maneira de fazer isso é na minha função que cria o mapa e os custom colliders, que eu expliquei em meu devlog #3, e como eu quero criar as fases no Tiled e deixar que o código se vire para criar esses colliders, eu irei criar um loop que passa por todos os tiles da layer para criar colliders em volta da área da layer, como na imagem abaixo.

Layer com colliders
Layer com colliders

Existem vários tutoriais sobre como fazer isso, basta pesquisar JavaScript calcular area 2d array e você verá alguns resultados. Depois de criar esses colliders, vou salvá-los em uma nova propriedade na layer chamada hideOrShowOnContactTriggers para que eu possa usá-los mais tarde na game scene.

dynamicLayer.layer.properties.forEach((property) => {
    const { value, name } = property;
    const customColliders = generateCustomCollidersForAreaOf2DLayer(dynamicLayer);
    dynamicLayer.hideOrShowOnContactTriggers = new GameGroup({
        scene,
        children: customColliders,
        name: dynamicLayer.layer.name,
    });

    // if the layer is supposed to start the game hidden, then set it's alpha to 0
    if (name === 'isHidden') {
        dynamicLayer.setAlpha(0);
    }
});

Com essa lógica pronta, posso voltar ao meu loop principal e iniciar a lógica da colisão com o herói. Primeiro vou pegar todos os tiles da layer que estão na mesma posição que o herói, se o herói estiver realmente em contato com qualquer um desses tiles, isso significa que eu preciso fazer a layer ficar oculta, se o herói não estiver em contato com nenhum dos tiles da layer, então a layer deverá ser exibida.

// dynamicLayers is an array of Phaser.GameObjects.Group
const { dynamicLayers } = this.mapData;
Object.values(dynamicLayers).forEach((dynamicLayerGroup) => {
    const layers = dynamicLayerGroup.getChildren();
    layers.forEach((layer) => {
        // Check layers that should be hidden/shown on contact
        if (layer.hideOrShowOnContact) {
            this.physics.add.overlap(layer.hideOrShowOnContactTriggers, this.player, () => {
                const tiles = layer.getTilesWithinWorldXY(
                    this.player.body.x,
                    this.player.body.y,
                    this.player.body.width,
                    this.player.body.height
                );

                const tile = tiles.find((t) =>
                    t?.index >= 0
                    && t.layer.name === layer.layer.name);

                if (tile) {
                    this.tweens.add({
                        targets: layer,
                        alpha: 0,
                        duration: 300,
                        ease: 'Power2',
                    });
                } else {
                    this.tweens.add({
                        targets: layer,
                        alpha: 1,
                        duration: 300,
                        ease: 'Power2',
                    });
                }
            });
        }
    });
});

É basicamente isso. Você pode ajustar os números do tween de acordo com as suas preferências, mas o resultado final com esses números é muito bom, eu acho.

Ultimamente estou em uma onda de desenvolvimento muito boa, cheio de boas ideias para recursos no jogo, mas estou me esforçando muito para fazer um level design legal e ta difícil. Se você tiver alguma dica de como eu posso melhorar minhas skills de level design, por favor comente nesse post, porque eu realmente estou com dificuldades sinistras.

Até o próximo devlog pessoal!

Tags:


Publicar um comentário

Comentários

Nenhum comentário.