The aim of this particle tutorial, is to build a simple particle fountain, controlled by mouse, from the ground up.
At every interim step, it is the intention to have a working program so that you can see what the results are of the piecemeal changes. You can either cut and paste the code in this document to the Basic-256 code window (although indentation gets lost.) or use the hyperlinks to the example programs.
What is a particle system?
A particle system in computer graphics is a technique used to simulate complex visual effects that are difficult to render with conventional methods. It involves creating a large number of small, simple objects (particles) and animating them to create the illusion of more complex, "fuzzy" phenomena like explosions, fire, smoke, sparks, waterfalls, clouds, fog, petals, grass, bubbles, and so on.
In this tutorial however, we will keep things a LOT simpler. In our particle system, each particle will have its own series of properties related to its behavior (for example, position, velocity, age, etc.). We will start our particle system from a blank slate
Basic-256 Boilerplate
For building up our particle system program, we will use the following scaffolding
Fastgraphics # write to video buffer.
size_x=800
size_y=800 # good practice to put every constant in a variable.
graphsize size_x,size_y # feel free to change the size of your canvas
color black
rect 0,0,size_x-1,size_y-1 # start out with a nice black screen.
refresh # dump the video buffer to screen
#Voila! This gives you a nice canvas to draw on.
Running this program gives us a nice black background. Not really exciting yet
Download CodeFirst, we need to define the specifics of our particle system. The particle system itself is stored into a multidimensional array, a grid if you like. The first element of the array denotes the number of particles in our system. (the number of lines on the grid) Here we will limit us to 300. (Feel free to adapt to the power of your system) The second denotes the number of properties each particle has. (the number of columns on the grid)
For an initial, minimal particle system, we will need x-position, y-position, x-velocity, y-velocity. This makes 4 properties in all.
We will set up the particle system to start from the centre, so x-position and y-position will be 400 (half of the 800x800 graphsize)
For the x- and y-velocities, we will take a very small number, here between -0.25 and 0.25.
Partnum=300
Dim Particle (Partnum,4) #particle system with 300 particles with 4 properties
for p = 0 to Partnum-1
Particle[p,0] = size_x/2
Particle[p,1]= size_y/2
Particle[p,2]= (rand-0.5)/2 # velocity from -0.25 to 0.25
Particle[p,3]= (rand-0.5)/2 #velocity from -0.25 to 0.25
next p
If we now dump our particles to the screen, we would just see a dot in the middle of a black screen as all particles are bunched up together. A particle system however is a dynamic system, so we will have to update it continually and reshow it.
We update our particle system by adding the velocities to the respective positions, we show the new screen and we clear it again. Finally we loop it with a simple goto-loop
Loop:
for p = 0 to Partnum-1 #write all the particles to the buffer and update them
color white
plot (Particle[p,0],Particle[p,1])
Particle[p,0]= Particle[p,0]+Particle[p,2]
Particle[p,1]= Particle[p,1]+Particle[p,3]
next p
Refresh #all particles shown updated on the screen
color black
rect 0,0, size_x-1,size_y-1 #initialize the screen to black again
goto Loop # do it all again
Now, if we run this program, we already get a nice star-burst effect, but it quickly fizzles out as most points move beyond the screen borders. (Feel free to change the number of particles and their velocities. Experimenting is fun!)
DownloadThere are 2 easy ways to prevent this fizzling out, to keep the particle system constrained within the borders of the graphics area.
-/ We bounce the particles of the screen borders by reversing the velocity direction for the impacted screen border (either x-velocity or y-velocity). This however turn chaotic very quickly
-/ Once it moves out of view, we reinitialize the particle to the centre of our screen again
First Option
In the loop where we update the x- and y-positions, we check if the particle has moved off the screen. If so we reverse the direction:
for p = 0 to Partnum-1 #write the particles to the buffer and update them
color white
plot (Particle[p,0],Particle[p,1])
Particle[p,0]= Particle[p,0]+Particle[p,2]
if Particle[p,0] < 0 or Particle [p,0]>size_x then Particle[p,2]= -Particle[p,2]
Particle[p,1]= Particle[p,1]+Particle[p,3]
if Particle[p,1] < 0 or Particle [p,1]>size_y then Particle[p,3]= -Particle[p,3]
next p
If we now run the program with this updated loop, we quickly find us in a chaotic situation with particles bouncing all over the place.
DownloadTo stop the chaotic motion after the Particles start to bounce off the screen edges, we can do the following:
-/ add a bit of gravity (by slightly increasing the y-speed)
-/ dampen the bounce (by multiply y-speed with eg 0.7-0.9 when the particle 'bounces').
For the gravity, we add a gravity parameter next to the array definition and set it to eg 0.001. (This is because on my system, I get about 500 full iteration loops per second. As we add gravity at every loop, the gravity influencssystem very quickly. Again experiment with this.).
For the dampening and in order to get some variation in the bounce, we dampen the bounce with a factor between 0.7 and 0.9.
This leads us to the following code changes
grav=0.001 # gravity increase
for p = 0 to Partnum-1 #write the particles to the buffer and update them
color white
plot (Particle[p,0],Particle[p,1])
Particle[p,0]= Particle[p,0]+Particle[p,2]
if Particle[p,0] < 0 or Particle [p,0]>size_x then Particle[p,2]=-Particle[p,2]*(0.7+rand/5)
Particle[p,1]= Particle[p,1]+Particle[p,3]
if Particle[p,1] < 0 then Particle[p,3]= - Particle[p,3] # bounce off ceiling
if Particle [p,1]>size_y then Particle[p,3]= - Particle[p,3]*(0.7+rand/5)bounce + dampen
Particle[p,3]= Particle[p,3]+grav # artificial gravity
next p
Download
In the second option to improve the simple starburst (disregarding the gravity addition), we have to prevent our particles from escaping the screen. We do this by reinitialising the particle to the centre of the screen if the particle has moved off the screen.
for p = 0 to Partnum-1 #write the particles to the buffer and update them
color white
plot (Particle[p,0],Particle[p,1])
Particle[p,0]= Particle[p,0]+Particle[p,2]
Particle[p,1]= Particle[p,1]+Particle[p,3]
if Particle[p,0]< 0 or Particle [p,0]>size_x or Particle[p,1]< 0 or Particle [p,1]> size_y then
Particle[p,0]= size_x/2
Particle[p,1]= size_y/2
endif
next p
If we now run the program with this updated loop, we see a never ending stream of particles being emitted from the centre.
DownloadHmmmm. In option one the particles come to rest and remain there indefinitely while in option two, the particles are being respawned. How can we combine this?
Well, we do this by giving each particle a random lifetime (random between certain limits) and decreasing the lifetime at every loop.
When the lifetime hits zero, we reinitialize the particle.
This lifetime will be a 5th property of the particle, so we have to change the size and the initialization of the array:
Dim Particle (300,5) #define a 300 particle system with 5 properties
and
for p = 0 to Partnum-1
Particle[p,0] = size_x/2
Particle[p,1]= size_y/2 # as we have gravity, we can moe this higher (eg size_y/6)
Particle[p,2]= (rand-0.5)/5 # velocity from -0.1 to 0.1
Particle[p,3]= (rand-0.5)/5 # velocity from -0.1 to 0.1
Particle[p,4]= rand *1020 # play with this setting until you get a nice bouncy effect
next p
Now that we have a life property (Particle[p,4]), we also need to decrease it so we check for viability and if dead (ie life < 0), we reinitialize the particle:
for p = 0 to Partnum-1 #write the particles to the buffer and update them
color white
plot (Particle[p,0],Particle[p,1])
Particle[p,0]= Particle[p,0]+Particle[p,2]
if Particle[p,0]<0 or Particle [p,0]>size_x then Particle[p,2]= -Particle[p,2]* (0.7+rand/5)
Particle[p,1]= Particle[p,1]+Particle[p,3]
if Particle[p,1] < 0 then Particle[p,3]= - Particle[p,3] # bounce off ceiling
if Particle [p,1]>size_y then Particle[p,3]= - Particle[p,3]* (0.5+rand/5)#bounce + dampen
Particle[p,3]= Particle[p,3]+grav # artificial gravity
Particle[p,4]= Particle[p,4]-1
if Particle[p,4]<0 then
Particle[p,0] = size_x/2
Particle[p,1]= size_y/2
Particle[p,2]= (rand-0.5)/5 # velocity from -0.1 to 0.1
Particle[p,3]= (rand-0.5)/5 # velocity from -0.1 to 0.1
Particle[p,4]= rand *1020# play with this setting until you get a nice bouncy effect
endif
next p
Download
currently, all particles are simply white. Based on the life parameter however, we can easily make the particles change color as they age.
There are of course many ways to map the age to a color. We will simply translate the age value to RGB colors.
Simply replace the following code:
color white
with the following code:
age=((Particle[p,4]/4) mod 255)
if age > 85 then
colblue = 255
colgreen = 255
colred = 255
color rgb(colred,colgreen,colblue)
else
if age >170 then
colblue = Particle[p,4] mod 255
colgreen = 255
colred = 255
color rgb(colred,colgreen,colblue)
else
colblue = 0
colgreen = Particle[p,4] mod 255
colred = 255
color rgb(colred,colgreen,colblue)
endif
endif
Download
The initial burst (the square full of particles that appears and bounces before the fountain really kicks in) is because we start the program by initializing all the particles in the system.
This of course also causes all particles to be alive at the chosen position at step 0.
To prevent this, we need to spawn particles all the time on the fly (and not just when one particle dies since then we'd have to wait for the initial particles to start dying first..)
For this, we replace the initialization of the Particle system so that all particles are DEAD (ie Particle [p,4]=0)
for p = 0 to Partnum - 1
Particle[p,4] = 0
next p
Then, during the main loop, the first thing we do is to continuously create a number of particles (here, 3 particles at every iteration). The number of particles here will depend on the total number and on the power of your PC.
for k = 1 to 3
spawni=int(rand * Partnum) # select a random particle
if Particle[spawni,4] = 0 then # if the particle is dead, create one
Particle[spawni,0] = size_x/2
Particle[spawni,1]= size_y/2
Particle[spawni,2]= (rand-0.5)/5 # velocity from -0.1 to 0.1
Particle[spawni,3]= (rand-0.5)/5 # velocity from -0.1 to 0.1
Particle[spawni,4]= rand *1020 # play with this setting
end if
next k
In the next loop (where we update the particles) we first check if the particle in question is dead and if so skip the rest of the loop.
So ,
for p = 0 to Partnum-1 #write the particles to the buffer and update them
age=((Particle[p,4]/4) mod 255)
if age > 85 then
colblue = 255
colgreen = 255
colred = 255
color rgb(colred,colgreen,colblue)
else
...
endif
next p
would become
for p = 0 to Partnum-1 #write the particles to the buffer and update them
if Particle[p,4]>0 then
age=((Particle[p,4]/4) mod 255)
if age > 85 then
colblue = 255
colgreen = 255
colred = 255
color rgb(colred,colgreen,colblue)
else
...
endif
endif
endif
next p
Voila! That is all there is to it! Mind you, if your PC is very fast and if your particle's life duration is set too high, the piecemeal generation of new partivcles will be so fast that it will look as if the initial square is still there...
Fix this by giving you particles a shorter lifetime when creating them piecemeal but a longer one when respawning dead particles.
With these last changes, if we now run our program (particle07.kbs) we get from frame one, a nice particle fountain.
However, we did say we were going to control it with our mouse.
Well, this is quite simple! All we need to do is to initialize the x- and y-locations to the mouse positions!
Like this:
Particle[p,0]= size_x/2
Particle[spwani,0]= size_x/2
Particle[p,1]= size_y/2
Particle[spwani,1]= size_y/2
would then become:
Particle[p,0]= mousex
Particle[spawni,0]= mousex
Particle[p,1]= mousey
Particle[spawni,1]= mousey
Running the program now would immediately start the fountain at location 0,0 (seeing that the mouse coordinates have not yet been initialized).
We can fix this by simply checking if mousex is greater than 0 right at the start of the Loop construct:
Loop:
for k = 1 to 3
spawni=int(rand * Partnum) # select a random particle
if Particle[spawni,4] = 0 then # if the particle is dead, create one
Particle[spawni,0]= mousex
Particle[spawni,1]= mousey
Particle[spawni,2]= (rand-0.5)/5 # velocity from -0.1 to 0.1
Particle[spawni,3]= (rand-0.5)/5 # velocity from -0.1 to 0.1
Particle[spawni,4]= rand *1020 # play with this setting
end if
next k
would then become:
Loop:
if mousex > 0 then
for k = 1 to 3
spawni=int(rand * Partnum) # select a random particle
if Particle[spawni,4] = 0 then # if the particle is dead, create one
Particle[spawni,0]= mousex
Particle[spawni,1]= mousey
Particle[spawni,2]= (rand-0.5)/5 # velocity from -0.1 to 0.1
Particle[spawni,3]= (rand-0.5)/5 # velocity from -0.1 to 0.1
Particle[spawni,4]= rand *1020 # play with this setting
end if
next k
endif
Voila! We now have Basic-256 program that gives us a nice particle fountain controlled by the mouse!!
DownloadIf you PC is fast enough, you can swap points for circles. For this, we just add a line in the main loop:
color rgb(colred,colgreen,colblue)
plot (Particle[p,0],Particle[p,1])
becomes:
color rgb(colred,colgreen,colblue)
# plot (Particle[p,0],Particle[p,1])
circle Particle[p,0],Particle[p,1],2
You can of course play around with the size of the circles.
Voila! all done! You can now start to experiment with increasing the screen dimension or the number of particles, changing the gravity parameter or the life duration, changing the speed at which particles are ejected (currently, we are using (rand-0.5)/0.5 for both x- and y-directions), etc. etc.
DownloadThe aim of this particle tutorial, is to have text decay in various ways.
At every interim step, it is the intention to have a working program so that you can see what the results are of the piecemeal changes. You can either cut and paste the code in this document to the Basic-256 code window (although indentation gets lost.) or use the hyperlinks to the example programs.
Basic-256 Boilerplate
For building up our particle system program, we will use the following scaffolding
Fastgraphics # write to video buffer.
size_x=800
size_y=800 # good practice to put every constant in a variable.
graphsize size_x,size_y # feel free to change the size of your canvas
color black
rect 0,0,size_x-1,size_y-1 # start out with a nice black screen.
color white
font "Tahoma",40,100
text 275,350,"Text Effect"
refresh # dump the video buffer to screen
#Voila! This gives you a nice canvas to start with.
Running this program gives us "Text Effect" on a nice black background. Not really exciting yet
Download CodeNext, we want to record the positions of all the white pixels in an array. We can do this by scanning the whole canvas, but since we know the text and the position, we can limit the scan area.
The next problem would be to size the array correctly. We can do this by first scanning the scan area for white pixels and simple add 1 to a counter, like this:
textlocx=size_x/2-150 # Approximate start of the text
arr=0 # counter for nbr of pixels.
for i = textlocx to size_x-1
for j = size_y/2-50 to size_y/2+50
if pixel(i,j)=white then
arr=arr+1
endif
next j
next i # dump the video buffer to screen
print arr# Shows the number of pixels, in our case is 4994 so let's say 5000.
Each pixel is defined by its x- and y-value, so we need 2 elements for each of the 4994 pixels. We can now correctly size the particle array and store the locations of all the white pixels.
dim textarray(5000,2)
textlocx=size_x/2-150 # Approximate start of the text
k=0 # counter for array position.
for i = textlocx to size_x-1
for j = size_y/2-50 to size_y/2+50
if pixel(i,j)=white then
textarray[k,0]=i # horizontal position of the pixel
textarray[k,1]=j # vertical position of the pixel
endif
next j
next i #we now clear the screen and dump the array on the screen
color black
rect 0,0,size_x-1,size_y-1
for i = 0 to k-1
plot textarray[i,0],textarray[i,1]
next i
Running this program gives us "Text Effect" but consisting of pixels. Not really exciting yet. You can however see that the anti-aliasing of the first picture is gone.
Download CodeNow the fun part starts...
An explosion will send all particles in a random direction, determined by an x-displacement and a y-displacement. These are stored in additional column in the array, so will will need to redimention it like:
dim textarray(5000,4)
Now we can add the x- and y-displacements when we find a white pixel, like this:
for i = textlocx to size_x-1
for j = size_y/2-50 to size_y/2+50
if pixel(i,j)=white then
textarray[k,0]=i # horizontal position of the pixel
textarray[k,1]=j # vertical position of the pixel
textarray[k,2]=(rand*2)-1 # horizontal displacement at each iteration
textarray[k,3]=(rand*2)-1 # vertical displacement at each iteration
endif
next j
next i
Now that we have stored the locations of the white pixels and their movement, we can create a loop to update the x- and y-location, dump the pixels to the screen and clear the screen.
pause 1 # show the text for some time
counter=0
while counter < 1000
# calculate the new location
for i = k-1 to 0 step -1
textarray[i,0]= textarray[i,0]+ textarray[i,2]
textarray[i,1]= textarray[i,1]+ textarray[i,3]
next i
# plot all the particles
color white
for i = 0 to k-1 step 1
plot textarray[i,0], textarray[i,1]
next i
refresh
# clear the screen again
color black
rect 0,0,size_x,size_y
counter = counter + 1
end while
Running this program gives us an exploding "Text Effect". You can change the 'speed' to your liking. You can also add a very small amount to textarray[i,3] to simulate gravity like this:
counter=0
while counter < 1000
...
textarray[i,0]= textarray[i,0]+ textarray[i,2]
textarray[i,1]= textarray[i,1]+ textarray[i,3] #add gravity
textarray[i,3]= textarray[i,3]+ 0.02 #add gravity
next i
...
end while
Download Code
Here is another one....
The Sand Effect will make the text fall from left to right. For this we need a timer and thus an additional column in our array
dim textarray(5000,5)
Now we can add incremental counter when we find a white pixel, like this:
for i = textlocx to size_x-1
for j = size_y/2-50 to size_y/2+50
if pixel(i,j)=white then
textarray[k,0]=i # horizontal position of the pixel
textarray[k,1]=j # vertical position of the pixel
textarray[k,2]=(rand-0.5)/5 # very small horizontal displacement
textarray[k,3]=1 # straight downward vertical displacement
textarray[k,4]=int(k/20) # 'speed' at which the text disintegrates
#int(k/4) goes slowly, int(k/60) goes fast
endif
next j
next i
Note that textarray[k,2] and textarray[k,3] have been changed. Playing with these give different effects.
Now, in the particle calculation, we subtract 1 from the textarray[k,4] and activate the location update when it reaches 0
pause 1 # show the text for some time
counter=0
while counter < 1000
#decrease the activation counter and check if 0 or less
for i = k-1 to 0 step -1
textarray[k,4]=textarray[k,4] - 1
if textarray[k,4] < 1 then
textarray[i,0]= textarray[i,0]+ textarray[i,2]
textarray[i,1]= textarray[i,1]+ textarray[i,3]
endif
next i
# plot all the particles
color white
for i = 0 to k-1 step 1
plot textarray[i,0], textarray[i,1]
next i
refresh
# clear the screen again
color black
rect 0,0,size_x,size_y
counter = counter + 1
end while
Running this program gives us an "Text Effect" turning to sand and falling. You can change the 'speed' to your liking. You can also add an ever-increasing small amount while calculating textarray[i,3] to simulate gravity like this:
counter=0
while counter < 1000
...
textarray[i,0]= textarray[i,0]+ textarray[i,2]
textarray[i,1]= textarray[i,1]+ textarray[i,3]
textarray[i,3]= textarray[i,3]+ 0.02 #add gravity
next i
...
end while
Download Code
Here the last effect....
The Burn Effect will make the text 'burn' from right to left. For changing the color of the 'burning', we need 2 additional columns in our array
dim textarray(5000,7)
Additionally, we will create a second array to store the original pixels. We use this to trigger when the particles are being 'burned':
#We also create a second array to store the original locations
dim origarray(5000,2)
textlocx=size_x/2-150 # Approximate start of the text
k=0 # counter for array position.
for i = size_x to 0 step -1
for j = size_y/2+50 to size_y/2-50 step -1
if pixel(i,j)=white then
# horizontal location
textarray[k,0]=i
# vertical location
textarray[k,1]=j
# horizontal movement
textarray[k,2]=rand +1
# vertical movement
textarray[k,3]=-((rand*2))
#change this to make the transition faster/slower
textarray[k,4]=int(k/10)
#This will be our red component
textarray[k,5]=255
#This will be our green component
textarray[k,6]=255
k=k+1
endif
next j
next i
#now we store the initial locations in the second array
for i = 0 to 4999
origarray[i,0] = textarray[i,0]
origarray[i,1] = textarray[i,1]
next i
Note that textarray[k,2] and textarray[k,3] have been changed.
Now, When we plot all the particles, we check if they are still in the original position and if so, plot them
When they are no longer in their original positions, we plot the particles as circles starting with a yellow color and, by decrreasing the green component turn them more and more red
We also check if the particles are still on the screen and that the green component does not go below 0
# plot all the particles
for i = 0 to k-1 step 1
if textarray[i,0]=origarray[i,0] AND textarray[i,1]=origarray[i,1] then
color white
plot textarray[i,0], textarray[i,1]
else
if textarray[i,0] > 0 and textarray[i,6] >1 then
color int(textarray[i,5]),int(textarray[i,6]),0
circle textarray[i,0], textarray[i,1],2
textarray[i,6] =textarray[i,6] -1
endif
endif
next i
refresh
# clear the screen again
color black
rect 0,0,size_x,size_y
counter = counter + 1
end while
Running this program gives us an "Text Effect" burning like a fuze and turning into a primitive kind of smoke. You can change the 'speed' to your liking.
Download Code