Log In  


Hello there!

I have been exploring Pico8 for over 2 years now trying to understand coding with let's say "some" progress.

But I'm stuck with Tables. I mean, I get the concept but there's something I haven't got to fully understand therefore I get mixed results.

So I created actors (player, enemy) as tables but I can't make them spawn several instances on screen. So far I have got to draw 5 enemies on stage (at random position) but they keep appearing all the time (where they should appear once), any light about what I'm doing wrong? here's the code:

--init
function _init()
--globals
wave=5
w=112
h=112

--player
pyr={
spt=1,
x=w/3,
y=h/3,
life=10,
spd=2,
flp=false,
idle=true,
}
enms={} --enemies

enm={ --single enemy

spt=10,
life=5,
spd=1,
flp=false,
idle=true,
draw=function()
spr(enm.spt,flr(rnd(w)),flr(rnd(h)))
end,
}

for a=1,wave do
add(enms,enm)
end

end

--draw
function _draw()
cls()
print(#enms,20,20,7) --checking created instances from enms table
foreach(enm,enm:draw())
end


I have read tutorials, videos and everything available but still I haven't figured it out yet :( Any help is much appreciated.



When you assign a table to a variable, or add it to a list, you create a reference to the table in memory. In your code, enm={...} creates a table, then assigns a reference to that table to the enm variable. add(enms,enm) then takes the reference to the table stored in enm and adds it to the enms list. It does not make a copy of the table itself, it just makes a copy of the reference. So your enms table actually ends up with 5 references (from the 5 waves) to the same table.

If you want to create five separate enemy tables, one way is to create a function that makes and returns the enemy table, then call that function five times:

function make_enemy()
  return {
    spt=10,
    life=5,
    spd=1,
    flp=false,
    idle=true,
    draw=function()
      spr(self.spt,flr(rnd(w)),flr(rnd(h)))
    end,
  }
end

for a=1,wave do
  add(enms,make_enemy())
end

One way to think of it is the {} braces are what create the table, and everything else just refers to the table. Because the function call evaluates a new pair of {} braces each time, it's actually creating a new table and returning it. In the previous version, the {} braces were only encountered once, so it only made one table.

Note that I had to make a change to the draw() function to use "self" to refer to the enemy being drawn. This is a special name that is set to the current enemy when you use the enm:draw() syntax. That's how draw() knows which table it belongs to.

This pattern can be improved so that it doesn't create a new copy of the draw() method for each enemy table, to save on memory. This involves a concept called prototypical inheritance, and uses a Lua feature called metatables. It's not that complicated but you can ignore this for now and come back to it if you find you need it.


P.S. With draw() as a method like this, your foreach() will need to be something like:

foreach(enms, function(enm) enm:draw() end)

foreach() takes a table and a function, and the function needs to be a regular function that takes the element as its argument, not a method of the element. In this version, I create an anonymous function for this purpose.

You might prefer making draw_enemy(enm) a regular function instead of a method, then just do:

foreach(enms, draw_enemy)

Awesome, thanks! I'll follow your explanation in detail and will come back :)


Hello again!

I got to finally handle the tables' nature (sort of speak) so I've doing a lot of progress, thanks for the help!

Now I have just a little question, I need to print the x position of one of the enemies that I created from the table/wave method but I only get the "NIL value" error message. My question is if it's possible to get such value? I'm using something like:

IF (BLT.X>ENM.X) THEN DEL (BLT,SELF) END

The overall goal is to compare the bullet's x position with enemy's x position to set a collision and destroy both.

Thanks in advance!



[Please log in to post a comment]