Automatically update the embedded Tileset for all your Tiled maps a Node.js script
Written in October 2, 2021 - 🕒 3 min. readCreating maps with Tiled is great, and you can use them in pretty much any game engine today, including Phaser JS.
To load a map with Phaser JS is simple, first, you load all the assets in the preload
function on your game scene, then you create a new map with this.make.tilemap()
and voilà it is done. Check the following code snippet from 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)
}
But sadly it is not all sunshine and rainbows, in order for that to work, you need your Tiled tileset
to be embedded into the map.
The problem with that is when you have many maps, they will each have their own version of the tileset
, so you make any changes to one of these, all other maps won’t be aware of it.
I actually already talked about this one this blog one year ago, in my Game Devlog #7, but that solution was to programmatically embed an external tileset
into a map, today I will get a tileset
that is already embedded in one map and copy it to all my other maps, let’s see how.
The code
My script will receive a parameter, which is going to be the name of the map where the tileset
needs to be copied from, so the goal is to run the script as node copy-tileset-data.js nameOfTheMapFile
.
First I will import all the needed utils from fs
and declare my maps directory with 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
}
Now for the copyTilesetData
function, I will loop through all my maps folders (maps_dir_1
, maps_dir_2
) and search for the map with the target tileset
, when I found it, I will read the file with readFileSync
and save it’s tilesets
attribute to a local variable to use later.
Then I will make another loop to go to each of the other maps and load the tileset
into them and saving it to the same path using 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)
);
}
}
And that’s it! I hope this script helps you the same way it helps me. See you in the next one!
Tags:
Related posts
Post a comment
Comments
No comments yet.