codinggames

Importando arquivo de tileset externo no Phaser JS com Webpack - Skate Platformer Game Devlog #07

Escrito em 8 de outubro de 2020 - 🕒 4 min. de leitura

Super Ollie vs Pebble Corp.
Super Ollie vs Pebble Corp.

No Game Devlog de hoje, vou mostrar como importar tilesets externos no Phaser JS, mas primeiro vamos tirar isso do caminho: Esqueça o nome "Skate Platformer", o nome do nosso jogo vai ser Super Ollie vs Pebble Corp. A gente não planejava dar um nome ao jogo agora, mas a ideia simplesmente veio nas nossas cabeças e achamos que o nome é perfeito. Deixa eu te perguntar uma coisa, qual é o pior inimigo do skatista? Indiscutivelmente essas pequenas pedrinhas, certo? E se no futuro houver uma empresa do mal que quer proibir o skate em todos os lugares, como essa empresa se chamaria? Pebble Corp, é claro. Pebble é o nome em inglês daquelas pedrinhas que geralmente você encontra em praias e tal. Agora com isso fora do caminho, vamos continuar com o post.

Importando tilesets externos

Tiled é uma ferramenta open source poderosa para construir mapas 2D suportados por muitas engines de jogos diferentes, incluindo Phaser JS, que é o framework que eu estou usando para esse jogo.

Há um pequeno problema, ao carregar mapas do Tiled no Phaser JS, o tileset precisa estar “embutido” no mapa, caso contrário, o Phaser JS não consegue carregá-lo. Isso é muito frustrante porque sempre que eu faço alterações em um tileset, eu tenho que ir em todos os meus arquivos de mapa e “reembutir” o tileset, isso acaba demorando muito se o projeto tiver muitos mapas e tilesets diferentes.

Embutir Tileset
Embutindo um tileset no Tiled

Para resolver este problema, decidi encontrar uma solução para embutir automaticamente os tilesets com o Webpack. Para fazer isso, comparei o arquivo JSON do mesmo mapa antes e depois de embutir um tileset, e ficou assim:

Tileset embutido vs não embutido
Tileset embutido vs não embutido

Como você pode ver, em vez de ter a propriedade source apontando para um arquivo externo, tipo "source": "../tilesets/test_tileset.json", ele contém todo o conteúdo desse arquivo mais um atributo firstgid, então tudo o que eu preciso fazer é percorrer todos os mapas do Tiled, ler a propriedade tilesets, carregar cada arquivo tileset e colocar o seu conteúdo em uma nova array na propriedade tilesets.

Foi assim que eu fiz:

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 copyWebpackPluginPatterns = [];
stageJsons.forEach((stage) => {
    copyWebpackPluginPatterns.push({
        from: `${STAGES_PATH}/${stage}`,
        to: '../assets/stages',
        transform: (fileBuffer, elPath) => {
            const manifestString = fileBuffer.toString();
            const stageData = JSON.parse(manifestString);
            const newTilesets = stageData.tilesets.map((tileset) => {
                // firstgid is an important id reference for Tiled
                const { firstgid } = tileset;
                if (!tileset.source) {
                    // if there's no source, it means the Tileset
                    // is already embedded
                    return tileset;
                }

                // on my setup, stages are in root/stages/
                // and the tilesets are in root/tilesets/
                // so we need to remove "../tilesets/" from the path
                // and load it based on the root dir of the tilesets
                const filePath = tileset.source.replace('../tilesets/', '');
                // eslint-disable-next-line import/no-dynamic-require
                const tilesetData = require(`${TILESETS_PATH}/${filePath}`);
                if (!tilesetData) {
                    console.error('Could not find file', `${TILESETS_PATH}/${filePath}`);
                    return tileset;
                }

                return {
                    ...tilesetData,
                    firstgid,
                };
            });
            stageData.tilesets = newTilesets;

            return Buffer.from(JSON.stringify(stageData));
        },
    });
});

module.exports = {
    plugins: [
        new CopyWebpackPlugin({
            patterns: [
                ...copyWebpackPluginPatterns,
            ],
        }),
    ],
};

Como você pode ver, precisamos do plugin copy-webpack-plugin para fazê-lo funcionar, copiando todos os mapas do diretório assets/stages para o diretório dist/assets/stages, e ao copiá-lo, definimos uma função transform que carregará os tilesets externos e os embutirá nos mapas Tiled. Adicione este código ao seu arquivo de configuração do Webpack se quiser obter o mesmo resultado.

Criando a tela inicial

Isso foi realmente muito fácil e direto, especialmente porque é praticamente o mesmo código do Game Devlog anterior.

Primeiro eu carrego o mapa na função create:

const map = scene.make.tilemap({ key: 'title_screen' });
const tileset = map.addTilesetImage('title_screen', 'city_tileset');
const { width, height } = this.cameras.main;

const background = map.createDynamicLayer('background', tileset, 0, 0);
const ground = map.createDynamicLayer('ground', tileset, 0, 0);
const foreground = map.createDynamicLayer('foreground', tileset, 0, 0);

[background, ground, foreground]
    .forEach((dynamicLayer) => {
        dynamicLayer.setY(
            height - background.height
        );

        // set this property to make the the layers move in different speeds
        dynamicLayer.setScrollFactor(dynamicLayer.parallaxSpeedX);
    });

// we're going to use that in the update function
this.parallaxCounter = 0;

Agora tudo o que fiz foi mover a câmera até a posição y = 640 e, quando ela chegar lá, mudar a posição de volta paray = 0, e repetir isso indefinidamente.

this.parallaxCounter += 1;
if (this.parallaxCounter === 640) {
    this.parallaxCounter = 0;
}

this.cameras.main.scrollX = this.parallaxCounter;

E por hoje é isso! Espero que tenha gostado, se quiser, deixe um comentário e considere se inscrever no meu canal no YouTube.

Tags:


Publicar um comentário

Comentários

Nenhum comentário.