Hi there!
This is my first tutorial on PICO-8. Today we’re gonna make a simple rain particle system. I will not focus too much on token count but just show you how to make a particle system with a pool that can create a simple rain effect.
Let’s get started.
Start by entering the following. You probably already know this, but _INIT() is for game initialization, _DRAW() is the draw-loop and _UPDATE() is the update-loop.
We clear the screen to a black color at every frame. This is done by CLS(0).
Let’s get started on the particle system.
Create a new function called new_particle_system that takes in a count of elements as the max particles allowed at a time.
Inside the function we will create a local variable called _pool:
_pool={}
Next up we need to create a particle function. Go to a new tab or scroll down and write the following:
function new_particle()
local _x,_y=0,0
local _xspeed,_yspeed=0,0
local _done=true
end
This is the basics of the particle class, we have an y-speed an x-speed and two positions (x,y). Now let’s add a return statement below _done=true:
function new_particle()
local _x,_y=0,0
local _xspeed,_yspeed=0,0
local _done=true
return {
launch=function(x,y,xspd,yspd)
if _done then
_x,_y,_xspeed,_yspeed,_done=x,y,xspd,yspd,false
return true
end
end,
update=function()
if (_done) return
_x+=_xspeed
_yspeed+=0.92
_y+=_yspeed
if _y>127 then
_done=true
end
end,
draw=function()
if (_done) return
line(_x,_y,_x+_xspeed,_y+_yspeed,7)
end
}
end
Above return an object with the following functions:
launch() – for launching the particle with x-position, y-position, x-speed and y-speed.
update() – for updating the position and the current speed of the particle.
draw() – for drawing the particle as a line with both speeds applied.
Let’s take a closer look at the functions:
launch starts by checking if _done is true. This means that we’re allowed to use the particle, so if that is the case we set all the variables to the inputs and set _done to false. We then return true. The return true will hopefully make sense later on.
update also starts by checking if _done and then we dismiss the function if that is the case. No reason to calculate anything if the particle isn’t supposed to show. After that we apply the _xspeed to _x and we apply gravity (0.92) to _yspeed. Then we check if the particle has left the screen (after 127 on y) and set _done to true if that is the case.
draw starts by dismissing if the particle is done. Else it will draw a line from _x,_y to _x+_xspeed, _y+_yspeed.
Cool! That’s all we need for the particle.
Let’s go to the particle system function and work some magic there.
Let’s initialize the particle pool by doing the following:
for i=1,maxparticles do
_pool[i]=new_particle()
end
LUA’s arrays starts at index 1 so that’s the reason why we do it from i=1 and not i=0.
Now that we have initialized the pool we can return the object with the following functions: rain(), update(), draw().
return {
rain=function()
end,
update=function()
end
draw=function()
end
}
The above code shows how return the object with functions declared inside. Now let’s fill out the blanks:
rain=function()
local rand=rnd(5)
for i=1,maxparticles do
if _pool[i].launch(rnd(127),-rand,0,rand) then
return
end
end
end
The rain function sets a random number between 0 and 5, loops through all particles and dismisses the function in case a particle’s launch function has been called. The call to launch randomly places the particle at an x-position inside the screen and above the screen by -rand (remember PICO-8’s coordinate system is top-left (0,0) to bottom-right (127,127)). We also set the x-speed to 0 and the y-speed to rand.
Now let’s create the update function, it’s pretty straight-forward:
update=function()
for i=1,maxparticles do
_pool[i].update()
end
end
The draw function is more or less the same except that we call draw instead of update:
for i=1,maxparticles do
_pool[i].draw()
end
And that’s it. Now let’s put it to use…
Go back to your main tab (or where you placed the initial functions and write the following:
function _init()
particlesys=new_particle_system(512)
end
function _draw()
cls(0)
particlesys.draw()
end
function _update()
particlesys.rain()
particlesys.update()
end
Go to the console and type in run and see it in action.
Next up is extending the particle systems with sparks!