This tutorial looks at how to create objects such as bullets and particles using a table. The objects can look after their own update and draw functions and can be created easily.
I have been dabbling around with Pico-8 for a while now. Never made any games really. Just trying to use it as a learning tool for programming concepts. And I could never wrap my head around how to use tables. Especially for "instancing" objects like projectiles or other things. And this REALLY gave me the ah-ha! Moment I needed. Thank you so much for this short, yet very concise video about that topic. Now I just need to understand collisions and using them to affect objects.
These are exactly the types of videos I needed when I started out with Pico-8! I really like the approach of isolated topics within each video, and aside from that, you are clear and concise. There is a lack of this kind of tutorial content for Pico-8 on TH-cam and hopefully your channel will fill that niche. Great stuff!
incase anyone else is confused with what "b" is about: i.e. for b in all(bullets) do... Basically this is saying for every item in the table 'bullets', assign the items of this table to a variable we defined as b, and then do the following to it ( i.e. b:update() etc....). In other words you can rename 'b' to anything u want. So this might not work as intended if you store other other objects like 'health' or whatever in the same table, since everything item with a function named update() gets called.
Thanks a lot for this! For one like me, who comes from procedural BASIC-Programming, the concept of tables was hard to grasp. I mean, in PureBasic I do have stuff like Structs and Linked Lists, and I know how to manipulate them, but this was a different beast altogether. After watching your tutorial, though, it became a lot clearer.
Amazing tutorial, as always. I was coming into this thinking I'd know everything already, but I was entirely unprepared for being able to put FUNCTIONS into objects. Not that I'm an expert, but I had read the documentation and, as far as I could tell, it makes no mention of this function-ality (ha). I'm going to have to revamp a couple of my projects to use this knowledge now.
Ah, and now the state video (later in the sequence) makes more sense, functions and code as variables - a bit like closures, really - thanks! I should have watched these very recent ones in strict order :-) Great explanation, by the way, very easy to follow and grasp.
--Bullet timer: h=0 bullet_timer = false if btn(5) then if h == 0 then bullet_timer=true add_new_bullet() end function _update() bullet_delay() ... end function bullet_delay() if bullet_timer then h+=1 if h>8 then h=0 bullet_timer=false end end end So basically a boolean is used as a switch here (bullet_timer). the contents of the function bullet_delay become active when this switch is turned to true. What the function then does is it increases h over time (can play with the values as u like). Once h hits a certain number, we set h to 0 and turn the h timer off. Then it allows btn(5) when pressed to activate add_new_bullet once again cause h =0 like it was originally in init before any bullets were first added.
Hi. Not really. BTNP reports TRUE only for the next frame after the button has not been pressed, so it reports FALSE for a button hold. You'd need to mash the button to have it use BTNP.
@doc_robs thank you for the wonderful tutorial series! However, I do have a question regarding collision. I have followed both this tutorial and the fget mget collision tutorial. I have attempted to combine the two but when I try to return the X, Y, W, H to the collision i am receiving runtime error at IF HIT(X+DX,Y,W,H) THEN. "ATTEMPT TO PERFORM ARITHMETIC ON GLOBAL 'X' (A NIL VALUE)". Is this because the object BULLET has no way to index values? Any help would be greatly appreciated. Cheers!
function _init() x=63 y=63 dx=0 dy=0 w=1 h=1 bullet={} end function _update() if btnp(🅾️) then add_new_bullet(2,0) dx=bullet.dx dy=bullet.dy x=bullet.x y=bullet.y w=bullet.w h=bullet.h end for b in all(bullet) do b:update() end if hit(x+dx,y,w,h) then dx=0 end if hit(x,y+dy,w,h) then dy=0 end x+=dx y+=dy end function _draw() cls() map(0,0,0,0,16,16) spr(2,x,y) for b in all(bullet) do b:draw() end end function hit(x,y,w,h) collide=false for i=x,x+w,w do if fget(mget(i/8,y/8))>0 or fget(mget(i/8,(y+h)/8))>0 then collide=true end end return collide end --bullet function add_new_bullet(_dx,_dy) add(bullet,{ x=63, y=63, w=1, h=1, dx=_dx, dy=_dy, draw=function(self) pset(self.x,self.y,7) end, update=function(self) self.x+=self.dx self.y+=self.dy end }) end
Hi, It could be that you have the function HIT running in the UPDATE function every turn but not specifically for an instance of BULLET. Try embedding the HIT function into the BULLET UPDATE function instead. There is a good chance that this might break things the first time you try it, so I would use the comments to delete code rather than actually deleting it, then you can roll back changes if it doesn’t work!
I have been dabbling around with Pico-8 for a while now. Never made any games really. Just trying to use it as a learning tool for programming concepts. And I could never wrap my head around how to use tables. Especially for "instancing" objects like projectiles or other things. And this REALLY gave me the ah-ha! Moment I needed. Thank you so much for this short, yet very concise video about that topic.
Now I just need to understand collisions and using them to affect objects.
Thanks for that!
These are exactly the types of videos I needed when I started out with Pico-8! I really like the approach of isolated topics within each video, and aside from that, you are clear and concise. There is a lack of this kind of tutorial content for Pico-8 on TH-cam and hopefully your channel will fill that niche. Great stuff!
Many thanks!
This is an excellent video! This perfectly explains something I've been having trouble with!
incase anyone else is confused with what "b" is about:
i.e. for b in all(bullets) do...
Basically this is saying for every item in the table 'bullets', assign the items of this table to a variable we defined as b, and then do the following to it ( i.e. b:update() etc....). In other words you can rename 'b' to anything u want. So this might not work as intended if you store other other objects like 'health' or whatever in the same table, since everything item with a function named update() gets called.
This and the "managing game state" videos have been so valuable for me starting out. My code is so well-structured thanks to you! 💪
Great to hear it's useful to you! Thanks.
Been trying to understand this for ages. Thank you!
Lovely video. Very helpful.
Thanks a lot for this! For one like me, who comes from procedural BASIC-Programming, the concept of tables was hard to grasp. I mean, in PureBasic I do have stuff like Structs and Linked Lists, and I know how to manipulate them, but this was a different beast altogether. After watching your tutorial, though, it became a lot clearer.
Just wanted to say thank you! This is insanely informative and on point. I also like your voice. Very calming :) Keep up the good work!
Amazing tutorial, as always. I was coming into this thinking I'd know everything already, but I was entirely unprepared for being able to put FUNCTIONS into objects. Not that I'm an expert, but I had read the documentation and, as far as I could tell, it makes no mention of this function-ality (ha). I'm going to have to revamp a couple of my projects to use this knowledge now.
Quality content here. You are a good teacher!
Thanks!
thank you so much for this this has help me make so many games.
Thanks a lot for this tutorial, it really helps with my work, otherwise, it would take ages for me to get a grip on this topic.
absolutely amazing! thanks man
Ah, and now the state video (later in the sequence) makes more sense, functions and code as variables - a bit like closures, really - thanks!
I should have watched these very recent ones in strict order :-)
Great explanation, by the way, very easy to follow and grasp.
Wow! I've been trying to do this!
--Bullet timer:
h=0
bullet_timer = false
if btn(5) then
if h == 0 then
bullet_timer=true
add_new_bullet()
end
function _update()
bullet_delay()
...
end
function bullet_delay()
if bullet_timer then
h+=1
if h>8 then
h=0
bullet_timer=false
end
end
end
So basically a boolean is used as a switch here (bullet_timer). the contents of the function bullet_delay become active when this switch is turned to true. What the function then does is it increases h over time (can play with the values as u like). Once h hits a certain number, we set h to 0 and turn the h timer off. Then it allows btn(5) when pressed to activate add_new_bullet once again cause h =0 like it was originally in init before any bullets were first added.
thank you so much for this tutorial! perfect video
This is OOP! nice!
i didnt know you could put functions inside of tables my mind has been expanded and ive been doing everything wrong
Could you use btnp to space out the bullets?
Hi. Not really. BTNP reports TRUE only for the next frame after the button has not been pressed, so it reports FALSE for a button hold. You'd need to mash the button to have it use BTNP.
@doc_robs thank you for the wonderful tutorial series!
However, I do have a question regarding collision. I have followed both this tutorial and the fget mget collision tutorial. I have attempted to combine the two but when I try to return the X, Y, W, H to the collision i am receiving runtime error at IF HIT(X+DX,Y,W,H) THEN. "ATTEMPT TO PERFORM ARITHMETIC ON GLOBAL 'X' (A NIL VALUE)". Is this because the object BULLET has no way to index values?
Any help would be greatly appreciated.
Cheers!
function _init()
x=63
y=63
dx=0
dy=0
w=1
h=1
bullet={}
end
function _update()
if btnp(🅾️) then
add_new_bullet(2,0)
dx=bullet.dx
dy=bullet.dy
x=bullet.x
y=bullet.y
w=bullet.w
h=bullet.h
end
for b in all(bullet) do
b:update()
end
if hit(x+dx,y,w,h) then
dx=0
end
if hit(x,y+dy,w,h) then
dy=0
end
x+=dx
y+=dy
end
function _draw()
cls()
map(0,0,0,0,16,16)
spr(2,x,y)
for b in all(bullet) do
b:draw()
end
end
function hit(x,y,w,h)
collide=false
for i=x,x+w,w do
if fget(mget(i/8,y/8))>0 or
fget(mget(i/8,(y+h)/8))>0 then
collide=true
end
end
return collide
end
--bullet
function add_new_bullet(_dx,_dy)
add(bullet,{
x=63,
y=63,
w=1,
h=1,
dx=_dx,
dy=_dy,
draw=function(self)
pset(self.x,self.y,7)
end,
update=function(self)
self.x+=self.dx
self.y+=self.dy
end
})
end
Hi,
It could be that you have the function HIT running in the UPDATE function every turn but not specifically for an instance of BULLET. Try embedding the HIT function into the BULLET UPDATE function instead. There is a good chance that this might break things the first time you try it, so I would use the comments to delete code rather than actually deleting it, then you can roll back changes if it doesn’t work!
@@docrobs Thanks!
Anyone know why mines saying self is a nil value?