BlogJogos

Atualize automaticamente o Tileset embutido do seu mapa no Tiled com um script Node.js.

Escrito em 2 de outubro de 2021 - 🕒 3 min. de leitura

Criar mapas com o Tiled é ótimo e da pra usá-los em praticamente qualquer game engine, incluindo a minha favorita, Phaser JS 😊

Para carregar um mapa com Phaser JS é bem simples, primeiro, você carrega todos os arquivos na função preload, depois cria um novo mapa com a função this.make.tilemap() e pronto. Veja abaixo um snippet de código do blog Ourcade:

preload()
{
    // load the PNG file
    this.load.image('base_tiles', 'assets/base_tiles.png')
    // load the JSON file
    this.load.tilemapTiledJSON('tilemap', 'assets/base_tiles.json')
}

create()
{
    // create the Tilemap
    const map = this.make.tilemap({ key: 'tilemap' })
    // add the tileset image we are using
    const tileset = map.addTilesetImage('standard_tiles', 'base_tiles')
    // create the layers we want in the right order
    map.createStaticLayer('Background', tileset)
}

Mas, infelizmente, nem tudo é perfeito, pois, para que isso funcione, você precisa que o seu tileset esteja embutido no mapa do Tiled.

Embed Tileset
Embedding a tileset on Tiled

O problema com isso é que quando você tem muitos mapas, cada um deles terá sua própria versão do tileset, então caso qualquer alteração seja feita em um deles, todos os outros mapas ficarão desatualizados.

Na verdade, eu já meio que falei sobre isso neste blog há um ano atrás, no meu Game Devlog #7, mas essa solução foi embutir programaticamente um tileset externo em um mapa do Tiled, hoje eu vou pegar um tileset que já está embutido em um mapa e copiá-lo para todos os meus outros mapas, vamos ver como.

O código

Meu script recebera um parâmetro com o nome do mapa de onde o tileset precisa ser copiado, e o objetivo é executar o script usando node copy-tileset-data.js nomeDoArquivoDoMapa.

Primeiro, vou importar todas as funções necessárias do package fs e declarar o meu diretório de mapas com path.resolve:

const { readdirSync, readFileSync, writeFileSync } = require('fs');
const path = require('path');

const TILESETS_PATH = path.resolve(
    __dirname,
    '..',
    'content',
    'assets',
    'sprites',
    'maps'
);

// Call the function with the script parameter
copyTilesetData(process.argv[2]);

async function copyTilesetData(tilesetName = null) {
    // TODO
}

Agora, para a função copyTilesetData, vou percorrer todas as minhas pastas de mapas (maps_dir_1, maps_dir_2, etc) e procurar o mapa com o tileset que quero, e quando achar, vou carregar o arquivo JSON com a função readFileSync e salvar o atributo tilesets em uma variável local para usar mais tarde.

Em seguida farei um loop nos outros mapas para carregar o tileset que salvei antes neles, e em seguida, salvar essas alterações nos arquivos com o writeFileSync.

async function copyTilesetData(tilesetName = null) {
    if (!tilesetName) {
        console.error('No Tileset passed');
        return;
    }

    const allFiles = [];
    let sourceTilesetData = [];
    // eslint-disable-next-line no-restricted-syntax
    for (const tilesetPath of ['maps_dir_1', 'maps_dir_2']) {
        const filePath = path.resolve(TILESETS_PATH, tilesetPath);
        // eslint-disable-next-line no-await-in-loop
        const spritesFiles = readdirSync(filePath);
        // eslint-disable-next-line no-restricted-syntax
        for (const spritesFile of spritesFiles) {
            if (spritesFile === `${tilesetName}.json`) {
                // eslint-disable-next-line no-await-in-loop
                const jsonData = JSON.parse(await readFileSync(path.resolve(filePath, spritesFile)));
                sourceTilesetData = jsonData.tilesets;
            } else {
                allFiles.push(path.resolve(filePath, spritesFile));
            }
        }
    }

    // eslint-disable-next-line no-restricted-syntax
    for (const jsonFile of allFiles) {
        // eslint-disable-next-line no-await-in-loop
        const jsonData = JSON.parse(await readFileSync(
            jsonFile
        ));

        jsonData.tilesets = sourceTilesetData;

        // eslint-disable-next-line no-await-in-loop
        await writeFileSync(
            jsonFile,
            JSON.stringify(jsonData, null, 2)
        );
    }
}

E é isso! Espero que este script ajude bastante você do mesmo jeito que me ajuda. Até a próxima galera!

Tags:


Publicar um comentário

Comentários

Nenhum comentário.