Descendo de plataformas com Arcade Physics do Phaser JS - Skate Platformer Game Devlog #06

Escrito em October 1, 2020 - Jogos

Fala pessoal! O Game Devlog dessa semana é um pouco longo, 15 minutos, mas vale a pena assistir, pois, tem muitas coisas interessantes sobre o jogo.

Seleção de Fases

Para facilitar o teste de novas fases, eu decidi adicionar uma tela de seleção de estágio básica, para que eu pudesse testar várias fases no mesmo build ao invés de ficar alterando o mesmo arquivo de fase toda a hora. Agora, sempre que adiciono um novo arquivo de estágio à pasta de fases, essa fase será automaticamente incluída no bundle do jogo, graças a algumas configurações personalizadas no Webpack.

const CopyWebpackPlugin = require('copy-webpack-plugin');
const { promises: fs } = require('fs');
const stageFiles = await fs.readdir(STAGES_PATH);
// get only the JSON files
const stageJsons = stageFiles
    .filter((stage) => stage.split('.')[1] === 'json');
const GAME_STAGES = JSON.stringify(
    stageJsons.map((stage) => {
        // eslint-disable-next-line import/no-dynamic-require
        const stageData = require(`${STAGES_PATH}/${stage}`);
        const stageName = getStageNameFromMap(stageData);

        return {
            stageName,
            stageKey: stage.split('.')[0],
        };
    })
);

module.exports = {
    plugins: [
        new webpack.DefinePlugin({
            GAME_STAGES,
        }),
    ],
};

Na scene de seleção de fases, você pode acessar os dados de fases na variável GAME_STAGES, então eu faço um loop por esses dados das fases e crio um seletor de fases.

let posX = 20;
let posY = 50;
GAME_STAGES.forEach((stageData, index) => {
    const { stageKey, stageName } = stageData;
    const stageSelector = new StageSelector({
        scene: this,
        x: posX,
        y: posY,
        stageKey,
        stageName,
        onClickCallback: (data) => {
            this.scene.start('GameScene', data);
        },
    });

    this.add.text(
        posX + 8,
        posY + 8,
        `${index + 1}`.padStart(2, '0')
    ).setDepth(50);

    this.add.existing(stageSelector);
    stageSelectors.add(stageSelector);

    posX += 52;
    if ((index + 1) % 7 === 0) {
        posY += 52;
        posX = 20;
    }
});

Parallax Effect

O jogo não estaria completo sem um bom parallax effect, e para conseguir isso eu decidi adicionar algumas propriedades personalizadas às layers do Tiled e acesso elas na função update da game scene.

update(time, delta) {
    const { dynamicLayers } = this.mapData;
    Object.values(dynamicLayers).forEach((dynamicLayer) => {
        dynamicLayer.layer.properties.forEach((property) => {
            const { name, value } = property;
            if (value !== 1) {
                if (name === 'parallaxSpeedX') {
                    dynamicLayer.setX(
                        this.cameras.main.scrollX * value
                    );
                } else if (name === 'parallaxSpeedY') {
                    dynamicLayer.setY(
                        this.cameras.main.scrollY * value
                    );
                }
            }
        });
    });
}

Obs: Depois de escrever esse post, eu descobri a função setScrollFactor e com ela é possível definir um atributo direto na layer ao invés de fazer um loop na função update.

Descendo de Plataformas

Para fazer isso eu pesquisei um pouco, e acabei fazendo de um jeito que é meio que uma gambiarra, mas funciona por enquanto, então decidi seguir em frente.

Primeiro eu verifico se o hero está no chão, se há um elemento encostando no hero por baixo e se o botão para baixo está sendo pressionado. Caso positivo, desabilitamos a colisão do baixo do hero por 150 milissegundos.

// Handle hero going down through element
if (
    this.isHeroOnGround()
    && this.isDownJustDown()
    && this.touchingDownObject
) {
    this.body.checkCollision.down = false;
    this.scene.time.delayedCall(
        150,
        () => {
            this.body.checkCollision.down = true;
        }
    );
}

Isso é bastante simples, mas como podemos acessar o atributo this.touchingDownObject? Bom, essa é a maior parte da gambiarra dessa solução. Primeiro, precisamos adicionar um collider entre o hero e a layer do mapa com uma função de callback que irá verificar algumas coisas e definir o atributo this.touchingDownObject para o hero.

this.physics.add.collider(
    dynamicLayers.ground,
    this.hero,
    (objectA, objectB) => {
        const [hero, element] = this.getHeroAndObject(objectA, objectB);

        if (
            hero.isHeroOnGround()
            && element.properties.collidesUp
            && element.properties.canFallThrough
        ) {
            hero.touchingDownObject = element; // TODO?
        } else {
            hero.touchingDownObject = null;
        }
    }
);

E é isso aí! Espero que tenham gostado e não esqueçam de deixar um comentário se acharam post útil.

Tags:


Publicar um comentário

Comentários

Nenhum comentário.