How to use a .js exported map with vanilla javascript

I’m new to game development and the docs are way too hard to understand. I just want to know how to go from exporting a map as a .js file and using it as a map in javascript game. The experted file is pretty hard to decipher no matter how basic I make the map and I’ve yet to be able to have it show up as a background. I feel like you have a pretty powerful tool here but the documentation is really not digestible and there aren’t really a lot of other resources out there for someone to learn how to import the code into their game. I’m interested in learning how to use a JSON file also, but honestly I’d like to be able to grasp both implementations. I don’t really know what I’m expecting in terms of an answer here, but could someone like maybe point me in the right direction for even beginning to try and understand what is going on? Would be much appreciated.

My goal is to be able to use tiled to set up collision detection but at this point I’m just taking it one step at a time.

Unless you specifically need to use JS (e.g. you’re running a system that doesn’t have a JSON library, or using an engine designed around the Tiled JS format), I think you should use JSON instead, and not worry about the JS format at all. JSON is easier to read and just as easy to parse. You get the same data from both formats (as far as I can tell), but with JSON you get an object you can process when you’d like, whereas with JS you get a function that needs to be executed and which adds the map data to a predetermined list.

The TMX documentation should have most of what you need. The property names and general structure and most of the possible values are the same between TMX and JSON.

Beyond that, where specifically are you getting stuck? The basic approach for just getting data needed to render your tile layers is:

Loop over every tileset in the tilesets array and store the important data somewhere accessible. For better performance, store tileset data somewhere where you can access it again when you load a different map, as tileset data doesn’t change between maps, only how the tilesets are used changes.

  • Store the firstgid for each tileset, it’ll be important. This is a map-specific property, so store something like a list of {tilesetReference, firstgid}, where tilesetReference refers to a Tileset object that has the tileset’s data. For ease, I highly recommend making sure this list is sorted in ascending order by firstgid, as you’ll be looking up the firstgids of tilesets a lot.
  • For the simple case, let’s assume you’re using image-based tilesets. From the tileset, the data you need for rendering is
    • tileWidth
    • tileHeight
    • spacing (may be absent or 0)
    • margins (may be absent or 0)
    • image source (URL of the image).
    • image width
    • image height
  • Once you have all those properties, you can calculate where in the image any given tile is based on its local tile ID. You don’t even need to load the image at this point, you only need it when you’re ready to render.

Now that you have your tileset data ready, you need some important map data:

  • mapWidth, the width of the map in tiles
  • mapHeight, the height of the map in tiles
  • tileWidth, the width of the cells in the map. This map tile size can differ from the tileset tile size in some instances.
  • tileHeight, the height of the cells in the map.

Based on these pieces of information, you can figure out where on the map a given tile is just based on its index into the list of tiles.

Loop over every item in the layers array. For each:

  • get either the layer id or the index in the array to use as a unique layer ID
  • check the encoding:
    • If it’s “csv”, then you can ignore compression, the data will not be compressed, it’ll just be CSV. Parse the CSV string into a list of numbers. This will be a list of unsigned global tile IDs.
    • If it’s “base64”, you’ll need to use a base64 library to decode the data. The result will be some binary data, put it into an ArrayBuffer, which is JavaScript’s way of storing binary data. It may or may not be compressed, so look at the compression field.
      • If it’s “gzip”, “zlib”, or “zstd”, use the appropriate library to decompress the data. The result will be an ArrayBuffer or ByteArray (depending on the library you use), which needs to be parsed as an array of unsigned 32-bit integers. You can do this by by making a Uint32Array from it, var tileIDs = Uint32Array(uncompressedArrayBuffer);
      • If it’s empty, the data isn’t compressed, it’s already just an ArrayBuffer of ints, so interpret it as 32-bit unsigned integers: var tileIDs = Uint32Array(uncompressedArrayBuffer);
  • Whatever the encoding and compression were, you now have an array of global tile IDs.
  • When you’re ready to render (or rather, generate your renderable object, you want to prepare it ahead of time instead of doing all this tilework every frame), loop through the list of global tile IDs and process each one:
    • A global tile ID, in addition to the actual ID, contains several bit-flags relating to the flipping and rotation of the tile, and you have to extract those first. This is the most confusing part for most people. These are the flags:
      • horizontal flip. Extract with hflip = gid & 0x80000000;
      • vertical flip. Extract with vflip = gid & 0x40000000;
      • diagonal flip. Extract with dflip = gid & 0x20000000; (for hex maps, this is actually a rotate 60 degrees flag)
      • (for hex maps only) rotate 120 degrees flag. Extract with hexRotate120 = gid & 0x10000000;
    • After you’ve saved these flags somewhere (they can just be temporary variables while you set up your renderable), clear them from the global tile ID: gid = gid & ~(0x80000000 | 0x40000000 | 0x20000000 | 0x10000000).
    • Your global tile ID now contains only the actual global tile ID. You now need to figure out what tileset it’s for. Iterate through your list of tilesets for this map, and find the tileset with the largest firstgid that’s less than or equal to this global tile ID (this is why having the list sorted by firstgid is helpful!). That’ll be the tileset this tile belongs to.
    • Subtract the firstgid from the global tile ID to get the tileset-local tile ID. As mentioned before, from the local tile ID and the tileset’s data, you can figure out what region of the tileset image it uses. From the tile’s index into the array of tiles and the map’s mapWidth and tileHeight and tileWidth, you can figure out where in the map this tile is located. Load the tileset image if you haven’t already and add this tile to your renderable object.

Once you’ve done this for every tile in every layer, you should have your renderables all ready.
I skipped over a lot of things in this, like layer opacity, rendering offsets, objects, etc, but hopefully this is enough to get you started.

1 Like

I appreciate the time you put into this but I don’t understand any of it. I have not even been able to access this data as the .js file seems to be a private function that I can’t access externally. Maybe I can start to digest some of the other stuff you went over once I manage to do that.

As I wrote in my post, I recommend ignoring the JS format entirely and using JSON instead.

The JS file is meant to be executed directly I believe, rather than parsed manually. It’s not a private function but a closure, when you execute the file, it runs and adds the tile map data to a listing of them.

Ok thank you that makes sense. I’ll have to do some more tinkering.

The reason I want to learn to use the .js version because I want to understand it, even if I don’t end up using it. But again, I appreciate your advice.

The .js version of the JSON export is pretty much an outdated way of conveniently including the data in a website. By putting the data in a closure it was possible to directly reference the .js file from an HTML file and then assume the data was available in subsequent scripts, with no need to meddle with XMLHttpRequest or JSON.parse. You should really prefer the plain JSON export.

1 Like