Particle Programming |
By Julian Smart |
Fire... |
Here is a tutorial for making fire effects similar to the flames you see on either side of this text. In this tutorial, C-Script will be written in red. To see the effects that I made in action, just double-click the “shortcut to tutorial” in the tutorial folder. |
Let’s Begin... |
Let’s start by building and programming the particle emitter. This entity will shoot the flames from its origin. Just open MED and add a random primitive (I used a sphere, but it really makes no difference, because it will be invisible and passable), and save it as “fire_start.mdl” without the quotes.
Open your level in WED (if you don’t have one, just use the office level), and open its script. Scroll to the bottom and add this right on the end:
action fire { my.passable = on; my.invisible = on; while(1){ effect(flame_1,3,my.x,nullvector); waitt(1); } }
This action makes the particle emitter passable (so it won’t be an obstacle to other entities) and invisible. The while loop after that creates 3 particles at the position of its origin every sixteenth of a second. Make sure you type “waitt(1)” and not “wait(1)”. “waitt” is used to make sure the amount of particles created per second isn’t affected by the frame rate. Don’t try test this with your level, because it won’t work. We still need to add the code for the particles, and before we do that, we are going to make the particles. So, now it is time to open a good paint program (please, make sure it’s better than MsPaint!!). |
Here is an example flame particle, made with Paint Shop Pro 5. Very easy. This particle is used in the example demo, and in the screenshots at the beginning of the tutorial. This one is actually stretched out. The one I used is only 128x128.
It is quite easy to make. |
Create a new 128x128 image. Fill the background with pure black (0,0,0). Set the foreground colour to approximately 255 red, 70 green, 0 blue. With the brush set to an opacity of around 50, a size of around 40, hardness set to zero and density set to 100, plant a few random spots around the place. Only a few, like 10 maximum. The example on the right has 6 spots, for those who are curious. Now, set the size between 70 and 80, and put a couple of blobs in the middle, so that they connect the smaller blobs without covering the outer ones up, and there is a clear center “main” blob, like in the example to the right. If you want, you can just use a single blob for the particles, but that will look less detailed and more basic than my example. Now, save it as “flame_1.bmp” without the quotes, into your level’s directory. |
Now we are going to program the particles! If you haven’t done so already, open the script file that you added the particle emitter code to. Above it, just before the action starts, write these two functions:
function flame_2() { my.skill_a += 6 * time; my.alpha = 100 - abs(my.skill_a); my.size -= 0.3 * time; }
function flame_1() { my.x += random(16) - 8; my.y += random(16) - 8; my.z += random(16) - 8; my.bmap = flame; my.size = random(20) + 20; my.lifespan = 32; my.flare = on; my.alpha = 0; my.bright = on; my.skill_a = -100; my.gravity = -0.2; my.move = on; my.function = flame_2; }
Now, lets go through what happens when our previously-programmed particle emitter reaches the “effect” line. 3 particles will be created at the particle emitter’s position. These particles will be controlled by flame_1(). The first three lines in flame_1() slightly randomize their positions, to make a more believable effect. Next, my.bmap tells the particles what to look like. In the next line, the particles’ size is given, in quants. This is slightly randomized, again to make a more believable effect. It is given a lifespan of 32 ticks (2 seconds), and its flare flag is turned on. Flare makes the darker parts of the particle more transparent, Letting our particles fade towards the edges. The particle’s alpha flag sets its opacity. In this case, and at this point, we want it to be completely transparent, so alpha is set at 0. If it started at 100, particles would appear suddenly, making the fire look worse. Later on, you will see how I fade it in and out to keep it smooth. Then the bright flag makes sure that the particle’s colour is always added to what you can see through it. This is how we get the yellows in our fire — when there are more orange particles behind them, the particles add to the colour, and make it look yellow. my.skill_a is used later on, but in this case it needs to be set exactly to –100. I will explain later. my.gravity is how much gravity affects each particle, and is the speed at which it accelerates downwards. It is set to –0.2 so that the particles accelerate upwards (as you probably know, fire travels up). my.move allows the particles’ gravity to affect them, and the last line gives the particles a new function to follow. They will loop through this function for the rest of their existence.
Now! More explaining to do as the particles move to flame_2(). This function is very easy to understand, as long as you know what I’m trying to achieve. I’ll place the code just for this function here again so you don’t have to keep scrolling up.
function flame_2() { my.skill_a += 6 * time; my.alpha = 100 - abs(my.skill_a); my.size -= 0.3 * time; }
This is the function that the particles will loop through and repeat for the rest of their lives. I told you before that I would explain my.skill_a, so here I go. In this case, this skill directly affects the particles’ alpha level. Every sixteenth of a second (tick), my.skill_a increases by 6. It starts at –100, and by the time its lifespan runs out, it is near +100. In the second line, you see abs(my.skill_a). This means that it converts my.skill_a to a positive integer before using it. So, line two starts off by setting alpha to 100 (completely opaque), then subtracting my.skill_a converted to a positive integer. At the beginning of the particle’s life, this will be 100 - 100 (0), then, as my.skill_a gets closer to 0, so does the number being subtracted from alpha’s 100 level. alpha starts at 0, goes up to 100, then fades back to 0 again as my.skill_a reaches 0 and starts increasing to 100. This makes sure that the particle starts invisible, fades in until completely visible, then fades out to almost complete transparency before its lifespan runs out, and it ceases to exist. The last line, my.size -= 0.3 * time, makes the particles shrink as they get older.
Now, some might be thinking, “If the particle takes the same amount of time to fade in as it does to fade out, why do the particles look more opaque at the base of the fire?” My answer to that is, “The particles’ gravity is constantly strengthening, so at first they are all close together, because they are hardly moving. Then, as gravity strengthens, they get faster and are more spread apart near the end of their lives.”
OK. We are just about finished. Just one more thing. Go in your script to wherever you declare bitmaps (usually right near the top before the main function). We still need to declare our particle bitmap for it to work! Just type:
bmap flame = <flame_1.bmp>;
And now you are finished! Test your level, and it should work well! If you are using a different scale to mine (which you probably are) you may need to change the speeds and sizes, of the particles, but after fiddling around a bit, you should have no trouble at all.
Thanks for reading, and I hope you have learnt something,
Julian |