Introduction
I'm trying to implement my "map to table" conversion as proposed in my previous forum post. To this end I created two functions: one for iterating over the map and storing it in a multi-dimensional table (generate_world()) and one for iterating over that table and placing the sprites (build_world()).
Problem
I get it to nicely iterate through the map, print out the tiles it finds and (presumably) store it in a table. However, when iterating over the table things get awry. I do get it to iterate over the first dimension (when printing it returns [table]), but when iterating over the second dimension it's empty (doesn't print anything).
All help would be greatly appreciated!
Code
function generate_world() world={} t_x=0 t_y=0 max_x=48 max_y=16 print "generating world ..." for x=0,max_x do world[x]={} for y=0,max_y do sp=mget(x,y) if sp != 0 then print("tile: "..x..","..y..": "..sp) world[x][y] = sp end end end print "done" end function build_world() for x in all(world) do for y in all(x) do print(y) -- this actually never gets executed end end |
Alright, I figured that the problem is nil values due to skipping tiles with sprite value 0. However, that got me even more lost as I'm now doubting whether this (multi-dimensional table) implementation can ever work.
So let me start over with a more generic question: how to actually achieve this? All pointers are very welcome.
Got it, I was over-complicating things. This is what worked for me:
tile={ x=x, y=y, sp=sp } add(world,tile) |
So I end up with a simpler table and just iterate over all elements.
Remember that Lua tables start at 1, not 0. When populating your table in the first post, you started at index 0. However, when you use all() later, it will start at index 1, skipping the first tile. In addition, all() iterates over a table's sequence (the "array" part), which ends when it encounters a nil value. Sequences are always consecutive, with no "nil holes".
The name of the function all() is a bit of a misnomer, as it doesn't iterate over all elements in the table, just in the sequence. To actually iterate over all elements in the table (although not necessarily in order; consider it as a hash table/dictionary), use pairs().
https://pico-8.wikia.com/wiki/All
https://pico-8.wikia.com/wiki/Pairs
Thanks, I missed that completely (tables starting at 1)!
My code works, but it feels horribly inefficient because every time I want to change a sprite I for-loop over the whole table:
if (item == 32 or item == 48 or item == 49) then for tile in all(world) do if (tile.x == x and tile.y == y) then tile.sp = 64 end end end |
Note that x and y are passed into the function from my collision detection.
Is there any way I could just look these up based on the index? I tried the suggested approach in https://blog.headchant.com/arrays-in-pico-8/ (index using concatenation of x and y), but I couldn't look up elements that way either.
Instead of actually concatenating the coordinates to a string, you can of course use arithmetic. See these examples for implementation:
- https://www.lua.org/pil/11.2.html
- https://stackoverflow.com/questions/1817631/iterating-one-dimension-array-as-two-dimension-array
That said, how exactly did using the concatenation of the x and y coordinates as the key/index not work? You should be able to look up elements that way, like it says in the blog post. However, as I mentioned before, you will not be able to iterate over a table using strings as keys/indices with all(), since that only iterates over the sequence/array part of the table. But like the blog post says, you can iterate over a table like that using pairs(), although then order is not guaranteed. (If you know Python already, for example, it's like iterating over a list vs. a dict.)
However, you went from having a two-dimensional table earlier, where you could look a tile up with x and y as indices in the respective tables/dimensions. Can't you just go back to doing that now that you know you should start at index 1 instead of 0? Then you can iterate in order, and also look up with world[x][y] (row-major order). That seems the easiest to me. Just remember that they start at 1. So then your original code can be used again (see my comments):
function generate_world() world={} t_x=0 t_y=0 max_x=48 max_y=16 print "generating world ..." for x=1,max_x do world[x]={} for y=1,max_y do sp=mget(x-1,y-1) -- the map is 0-indexed... if sp != 0 then print("tile: ".. x-1 ..",".. y-1 ..": "..sp) world[x][y] = sp end end end end function build_world() for x in all(world) do for y in all(x) do print(y) -- this should now be executed end end end |
Just noticed I didn't respond here anymore, sorry for that. You're right: the only problem I had was that I missed that Lua tables start at 1 instead of 0. Works like a charm now :)
[Please log in to post a comment]