So LD39 is over and I've got finally time to reflect the struggles I had with understanding how STI handles its data structure two days ago. I've now figured its syntax out to some extent, and actually made some functions that someone else might also find useful, so I'll share them here. I'll also go through STI's data structure in the end - just to recap things for myself.
The first function I did returns a tile instance by its (x,y) coordinates. If the tile's layer index is not specified, layer 1 is used.
Code: Select all
function Map:getInstanceByPixel( x, y , layerindex)
local tilex, tiley = self:convertPixelToTile(x,y)
if not layerindex then layerindex = 1 end
local gid = self.layers[layerindex].data[tiley+1][tilex+1].gid
for i,ins in ipairs(self.tileInstances[gid]) do
if ins.x == x and ins.y == y then
return ins
end
end
end
The second and third functions are special cases of Map:swapTile. The second one removes a tile instance from its tilemap and spritebatch:
Code: Select all
function Map:removeInstance( instance )
if instance.batch then
instance.batch:set(instance.id, 0,0,0,0,0)
end
for i, ins in ipairs(self.tileInstances[instance.gid]) do
if ins.batch == instance.batch and ins.id == instance.id then
table.remove(self.tileInstances[instance.gid], i)
break
end
end
local tilex, tiley = self:convertPixelToTile(instance.x, instance.y)
instance.layer.data[tiley+1][tilex+1] = nil
end
The third function is a bit of a hack. It adds a tile instance to a tilemap and a spritebatch - the position the tile is added to must be empty, this won't work otherwise! I didn't figure out any elegant ways to acquire the correct spritebatch so I'll just loop through them and pick the last one (:D). If the correct spritebatch is known, it can be given as an argument.
Code: Select all
function Map:addInstance( tile, x, y, batch, layerindex)
if not layerindex then layerindex = 1 end
if not batch then
for k,v in pairs(self.layers[layerindex].batches) do
batch = v --lol it's the last one
end
end
id = batch:add(tile.quad, x, y, tile.r, tile.sx, tile.sy)
local instance = {
layer = self.layers[layerindex],
batch = batch,
id = id,
gid = tile.gid,
x = x,
y = y,
r = tile.r,
oy = tile.r ~= 0 and tile.height or 0
}
local tilex, tiley = self:convertPixelToTile(x,y)
self.layers[layerindex].data[tiley+1][tilex+1] = tile
if not self.tileInstances[tile.gid] then self.tileInstances[tile.gid] = {} end
table.insert(self.tileInstances[tile.gid], instance)
return instance
end
If you use bump:
Then it's important to also update bump's world object when adding or removing tiles. With removal it's easy: if you're looping through collisions (cols) then just do this:
Code: Select all
world:remove(cols[i].other)
instance = map:getInstanceByPixel(cols[i].other.x, cols[i].other.y)
map:removeInstance(instance)
But when adding an instance there's more to it:
Code: Select all
instance = scene.map:addInstance(tile, x,y)
local t = {
x = instance.x + scene.map.offsetx,
y = instance.y + scene.map.offsety,
width = scene.map.tilewidth,
height = scene.map.tileheight,
layer = instance.layer,
properties = tile.properties
}
scene.bumpworld:add(t, t.x, t.y, t.width, t.height)
table.insert(scene.map.bump_collidables, t)
So that's it. I hope these are helpful - I wouldn't mind seeing something like this in STI itself! Of course, I don't know if these are unnecessarily complex or redundant in some ways (or even fail in some scenarios I didn't yet witness), so all feedback&critique is welcome. At least they were useful for my LD39 entry!
Also, it was important to realise how tile instances or tiles differ and how they are located. Tile instances are found in
where i is just some iterable number. Tile instances have sprite batches assigned to them: sprite batch is used to draw things, so deleting a tile instance from the aforementioned two locations doesn't change how things are drawn on screen. Sprite batches are located in
Tiles themselves (essentially portions of the tileset) are located in
where id is the tile's id in its tileset and can be read from Tiled. We have to add one to it because tile id=0 is reserved for an empty tile. Tiles can also be accessed by
Code: Select all
map.layers[layerindex].data[tiley+1][tilex+1]
Coordinates tilex and tiley can also be read from Tiled - we have to add one to them, too, because Lua tables start from 1 instead of 0.
EDIT 17.8.2017. Tables in map.layers.data are tiles, not tile instances. Fixed my message accordingly.