Game Mechanics:
Particle Effects
Explanation of Code!
function _init()
--particles
effects = {}
--effects settings
trail_width = 1.5
trail_colors = {12,13,1}
trail_amount = 2
fire_width = 3
fire_colors = {8,9,10,5}
fire_amount = 3
explode_size = 5
explode_colors = {8,9,6,5}
explode_amount = 5
--sfx
trail_sfx = 0
explode_sfx = 1
fire_sfx = 2
--player
player = {x=53, y=53, r=2, c=7}
end
As usual, we set most of the variables we will use inside of the main _init()
function, or if you are using multiple game states such as a main menu, game play, game over, etc. then you should put these variables inside of the init function for the state that will use them.
effects = {}
creates an empty table that will be used to hold all of the particle effects data.
Each particle (could be a single circle or a single pixel) will have data attached to it such as the screen position, size, force and direction of movement, color, and lifetime. Each particle\'s data will be held in a smaller table and then all of the particle tables will be added to this effects
table.
It is a good idea to organize your code so that variables that you may need to adjust many times, are all grouped together near the beginning of your game file. This way, when you are play testing your game and you want to tweak things, you can simply go to one place in your code and change the values of the correct variables instead of hunting them down in your code every time.
This is why we created an --effects settings
section to organize the particle effects variables that can be quickly changed depending on the feel of the game. It\'s like having a settings menu inside of our code.
Want to increase the fire effect size? No need to find the fire effects function, simply find the fire effects variables in these "effects settings" section and try different amounts quickly and easily.
In this tutorial, we will create 3 effects:
trail
= usually a line or several pixels that constantly follow behind something moving.
fire
= some kind of pixel effect that looks like fire and/or smoke.
explode
= usually circles or pixels that spread outward in every direction at the same time.
But there are many other types of effects that you could create from this code and we\'ll show how easy that is at the end.
Each effect has 3 settings variables:
width
or size
= a number of pixels that controls the area of space that the effect is created in.
colors
= a table of up to 4 color numbers (you could add more) that each particle will cycle through as its lifetime runs out.
amount
= a number of particles that will be created at the same time.
Each effect also has a sound effect (SFX) number and we grouped those together as variables here as well. It is a good idea to create and name these variables appropriately so that your code is highly readable for yourself and others.
--sfx
trail_sfx = 0
explode_sfx = 1
fire_sfx = 2
If I see sfx(0)
in your code, all I know is that you are playing the sound effect #0. Then I have to go to the SFX editor, find #0, and listen to it to know what sound effect it actually is.
If I see sfx(explode_sfx)
, then I know you are playing an explosion sound effect. Done. And if you or I want to change the sound effect number, then we only need to change one variable setting instead of searching all of the code for sfx(0)
and change each one.
player = {x=53, y=53, r=2, c=7}
= For this demo, we needed to have a controllable "player", which is just a circle.
It is a table that has four keys:
x
= X-axis position (number of pixels from the left)
y
= Y-axis position (number of pixels from the top)
r
= (radius) size
c
= color number
There are 3 core functions that we will build to create and run every particle effect from tiny sparkles to giant explosions. First we need a function that will take data about one particle, put that data together into a table, then add that particle table to the larger effects
table.
Second, we will need a function that will update all the particles in the effects
table, controlling how they move and when they change size or colors.
Lastly, we will need a function that will draw all the particles in the effects
table to the game screen.
Here is the first function that adds each particle data to the effects table, so we named it "add_fx":
function add_fx(x,y,die,dx,dy,grav,grow,shrink,r,c_table)
local fx={
x=x,
y=y,
t=0,
die=die,
dx=dx,
dy=dy,
grav=grav,
grow=grow,
shrink=shrink,
r=r,
c=0,
c_table=c_table
}
add(effects,fx)
end
function
creates a new custom function. Then we write the name add_fx
. Then inside of parentheses we name all the variables that we will need to know about a particle in order to build the particle table. Here is what those variable names are and what they are for:
x
= X-axis position (number of pixels from the left)
y
= Y-axis position (number of pixels from the top)
t
= (time) starts at 0 and used for counting the particle\'s lifetime.
die
= a number used for how long the particle should "live" (when it should "die")
dx
= (change in X) for moving the particle left or right.
dy
= (change in Y) for moving the particle up or down.
grav
= a boolean (true or false) variable for whether to apply gravity to the particle or not.
grow
= a boolean (true or false) variable for whether to increase the particle size or not.
shrink
= a boolean (true or false) variable for whether to decrease the particle size or not.
r
= (radius) a number used for a particle\'s size. 1 = a single pixel.
c
= (color) the current color of the particle. It is first set as 0 but will be updated later depending on the color table.
c_table
= (color table) a simple list of color numbers in the order of what color the particle should be through its lifetime.
We take all of those variables and put them inside a local table named fx
.
It might look funny that each entry in the fx
table looks like x = x
but understand that the first "x" there is what the key will be named inside the table. Then the second "x" is the value of the variable x
that we will pass in to this function from inside the parentheses. We just decided to keep our variable naming consistent which is good practice, but it\'s unfortunate that it sometimes leads to this code that can look a bit confusing at first.
To finish this function we need to take that local fx
table and add it to the larger effects
table.
add(effects,fx)
will do that and now effects
is a table of tables!
The next core function is for updating the effects, so we appropriately name it update_fx
Here is the code overview:
function update_fx()
for fx in all(effects) do
--lifetime
...
--color depends on lifetime
...
--physics
...
--move
...
end
end
Notice the first line creates the function, names the function, and then has empty parentheses. That\'s because this function simply needs to run the same way every time and does not need to be given any variables like the add_fx
function did.
More honestly, this function does run differently every time, but that\'s because it will use the effects
table and check each particle\'s data to know how to change things with that particle.
The first thing we do inside this update function is access and loop through the effects
table. To do that we use a special table loop: FOR entry IN ALL(table) do
for fx in all(effects) do
We name a variable fx
there to become the local variable of each entry in the effects
table.
Remember that the effects
table is a table of tables, so each entry is a smaller table of its own. So each particle table inside the effects
table will be able to be accessed as a new local variable named fx
. And anything we want to change about that particle, we can simply change fx
while we are inside of this table loop.
--lifetime
fx.t+=1
if fx.t>fx.die then del(effects,fx) end
This next bit of code handles the lifetime of the particle. Inside each particle table (now named "fx") we already added a key named t
for tracking the amount of time the particle has been "alive".
So every time the game update runs, we will add to each particle\'s t
to increase their lifetime counter.
Now that the lifetime counter is working, we can check if the particle\'s life runs past its time to "die", which is another variable we already set inside of each particle. So if the lifetime is more than when the particle should die, then we should delete the particle and remove it from the effects
table completely.
That is eactly what this code will check and do: if fx.t>fx.die then del(effects,fx) end
In this next part, we will change the color of the particle based on the lifetime.
For example, in the fire particle effect, we will want to create many particles as single pixels that start off red, move upwards and change to orange, then yellow, then gray as smoke. Then the particle can die and be erased.
So first we need to figure out a way to know where the particle is in its lifetime.
Did the particle just start? Is it half way through its life? Or is it close to the die
time?
We know how long it has been alive because of each particle\'s lifetime counter: t
We also know how long it will live because of each particle\'s death variable: die
So if we create a fraction that is lifetime counter over death time, then that will tell us how close the particle\'s life is getting to its end.
Fractions are simply division, so we divide t
by die
and we will get a decimal that is somewhere between 0 and 1. Zero would be the start of a particle\'s life, and one would be the particle\'s death. So now we have a decimal to tell us how near or far the particle is to die.
So we could simply check for something like this:
--color depends on lifetime
if fx.t/fx.die < .5 then
--particle is younger than half its life
else
--particle is older than half its life
end
That\'s nice a simple but it would only let us set 2 colors: one color for when the particle is young, and one color when it is old.
But we already thought about a fire effect that would be 4 colors: red, orange, yellow, and finally gray. So we definitely want more than just 2 colors, and we also want the freedom to use only 2 or 3 colors too.
That just takes a little bit more math like when we compared lifetime with death time as a fraction.
This time we want to compare the number of colors in each particle\'s color table. That is saved in c_table
. And remember we access that from the new local variable for each particle that we named FX. So to get the color table we write: fx.c_table
We can check the total number of colors in the color table by getting the count of entries in the table. To do that we use the #
symbol before the table. So this will get the total count of colors: #fx.c_table
Now we can create more fractions using the total color count.
If the color count is 2. Then 1/color_count = 1/2. So that would be half.
If the color count is 2. Then 2/color_count = 2/2. So that would be full, or the end.
Imagine the same thing but with a different total color count.
If the color count is 4. Then 1/color_count = 1/4. And that would be a quarter.
If the color count is 4. Then 2/color_count = 2/4 = 1/2. And that would be only half.
So notice we are doing the same math there but depending on the total number of colors in the color table, the result will change to show a different result. And again, by doing the actual division it will give us a decimal between 0 and 1.
Now we have something to compare the lifetime decimal with!
Before we write the code, lets just think it through first.
If a particle\'s life compared to its death is less than the first color compared to the total number of colors, then the particle should be the first color.
If a particle\'s life compared to its death is more than the first but less than the second color compared to the total number of colors, then the particle should be the second color.
And so on. Here\'s the actual code:
--color depends on lifetime
if fx.t/fx.die < 1/#fx.c_table then
fx.c=fx.c_table[1]
elseif fx.t/fx.die < 2/#fx.c_table then
fx.c=fx.c_table[2]
elseif fx.t/fx.die < 3/#fx.c_table then
fx.c=fx.c_table[3]
else
fx.c=fx.c_table[4]
end
The if
and all elseif
here are checking Algebra really. So to help you understand what\'s happening try plugging in different numbers and doing the division yourself to see how those compare with each other. Then you should see that the math works out.
This is built for a maximum of 4 colors. As it is, you could add 5 or 6 colors to a particle\'s color table, but this code that updates the actual color will simply ignore any higher than 4.
But it\'s easy to continue the elseif
pattern to allow for 5, 6, 7, or more colors.
If any of those checks are true, then we want to set the particle color (fx.c
) using the correct color from the color table (fx.c_table[#]
).
That\'s why we do: fx.c = fx.c_table[1-4]
The square brackets [ ]
, is another way to select a value from inside a table.
Hang on, look what that means! Let\'s just take a step back for a moment to appreciate the fact that we are looping through the effects
table and accessing each particle as an fx
table, AND NOW we are accessing each color from inside the particle\'s c_table
color table!
We have just successfully jumped 3 levels of table inception! And if you are following along without a nose bleed, well done!
The next code we update for each particle will be adding physics, but only if that particle has those physics variables set to true.
--physics
if fx.grav then fx.dy+=.5 end
if fx.grow then fx.r+=.1 end
if fx.shrink then fx.r-=.1 end
if fx.grav then
checks if the gravity variable on the particle is true.
If so, then we add .5 to the particle\'s dy
so that it moves down a little faster.
if fx.grow then
checks if the growing variable on the particle is true.
If so, then we add .1 to the particle\'s r
(radius) so that it gets bigger.
if fx.shrink then
checks if the shrinking variable on the particle is true.
If so, then we subtract .1 from the particle\'s r
(radius) so that it gets smaller.
These three are basically settings for you to create many different types of effects yourself.
The last bit of code we update for each particle is changing the particle\'s position, creating movement.
--move
fx.x+=fx.dx
fx.y+=fx.dy
fx.x
plus fx.dx
takes the current X-position and adds the X-momentum variable.
fx.y
plus fx.dy
takes the current Y-position and adds the Y-momentum variable.
To understand how the momentum variables work, check out our Advanced Movement tutorial.
The last core function is for drawing the effects, so we appropriately name it draw_fx
.
function draw_fx()
for fx in all(effects) do
--draw pixel for size 1, draw circle for larger
if fx.r<=1 then
pset(fx.x,fx.y,fx.c)
else
circfill(fx.x,fx.y,fx.r,fx.c)
end
end
end
Just like the update_fx
function, we want to cycle through every particle in the effects
table. So we use the same table loop: for fx in all(effects) do
Inside of that we simply want to draw the particle with the correct position (X,Y), size (R), and color (C).
There are two different ways we want to draw particles.
Small particles we want to draw as single pixels, so we use a pixel drawing function: pset()
Larger particles we want to draw as a circle, so we use a circle drawing function: circfill()
To figure out which of those to use, we first check the currect size of the particle.
if fx.r<=1 then
checks the size (R for radius) as less than or equal to 1.
So if we want single pixel particles, then we just set the r
to 1. Or if we want a shrinking particle, then as its r
size gets smaller, and becomes 1 or less, then it will be drawn as a single pixel.
pset(fx.x,fx.y,fx.c)
is how we take the position (x
, y
) and color (c
) from the particle table (fx
).
Then we just write else
so that it catches any particles that come back as false from the first check.
So if any particles size is not less than or equal to 1, that means it must be more than 1! So let\'s draw a circle instead.
circfill(fx.x,fx.y,fx.r,fx.c)
will draw a filled in circle all the same color. We take the variables out of the particle table (fx
) again, but this time we also use the size variable (r
) to set the correct size of the circle.
That\'s it for drawing each particle!
Creating your own visual effects using the above 3 functions is surprisingly easy!
All you need to do is set up a new function, name it whatever fits for the effect you want to create, and then use the add_fx()
function to create particles with the settings that you want, to create the effect you want.
Let\'s take a look at a few examples we\'ve written to show you how to set up different effects functions and change the settings in little ways to create wildly different results.
We are going to use two words often in the follow explanations: "parameter" and "argument".
parameter = a local variable name set inside of the parentheses when a function is declared.
Example: function shoot(x,y,z)
X, Y, and Z are "parameters" of this shoot function.
argument = a variable given inside of parentheses when a function is called.
Example: shoot(player_x, player_y, player_z)
It\'s the same function, but somewhere in the game\'s update code when we want to run the shoot function. Notice it does not have the word "function" before the name, so we are asking it to run, not creating a new function.
The variables inside of the parentheses already hold data about the player and want to pass the data to the shoot function, so they are "arguments".
We will be creating effects functions and think ahead about what parameters we should set them up with.
Overview
The motion trail effect will create particles behind a moving object that change color and disappear slowly.
Here is the full function for setting trail effect particles and sending them to the effects
table using our add_fx( )
function.
-- motion trail effect
function trail(x,y,w,c_table,num)
for i=0, num do
--settings
add_fx(
x+rnd(w)-w/2, -- x
y+rnd(w)-w/2, -- y
40+rnd(30), -- die
0, -- dx
0, -- dy
false, -- gravity
false, -- grow
false, -- shrink
1, -- radius
c_table -- color_table
)
end
end
It looks long because we expanded it out and added comments for easy editing. But this is what it looks like if we condense it down.
-- motion trail effect
function trail(x,y,w,c_table,num)
for i=0, num do
add_fx( ... )
end
end
So now we can see there are really only 3 parts to this effect function.
1. The parameters for being able to customize the effect.
2. Repeatedly create a certain number (num
) of particles.
3. Send each particle\'s data to the add_fx
function so it can be added to the effects
table.
Parameters
function trail(...)
creates the function and names it "trail".
( x,y,w,c_table,num )
are all the parameters for customizing the trail.
x,y
are for the starting position of the particle.
In your game, you may want a trail behind a player when it jumps, so you can tell this trail function to run, and pass the player\'s X and Y position so the trail starts creating particles at the player\'s position.
w
is for the width of the trail.
A large width will create a wider or narrower trail. It is the size of the spawn zone for each particle in the trail. A smaller spawn zone will make sure all of the particles are close together and create a thin trail. Balance this with the number (num
) of particles spawned together to create wildly different effects.
c_table
is a table of color numbers.
If you think your game will only have one trail and always the same color, then simply remove this as a parameter and hard set the color table inside of this function instead. But this is a parameter so that you can create trails with different colors. For example, if you have a multiplayer game, then each player can have a trail color to help set them apart. Or the player\'s bullets can be a blue trail, and the enemy bullets can be red. So many ways to use this same effect and simply change the colors used in the effect with this parameter!
num
is a number of particles that will be created together at the same time.
So it will control the amount or volume of particles in the effect. A low number will create a sparse effect to make something like a glitter trail. A high number will create a densely packed effect to make something like a beam trail. Balance this with width (w
) to create wildly different trail effects. We created the settings variables named "..._amount" to control this up in the _init()
function.
Loop for Bursts of Particles
We will want to be able to create more than one particle at a time to make a dense trail. So we start with a For Loop: for i=0, num do
That will count a number (i
) starting at 0
and stop when it reaches whatever number we decide to set in the amount of particles (num
). And it will run the code we put inside of this For Loop that many times.
We will just create one particle each time this loop runs, and num
will control how many should be created in each burst.
Add Each Particle to Effects Table
Next we will use the add_fx
function to build each particle.
We already wrote the function with parameters, so this tells us not only what data we need to pass, but also the order we need to pass it in. Here\'s a reminder:
add_fx(x,y,die,dx,dy,grav,grow,shrink,r,c_table)
The data we pass are the arguments and they must match what the function is expecting to receive, the parameters.
So compare the parameters (above) with the arguments (below) when we call this function from inside the trail effect function.
--settings
add_fx(
x+rnd(w)-w/2, -- x
y+rnd(w)-w/2, -- y
40+rnd(30), -- die
0, -- dx
0, -- dy
false, -- gravity
false, -- grow
false, -- shrink
1, -- radius
c_table -- color_table
)
Arguments (Particle Settings)
Let\'s take a closer look now at the arguments to figure out why these specific arguments create the particle settings for a trail effect.
--settings
add_fx(
x+rnd(w)-w/2, -- x
y+rnd(w)-w/2, -- y
40+rnd(30), -- die
0, -- dx
0, -- dy
false, -- gravity
false, -- grow
false, -- shrink
1, -- radius
c_table -- color_table
)
Argument 1: x
= x+rnd(w)-w/2
Argument 2: y
= y+rnd(w)-w/2
This takes the x
and y
that was passed to this trail function, adds a random number between 0 and the width (w
) that was passed to this function, then subtracts half of the width. Subtracting half of the random number\'s max value, shifts the range of random numbers to a positive and negative range with 0 being in the middle. This allows the X and Y coordinates to be set somewhere inside of a spawn zone created by the random range with W.
Argument 3: die
= 40+rnd(30)
This sets the lifetime of the particle to be at least 40, and a maximum of 30 more than that.
How? Well rnd( )
can be a number between 0 and the number inside the parentheses. So if the random function comes out at 0, then the lifetime of this particle will be 40, and if random comes out at 30, then 40 + 30 = 70. So each particle in this trail will die (get deleted) at unique times. This creates the end appearance of the trail where if it looks like it fades away.
Change the length of the trail = change the number 40.
Change the length of the trail\'s fadeout = change the number 30.
Argument 4: dx
= 0
Argument 5: dy
= 0
The DX and DY are the momentum variables. Together they will create the direction and speed of each particle\'s movement. In a trail effect, we don\'t want each particle to move. So we set them both to 0. Each particle will be created at a specific location, change color as it gets older, then die. If this trail is created based on the position of a moving object, then each particle of the trail will be placed and remain at a location where the moving object once was, and together, all of those particles will appear as a trail.
Argument 6: gravity
= false
Argument 7: grow
= false
Argument 8: shrink
= false
These arguments are all set to false. You could try turning them on to experiment with different trail effects that you could make. The names are pretty obvious for what they do.
Argument 9: radius
= 1
This is the size of the particle. 1 means a single pixel while anything above 1 will be drawn as a circle, or whatever you set up the draw_fx
to do.
Argument 10: color_table
= c_table
This uses the parameter passed to this trail function: c_table
It should be a table of numbers as a simple list, like this: { 1,2,3,4 }
That determines the sequence of colors that the particle should be drawn in based on its lifetime.
Overview
For a detailed explanation on each piece, see the above Trail Effect. Only the unique differences will be explained here.
-- explosion effect
function explode(x,y,r,c_table,num)
for i=0, num do
--settings
add_fx(
x, -- x
y, -- y
30+rnd(25),-- die
rnd(2)-1, -- dx
rnd(2)-1, -- dy
false, -- gravity
false, -- grow
true, -- shrink
r, -- radius
c_table -- color_table
)
end
end
Parameters
function explode(x,y,r,c_table,num)
Almost exactly like the Trail Effect above, except for:
r
= the starting radius of each particle.
1 is a single pixel, while anything above 1 is a circle, depending on how you set up the draw_fx
function.
Loop
The same FOR LOOP is used to create a burst of particles all at once, like the Trail Effect. Except the Trail Effect, you want to run multiple times to continue creating particles as an object moves. But with an Explosion Effect, you only want to create a large number of particles at one time, when something major happens like an important collision.
Arguments
add_fx(
x, -- x
y, -- y
30+rnd(25),-- die
rnd(2)-1, -- dx
rnd(2)-1, -- dy
false, -- gravity
false, -- grow
true, -- shrink
r, -- radius
c_table -- color_table
)
Argument 1: x
= x
Argument 2: y
= y
This takes the x
and y
that was passed to this explosion function, and sets all of the particles in this effect to the same position to be the center point of the explosion.
Argument 3: die
= 30+rnd(25)
This sets the lifetime of the particle to be at least 30, and a maximum of 25 more than that, for a range of 30 to 55.
How? Check out the Trail Effect explanation above.
Change the duration of the explosion = change the number 30.
Change the duration of the fadeout = change the number 25.
Argument 4: dx
= rnd(2)-1
Argument 5: dy
= rnd(2)-1
The DX and DY are the momentum variables. Together they will create the direction and speed of each particle\'s movement. In an explosion, we want each particle to move away from the center point in any direction. So we randomly choose a number between 0 and 2, then take away 1 for a range between -1 and +1.
Argument 6: gravity
= false
Argument 7: grow
= false
Argument 8: shrink
= true
We don\'t want the explosion to fall, so gravity is turned off (false
).
We don\'t want the explosion to get bigger, so grow is turned off (false
).
We do want the explosion to get smaller, so shrink is turned on (true
).
You might prefer an explosion that grows instead of shrinks, so flip those and try it out!
Argument 9: radius
= r
This is the size of the particle. We use the parameter passed to this explosion function, so that it can be set differently inside of your game to create small explosions for small collisions, and large explosions for important collisions.
Argument 10: color_table
= c_table
Same as the Trail Effect. It uses the parameter passed to this function: c_table
It should be a table of numbers as a simple list, like this: { 1,2,3,4 }
That determines the sequence of colors that the particle should be drawn in based on its lifetime.
Overview
For a detailed explanation on each piece, see the above Trail Effect. Only the unique differences will be explained here.
-- fire effect
function fire(x,y,w,c_table,num)
for i=0, num do
--settings
add_fx(
x+rnd(w)-w/2, -- x
y+rnd(w)-w/2, -- y
30+rnd(10),-- die
0, -- dx
-.5, -- dy
false, -- gravity
false, -- grow
true, -- shrink
2, -- radius
c_table -- color_table
)
end
end
Parameters
function fire(x,y,w,c_table,num)
Exactly the same parameters as the Trail Effect above.
Loop
The same FOR LOOP is used to create a burst of particles each time, just like the Trail Effect.
Arguments
add_fx(
x+rnd(w)-w/2, -- x
y+rnd(w)-w/2, -- y
30+rnd(10),-- die
0, -- dx
-.5, -- dy
false, -- gravity
false, -- grow
true, -- shrink
2, -- radius
c_table -- color_table
)
Argument 1: x
= x+rnd(w)-w/2
Argument 2: y
= y+rnd(w)-w/2
This takes the x
and y
that was passed to this fire function, within a spawn zone set by w
, just like the Trail Effect.
Argument 3: die
= 30+rnd(10)
This sets the lifetime of the particle to be at least 30, and a maximum of 10 more than that, for a range of 30 to 40.
How? Check out the Trail Effect explanation above.
Change the height of the fire = change the number 30.
Change the length of the fadeout = change the number 10.
Argument 4: dx
= 0
Argument 5: dy
= -.5
The DX and DY are the momentum variables. Together they will create the direction and speed of each particle\'s movement.
In a fire effect, we want each particle to move upwards from the starting point. So we set X (left and right movement) to 0. And we set Y (up and down) to a negative number, so that the particles move up the screen.
Argument 6: gravity
= false
Argument 7: grow
= false
Argument 8: shrink
= true
We don\'t want the fire to fall, so gravity is turned off (false
).
We don\'t want the fire to get bigger, so grow is turned off (false
).
We do want the fire to get smaller, so shrink is turned on (true
).
Argument 9: radius
= 2
This is the size of the starting particle. We hard-set this to 2 here in the effect function because we don\'t think we will create different size fires. To create larger fires, we could have multiple of these effects happening near each other.
Argument 10: color_table
= c_table
Same as the Trail Effect. It uses the parameter passed to this function: c_table
It should be a table of numbers as a simple list, like this: { 1,2,3,4 }
That determines the sequence of colors that the particle should be drawn in based on its lifetime.
These are simply the sounds we created in the demo below. They are based on Gruber Music\'s suggestions and lessons in our Music section, so go there to understand how to use the SFX editor and all of the sound options to create just the right sound effects to match your particle effects!
Here are just examples to get you started:
function _init()
--particles
effects = {}
--effects settings
trail_width = 1.5
trail_colors = {12,13,1}
trail_amount = 2
fire_width = 3
fire_colors = {8,9,10,5}
fire_amount = 3
explode_size = 5
explode_colors = {8,9,6,5}
explode_amount = 5
--sfx
trail_sfx = 0
explode_sfx = 1
fire_sfx = 2
--player
player = {x=53, y=53, r=2, c=7}
end
function _update60()
--update particles
update_fx()
--player controls
if btn(0) then player.x-=1 end
if btn(1) then player.x+=1 end
if btn(2) then player.y-=1 end
if btn(3) then player.y+=1 end
if btn(4) then
fire(player.x,player.y,fire_width,fire_colors,fire_amount)
sfx(fire_sfx)
end
if btnp(5) then
explode(player.x,player.y,explode_size,explode_colors,explode_amount)
sfx(explode_sfx)
end
if btn(0) or btn(1) or btn(2) or btn(3) then
trail(player.x,player.y,trail_width,trail_colors,trail_amount)
sfx(trail_sfx)
end
end
function _draw()
cls()
--draw particles
draw_fx()
--player
circfill(player.x,player.y,player.r,player.c)
end
-->8
-- core particle functions
function add_fx(x,y,die,dx,dy,grav,grow,shrink,r,c_table)
local fx={
x=x,
y=y,
t=0,
die=die,
dx=dx,
dy=dy,
grav=grav,
grow=grow,
shrink=shrink,
r=r,
c=0,
c_table=c_table
}
add(effects,fx)
end
function update_fx()
for fx in all(effects) do
--lifetime
fx.t+=1
if fx.t>fx.die then del(effects,fx) end
--color depends on lifetime
if fx.t/fx.die < 1/#fx.c_table then
fx.c=fx.c_table[1]
elseif fx.t/fx.die < 2/#fx.c_table then
fx.c=fx.c_table[2]
elseif fx.t/fx.die < 3/#fx.c_table then
fx.c=fx.c_table[3]
else
fx.c=fx.c_table[4]
end
--physics
if fx.grav then fx.dy+=.5 end
if fx.grow then fx.r+=.1 end
if fx.shrink then fx.r-=.1 end
--move
fx.x+=fx.dx
fx.y+=fx.dy
end
end
function draw_fx()
for fx in all(effects) do
--draw pixel for size 1, draw circle for larger
if fx.r<=1 then
pset(fx.x,fx.y,fx.c)
else
circfill(fx.x,fx.y,fx.r,fx.c)
end
end
end
-->8
--example particle effects
-- motion trail effect
function trail(x,y,w,c_table,num)
for i=0, num do
--settings
add_fx(
x+rnd(w)-w/2, -- x
y+rnd(w)-w/2, -- y
40+rnd(30), -- die
0, -- dx
0, -- dy
false, -- gravity
false, -- grow
false, -- shrink
1, -- radius
c_table -- color_table
)
end
end
-- explosion effect
function explode(x,y,r,c_table,num)
for i=0, num do
--settings
add_fx(
x, -- x
y, -- y
30+rnd(25),-- die
rnd(2)-1, -- dx
rnd(2)-1, -- dy
false, -- gravity
false, -- grow
true, -- shrink
r, -- radius
c_table -- color_table
)
end
end
-- fire effect
function fire(x,y,w,c_table,num)
for i=0, num do
--settings
add_fx(
x+rnd(w)-w/2, -- x
y+rnd(w)-w/2, -- y
30+rnd(10),-- die
0, -- dx
-.5, -- dy
false, -- gravity
false, -- grow
true, -- shrink
2, -- radius
c_table -- color_table
)
end
end
3597
2 Nov 2022