In case anyone stumbles on this thread while searching for "Playdate isometric Tiled loader" here is code I'm using in my game to do that.
This is loading an isometric Tiled map saved as a .tmj
with embedded tilesets. The tileset is created using images named properly for use as an imagetable on the Playdate.
This is a minimal class which doesn't support multiple layers or tilesets. The device also doesn't have the performance to scroll the tilemap very well without a fair amount of extra work which isn't shown here (my game has a whole chunk load/unload system which keeps about 1 screenful of tiles in place at a time and even that just barely hits 30fps).
What's most useful here, I think, is the math in addSprites()
which shows how to do the coordinate calculations.
This isn't a drop-in "just works" thing but it is really small and not too opinionated so it can be a reasonable place to start if someone wants to use an isometric Tiled map. From here one could add things to support various Tiled features as needed.
Note: that the tiled format has some relatively involved mapping from the tile id numbers in the tmj files to tile id numbers in tilesets. However, if you only have one tileset the mapping can be simplified to a -1 operation on the tile id which is what I do here.
import 'CoreLibs/graphics'
import 'CoreLibs/object'
import 'CoreLibs/sprites'
import 'TileType'
local gfx <const> = playdate.graphics
local geo <const> = playdate.geometry
--
-- Tilemap
--
-- Loads Tiled map files (.tmj) and displays them with sprites.
-- Currently only supports iso maps.
--
class('Tilemap').extends(Object)
function Tilemap:init()
end
function Tilemap:load(filename)
self.mapData = json.decodeFile(filename)
assert(self.mapData)
for _, layer in ipairs(self.mapData.layers) do
print('Layer: '..layer.name..' ('..layer.type..')')
end
-- Set this to whatever your tileset name is.
self.tileset = self:getTileset('ground')
print('Tileset: '..self.tileset.name)
local path = 'assets/images/'..self.tileset.name ..'/'..self.tileset.name
print(' '..path)
self.tilesetImages = gfx.imagetable.new(path)
assert(self.tilesetImages, 'Couldn\'t load tileset images')
print(' '..#self.tilesetImages..' images loaded')
self.tileWidth = self.mapData.tilewidth
self.tileHeight = self.mapData.tileheight
self.scaleX = 2
self.scaleY = 1
self:addSprites(self.mapData.layers[1].data, self.mapData.layers[1].width, self.mapData.layers[1].height)
end
function Tilemap:getTileset(name)
for _, tileset in ipairs(self.mapData.tilesets) do
if tileset.name == name then
return tileset
end
end
return nil
end
function Tilemap:getLayer(name)
for _, layer in ipairs(self.mapData.layers) do
if layer.name == name then
return layer
end
end
return nil
end
function Tilemap:addSprites(tiles, width, height)
assert(tiles)
for i = 0, height - 1 do -- Loop through rows
for j = 0, width - 1 do -- Loop through cols in the rows
local tileIndex = (i * width) + j + 1
if tiles[tileIndex] ~= 0 then -- If there is a tile to draw
local x =
(j * (self.tileWidth / 2)) -- The width on rows
- (i * (self.tileWidth / 2)) -- The width on cols
local y =
(i * (math.floor(self.tileHeight / 2))) -- The height on rows
+ (j * (math.floor(self.tileHeight / 2))) -- The width on cols
local tile = tiles[tileIndex] - 1
local image, error = self.tilesetImages:getImage(tile)
assert(image, tile)
local sprite = gfx.sprite.new(image)
assert(sprite)
-- If anything has high Z bump them up to a higher layer so that overlapping works properly.
-- I'm using an enum but you could just keep a table of wall-like tiles.
local z = 0
if tile == TileType.Wall or
tile == TileType.Spikes or
tile == TileType.Bumper then
z = 1
end
-- Your player character and other moving objects should be at the same layer as the walls. You will need to set their Z index every time they move using a call similar to the one below.
-- The `setCenter()` offset here accounts for the size of tile graphics.
-- We want the origin to be right at the top center pixel of a ground tile.
-- You will likely need to adjust this based on the visual "thickness" of your tiles.
sprite:setCenter(0.5, 0.393939)
sprite:moveTo(x, y)
sprite:setZIndex(y + 34 + z * 1000) -- `The setZindex()` call also takes into account the tile graphic height.
sprite:add()
end
end
end
end