First post on the forum for this PICO-8 newbie.
I'm trying to find a way to store data in a 3D array and it seems to work by itself but when I try to implement it in more advanced code I start getting errors about referencing a nil value. I got the idea from this reddit thread and thought if I wanted to have multiple variables stored for each point on the grid I could just add a 3rd array. This is what I ended up with:
pixelgrid = {} for row = 1, 43 do pixelgrid[row] = {} for column = 1, 43 do pixelgrid[row][column] = {} for zpos = 1, 3 do pixelgrid[row][column][zpos] = 0 end end end |
But then if I try to edit a value like so:
for x = left, left + discsize do for y = top, top + discsize do pixelgrid[x][y][1] = px pixelgrid[x][y][2] = py pixelgrid[x][y][3] = magsq end end |
I get an error indicating that pixelgrid[x][y][1] wasn't initliased.
So I'm just wondering have I got this whole 3D array idea wrong or is it more likely something is going wrong elsewhere in the code that is causing my x and y values to not line up with the array and "miss" the variables I defined?
Hope this helps:
-- multidimensional arrays
a = {{1,2,3}, {4,5,6}, {7,8,9}}
-- print part of array
print (a[1][1])
-- print at screen location
print (a[1][1],10,10)
-- return cursor
cursor (0,20)
-- print all of array
for y = 1, 3 do
for x = 1, 3 do
print(a[y][x])
end
end
-- determine size
print (#a)
-- for additional information see - http://blog.headchant.com/arrays-in-pico-8/
My guess is that you're accidentally accessing outside of the valid range.
Try this:
for x = left, left + discsize do for y = top, top + discsize do _x=x _y=y pixelgrid[x][y][1] = px pixelgrid[x][y][2] = py pixelgrid[x][y][3] = magsq end end |
When it crashes, print the debug globals _x and _y at the command prompt and see if they're outside 1..43.
As an aside, why exactly are you creating this array? Like, what's the purpose? Sometimes that helps us readers offer ideas for alternate methods that may work better, if there are any.
Thanks for the quick responses!
I think I overcomplicated the question. Just to be clear I'm not trying to create 3D for graphics, I only want to store more than one variable in each cell of my 2D array so I assumed a third array would achieve this.
Let's say I have a grid of data (my 2D array) that I can look up by x and y. If I call grid[x][y] I will get the value of that cell on the grid but I also want to be able to specify a third argument like grid[x][y][z] to choose from mutiple values stored in that cell on the grid. So x = which column the data is on, y = which row the data is on and z = which variable I want to retrieve from that cell.
Am I making this difficult for myself? Should I just create a seperate 2D array for each variable instead?
@Felice I'm not at my computer right now but I'll post the specific example when I get home.
No, what you're doing totally makes sense.
All you're really doing anyway is creating tables whose members are also tables. You can do this all the way down to an out-of-memory error--there's no limit.
Sounds like what you're wanting is a 2D array of tables with values related to the 2D position. What you have is basically that, though I think the last layer might be better off using named members than numbered ones:
pixelgrid = {} for row = 1, 43 do pixelgrid[row] = {} for column = 1, 43 do pixelgrid[row][column] = { px = 0, py = 0, magsq = 0 } end end -- to set up an entry specifically pixelgrid[x][y].px = px pixelgrid[x][y].py = py pixelgrid[x][y].magsq = magsq |
It's just a matter of aesthetics/readability though.
What are the values you are using for left and discsize? Is the left variable 1-based like the array? Is discsize bigger than 43?
Not able to check at the moment but I think if the x dimension of the table hasn't been initialised, then regardless of whether y and z are in range it won't work? Then same applies if y isn't initialised and you try to access z. Hope that makes sense.
So I was trying to use the code I found here in PICO-8 and I got it working with a spinning globe but the CPU was at 96%. There was a lot of math that was being done multiple times in the same step despite the result not changing so I was trying to initialise these values at boot and just read them from memory instead of doing the math every time. For each x,y location I needed 3 variables; px, py and magsq. The original code without the array is here:
function _init() left = 42 top = 42 discsize = 43 t = 0 rotationspeed = .5 mapheight = 32 end function _draw() cls() t+=1 for x = left, left + discsize do for y = top, top + discsize do px = (x - left) * 2/discsize - 1 py = (y - top) * 2/discsize - 1 magsq = px * px + py * py scale = 0.35 * magsq + (1 - 0.35) px = px * scale py = py * scale u = t * rotationspeed + (px + 1) * (mapheight/2) v = (py + 1) * (mapheight/2) u = u % (2 * mapheight) color = sget(u, v) pset(x, y, color) if ( magsq > 1 ) then pset(x, y, 0) end end end print("cpu = "..stat(1),8,8,7) end |
And after using Felice's debugging method I found my that my code was accessing a cell just outside of the range I defined. So I added one more cell to each axis of the array and got it all working the way I wanted. Here is the end result:
function _init() left = 42 top = 42 discsize = 43 t = 0 rotationspeed = .5 mapheight = 32 pixelgrid = {} for row = 1, 44 do pixelgrid[row] = {} for column = 1, 44 do pixelgrid[row][column] = { px = 0, py = 0, magsq = 0 } end end for x = left, left + discsize do for y = top, top + discsize do px = (x - left) * 2/discsize - 1 py = (y - top) * 2/discsize - 1 magsq = px * px + py * py scale = 0.35 * magsq + (1 - 0.35) px = px * scale py = py * scale _x = x - left + 1 _y = y - top + 1 pixelgrid[x-left+1][y-top+1].px = px pixelgrid[x-left+1][y-top+1].py = py pixelgrid[x-left+1][y-top+1].magsq = magsq end end end function _draw() cls() t+=1 for x = left, left + discsize do for y = top, top + discsize do u = t * rotationspeed + (pixelgrid[x-left+1][y-top+1].px + 1) * (mapheight/2) v = (pixelgrid[x-left+1][y-top+1].py + 1) * (mapheight/2) u = u % (2 * mapheight) color = sget(u, v) pset(x, y, color) if ( pixelgrid[x-left+1][y-top+1].magsq > 1 ) then pset(x, y, 0) end end end print("cpu = "..stat(1),8,8,7) end |
Unfortunately not as much of a performance boost as I was hoping for but at least I know I wasn't barking up the wrong tree by nesting arrays in arrays. I'm sure that will come in handy again later.
you have a lot of redundant dereferencing.
using locals should provide a little boost:
for x = left, left + discsize do local pixelgrid_x=pixelgrid[x-left+1] for y = top, top + discsize do local pixelgrid_xy=pixelgrid_x[y-top+1] u = t * rotationspeed + (pixelgrid_xy.px + 1) * (mapheight/2) v = (pixelgrid_xy.py + 1) * (mapheight/2) u = u % (2 * mapheight) color = sget(u, v) pset(x, y, color) if (pixelgrid_xy.magsq > 1 ) then pset(x, y, 0) end end end |
@ultrabrite: Thanks for the tip!
With that optimization the CPU dropped all the way down to 54% but I don't fully understand how it works...
What exactly do you mean by redundant dereferencing? Does that just mean that I'm accessing global variables more often than I need to?
Correct me if I'm wrong but it looks like you are copying the 3D array into two local variables so pixelgrid_x contains the y array for the current x coordinate and pixelgrid_xy contains the variables for the current x,y coordinates. My guess is that the boost in performance is coming from the fact that we are only referencing the global variable once per row and once per column as opposed to looking it up every time we move to the next cell.
Now that I've typed that out I feel like I might have answered my own question but I'm still keen to see if my guess is correct :P
Another related question though... Can anyone see anything else that can be moved out of the _update function? I'm wondering if I might be able to create another 3D array for variables with predictable outcomes but at first glance it seems like the remaining code in _update needs to be updated constantly due to the fact that the rotation is changing the sprite lookup coordinates on every frame. Thoughts?
you got that right! plus there's no copying, pixelgrid_x is a reference/pointer to the current y-indexed table.
the next thing to do is move constant calculation out of the loops.
for instance:
local half_mapheight=mapheight/2 local uu = t * rotationspeed + half_mapheight for x... for y... ... u = uu + pixelgrid_xy.px*half_mapheight ... end end |
Nice demo effect!
You are already doing a good deal of pre-calculation, but you can squeeze some more:
- use locals as much as possible (slight perf boost)
- look at each computation in your inner loop and see if it can moved outside (eg. mapheight*/2)
By applying these simple rules, I am now down to 22%.
@ultrabrite: dang, missed the time*speed constant!!
(at least, we agree on the recommendations :) )
[Please log in to post a comment]