Home
Information
Meet the team
Forum of Youth
Contact Us

History
Story
Characters
Screenshots & artwork
Coding & animation
Soundtrack & SFX

Full
Demo
Wallpapers
Extras

Concept Art
Support & Hints/tips
Tutorials
Dev. Diary
Policy
Tutorials

Tutorial III - Realizing a Particle System with AGS

Introduction
In this article, I will describe how to design and effectively program a simple particle system using the AGS-Editor. I will use AGS version 2.72 for this demonstration, but the presented source code is going to compile with any older version as well - just don't try to use it with something before 2.71, it might still work but is completly untested! Please click here to download the editor if you haven't got it on your harddrive already.

With those things cleared up, we can finally move on to the topic: the particle system. In a realistic environment we could use those systems to represent flames, fountains or sparks, but we can also be a little more creative - it's up to you.

Before we even get started with the actual creation I would like to drop some lines on how exactly this tutorial is going to work! You will (hopefully) learn two things here: how to create a particle system on the one hand of course, on the other hand I will try to explain some crucial elements of programming as well. I will show you single functions (or parts of those functions) and then I will go through every single line and try to explain as good as possible what they do and how they work. I will not be able to go into great detail and teach you the basics of programming, but I am sure that even somebody little experienced in programming generally will be able to understand the basics sooner or later - in the end it's all logic (that's what programming is all about anyways)!

You will not need to copy and paste every single line from those examples (and you shouldn't, because I am not necessarily presenting them in the right order), I will give you the whole source code in one big chunk at the end.

Also note that we don't plan on implementing this technique into Indiana Jones and the Fountain of Youth at this point in time. It doesn't fit in with the traditional gamestyle that we're trying to accomplish in this game. But that doesn't mean that adventure games shouldn't use special effects such as this one - I've seen a game in which you play as a magician and whenever you used your magic wand it would create sparkles. Those were managed with such a particle system.

Getting started
Since we had New Year's just seven days ago (note: this shows how long it took me to write this - it's summer now), I decided to create a "New Year's Simulator" to show off our particle system. So if you are really into shooting rockets and throwing crackers, this might be your chance to have this kind of fun all through the year - legally.

screenshot
Our own "New Year's Simulator"
featuring a particle system...

When programming, it's essential that you have a very good understanding of what you want to achieve. If we put it the other way: it's impossible to code something if you don't know what you want. Knowing that, I'll give you a very short overview of how this "New Year's Simulator" is going to work practically: there will be a crosshair that you can move with your mouse. You will be able to left-click to emit a bunch of stars (which will look like a rocket exploding). To make things a little more interesting, you will also be able to right-click to change to the "repeating mode". In this mode, you will no longer be able to emit stars by left-clicking - the stars will be automatically emitted in smaller amounts (this will look more like sparkles coming from the cursor). Right-clicking again will of course switch to the first, "standard" mode again so that you can pretty much switch the "repeating mode" on and off by right-clicking. Also there will be a message on the screen to inform you about your controls (it will say something like: "Left-click to emit! Right-click to change mode!").

Now that we have an idea of what will be going on, it's time to start up AGS: select "Start a new game" and give it a title (I chose "Stars", but you can feel free to use whatever you want to). I'd recommend to use the "Empty Game Template". Create a room (we will only need one) and use whatever background image you think fits - you could for example draw a quick skyline with a nice evening sky or something completely different, once again it's entirely up to you. I'll go with a simple black background for the sake of simplicity. Also make sure to save the room in your game folder (not the compiled folder) with a name like "room1.crm" please.

Good, now we need two more graphical components, before we can start with the actual programming part. The first component is an image of a star. Basically our "particles" will look like this. But if we put things in relation, it will eventually strike our minds that it must be really bad having up to one-thousand stars on the screen at the same time and letting them all look the same! Thus we should decide to draw two or three pictures with little stars, but drawing them all a little different (choose a different color, change their sizes or give them a different shape). The second image we need is a simple crosshair - after all we need something that we can aim with just like in every good game.

stars
Those are the heinous graphics that I am gonna use as my three stars...

crosshair
And this is the crosshair...

Programmers call images like those above "programmer's art". There's this idea all around that programmers can't draw and artists can't program. I don't think very much of that (although it definately is true in my case)!

As you can see, I used pink to fill in those spaces, that are later going to be transparent (don't worry, AGS will do this automatically for us). I made all three star images 9x9 pixels big (but you can make them as big as you like to - you can even use different sizes for them) and I chose a size of 13x13 pixels for the crosshair.

Now let's go back into AGS and import our images. To make this as easy as possible, I recommend to first select and copy your first star image with your paint program and then right-click in the sprite manager and choose "Paste new from clipboard (320-res)". This will import your selection and make it a new sprite. Repeat this for all the star images and for the crosshair. If this way of importing sprites somehow fails to work for you, then just do it the old way: save all images on your harddrive somewhere, then right-click and choose "Import new sprite..." and select your images.

So now that we have all four images in AGS, we can finally begin with working on the really interesting stuff - the programming!

Some definitions first
Open up the global script by either pressing ctrl + g or by clicking on Script, then choose Edit global script. What you see is the mess that AGS makes us start with. Every programmer will find his very unique style when programming. Compare those two code snippets for example:

function draw_healthbar ( int x, int y, int amount )

// draws the healthbar somewhere with a given amount

{

    if ( amount > 0 )

    {

        RawDrawRectangle ( x, y, x + y, x - y );

    }

}

function DrawHealthBar(int X,int Y,int Amount){

  // Draws The Health Bar Somewhere With A Given Amount

  {

    if (Amount>0){

    RawDrawRectangle(X,Y,X+Y,X-Y);

    }

  }

Those are not the only two possibilities of course. As I said before, there are as many styles out there as there are programmers at least. I don't want to force you into a decision, however I will of course use my own style for this tutorial - if you feel like it, you can always replace all my function names, variable names etc. with your style!

So what I like to do, is to "convert" all that mess in our global script with the same mess - but in my style. Don't get me wrong, I will not change a thing - it will all work the same as before, I will only change how things look. You might wanna copy the following code and overwrite everything that is currently in your global script with this:

#sectionstart game_start

function game_start ( )

// is called on game start and before the first room loads

{


}

#sectionend game_start


#sectionstart repeatedly_execute

function repeatedly_execute ( )

// is called every frame

{


}

#sectionend repeatedly_execute


#sectionstart on_key_press

function on_key_press ( int keycode )

{

    if ( keycode == 27 )

    // if "ESC" was pressed

        // quit the game

        QuitGame ( 0 );

    else if ( keycode == 434 )

    // if "F12" was pressed

        // save a screenshot

        SaveScreenShot ( "Screenshot.bmp" );

}

#sectionend on_key_press


#sectionstart on_mouse_click

function on_mouse_click ( MouseButton button )

{


}

#sectionend on_mouse_click

Now that's a million times better to read and easier to understand. To check if you've done everything right, close the window, save the changes you made and run the game by either hitting ctrl + t or by clicking on File and on Test game! You'll enter the room that you created, but you won't be able to do anything in there. You'll see no cursor and left- or right-clicking will not result in anything. You should be able to leave by pressing either ESC or alt + x.

So let's get to the promised definitions. You need to put those at the beginning of your global script - above everything else that we pasted in before.

#define MATH_PI             3.14159265358979323846 // little mathematical helper

#define PARTICLES_MAX       1000                   // maximum number of particles

#define PARTICLES_NUM_CLICK 50                     // how many particles will be shown when mouse is clicked

The first line defines MATH_PI as 3.1415 - this is going to be useful when working with angles later on. As you may remember from your days at school, pi is a number that has infinite many characters after the dot. What we defined as MATH_PI is therefore only an approximation, but it will be close enough to work flawlessly!

Next we define PARTICLES_MAX to be 1000. The comment after that line explains it all rather well. This is going to be the absolute maximum number of particles that we are going to have on the screen at one time. If we have too many (over 1000), then we will just not add any new ones.

PARTICLES_NUM_CLICK is simply the definition of "a bunch". Remember when I told you that left-clicking will make "a bunch" of stars appear? This is exactly how many we will create when clicking.

The reason why we use those definitions is simple: Imagine you wouldn't use them for a second. Later in the script, you'd use all those numbers directly. It would work the same, but once you want to tweak some values here and there, it's gonna be a pain in the butt to change everything around. It's much easier like this. Say you want to spawn more stars when the user performs a left-click: simply define PARTICLES_NUM_CLICK as 200 instead of 50 - it's that easy. Imagine you would need to go through all the lines of your script later on and replace this value everywhere, depending on the size of your code this could take years!

One more thing we need is a global variable. We want to keep track whether the user is currently in "repeated mode" or not.

bool rep_mode; // keep track of whether we are in "repeated mode" or not

It's a boolean variable, which means that it can only be true or false. If it is true, then we are in "repeated mode" and if it is false, then we are not in "repeated mode".

Coming up with a structure and the concept of an array
I will take my time to explain the logic behind a structure first and then describe how we can come up with one for our specific case - the star.

Let's work with an example again. Imagine that you would want to code an enemy into one of your other games. This enemy will have different variables, that all have to do something (or you could say: they all hold some kind of information about the enemy). The list of variables could look like this:

// definitions

string enemy_name; // name of the enemy

int enemy_health;  // health of the enemy

bool enemy_alive;  // is the enemy alive or not?


// let's change his health

enemy_health = 32;

This would work, but using structures in such a case is way more flexible and clean. Take a look at this:

struct enemy

{

    string name; // name of the enemy

    int health;  // health of the enemy

    bool alive;  // is the enemy alive or not?

};


enemy our_enemy;


// now let's change his health

our_enemy.health = 32;

It doesn't change a whole lot you might say. But consider that we will have up to 1000 stars (or enemies in our example). Wouldn't we need to come up with 1000 variables? And wouldn't that somehow need to look like this:

// definitions

string enemy1_name; // name of the enemy 1

int enemy1_health; // health of the enemy 1

bool enemy1_alive; // is the enemy 1 alive or not?


string enemy2_name; // name of the enemy 2

int enemy2_health; // health of the enemy 2

bool enemy2_alive; // is the enemy 2 alive or not?


// skipping...


string enemy1000_name; // name of the enemy 1000

int enemy1000_health; // health of the enemy 1000

bool enemy1000_alive; // is the enemy 1000 alive or not?

Wrong! This would take ages and there wouldn't be a game with more than 32 enemies out there. We can only go the easy way if we use structures. First we will keep our enemy structure definition from above:

struct enemy

{

    string name; // name of the enemy

    int health;  // health of the enemy

    bool alive;  // is the enemy alive or not?

};


enemy our_enemy;

So we have one enemy defined, but what if we want a few more (for example 1000)? Doing it this way again makes no sense:

struct enemy

{

    string name; // name of the enemy

    int health;  // health of the enemy

    bool alive;  // is the enemy alive or not?

};


enemy our_enemy1;

enemy our_enemy2;


// skipping again...


enemy our_enemy1000;

The above would be pretty much the same as the first version - we would still need to define all our enemies manually. It would be way easier and faster if the computer could somehow automatically create those needed variables. And of course, since computers are so good at doing things automatically, there is that possibility, called the array. Those are made just for our problem. Check this out:

struct enemy

{

    string name; // name of the enemy

    int health;  // health of the enemy

    bool alive;  // is the enemy alive or not?

};


// define an array of enemies

enemy our_enemy[1000];

Finally, we have 1000 enemies created with one single line of definition code. Now if we would want to change the health value of - say - enemy number 742, we would do the following:

// change health value of enemy number 742 to 32

our_enemy[742].health = 32;

In the above line, we are accessing the health property of our enemy 742 and changing it to 32.

Allright, so far, so good. Now I will show you the way I designed the structure for each one of our stars. I didn't think of this all rightaway, usually when designing a structure you start with the obvious and the rest comes by itself. For example here, the first thing that came to my mind was: what does every single star need? A position probably. So I added two integer-variables for the position on the x- and the y-axis respectively. By asking yourself these questions, you can also determine just how complex you want your system to turn out later on. If you want all the stars to have a unique percentage of transparency (so that some stars would look almost transparent, while others might look almost non-transparent) to make your particles look even smoother and better, you should add a transparency-property to the star-structure, just as I did, for example.

struct particle

{

    float x;          // the position on the x-axis ( in pixel )

    float y;          // the position on the y-axis ( in pixel )

    float velocity_x; // the velocity on the x-axis ( in pixel )

    float velocity_y; // the velocity on the y-axis ( in pixel )

    float speed;      // the speed of the particle ( in pixel )

    float angle;      // the angle of the particle ( in radians )

    int time;         // the number of frames it has been living ( in frames )

    int sprite_slot;  // the sprite slot of the particle sprite

    float gravity;    // the amount of gravity that affects the particle

    int transparency; // the amount of transparency for the particle ( in percent )

};


// define our stars through an array

particle star[PARTICLES_MAX];

There it is. Although again I have added quite detailed explanations behind every single property, I will make sure to go over them again. The first thing you might notice before even looking at those properties is that I didn't name our structure "star" or "stars" but "particle". I did this to emphasize that you don't need to use your particles as stars.

The first two properties are the position on the screen for both axis. They are not defined as integers, but as floating values, which means our particles can have a position of 45.327 on the x-axis and 93.033 on the y-axis. Of course, when drawing our particles we will need to convert those numbers to integer values, because there is no pixel 45.327 on the screen. This would then get rounded down to 45 and be drawn there. But let's worry about that later (when we're talking about the drawing function), right now it's just important that we pretend there would be a pixel 45.327 and do our calculations later on using floating values.

The next two entries called "velocity_x" and "velocity_y" stand for the velocity of our particle. Since our particles will be emitted or launched into a set direction with a set speed, we will need to keep track of how fast our pixel is currently moving and in what direction.

Similarly, the following two properties mean pretty much the same thing, however this time we save the direction and speed of the particle in a different way (which is again useful for our later calculations) using one variable to keep track of the current speed and one of the current angle - usually this way of saving those two pieces of information is regarded as more intuitive. One thing to notice is that the angle is not given in degrees as one might expect, but in radians. Yet again this only matters for the calculations later on.

Finally it starts to get a little less heavy on the maths - the next line defines a simple variable called "time", which counts the number of frames that the particle has already been living. When spawning and launching or shooting off our particle, this variable will be set to the logical, inertial value of zero. Then this value will be increased every frame. More information on all this later on, of course, when we are actually including all this functionality.

And it continues as easy and simple to understand as the last line was - this time the variable is called "sprite_slot" and keeps track of the so-called sprite-slot that AGS gave our three star-sprites when we imported them. We need this because we drew three different looking sprites. We will set this to the according value (or sprite-slot number) after spawning the article. Why exactly we added this property may not be clear to you right now, but trust me, it is going to be useful later on, you don't need to understand the purpose of the variable at this point in time.

Now it starts to get interesting again - with "gravity". I am sure you all know what it is. You don't need to have this in a particle system, but it's definately a nice addition and has an interesting effect, plus it gives you the possibility to simulate particle systems not only in conditions similarly to the earth, but also on other planets, underwater etc. Now who doesn't need a scene with real-time particles on the moon in his adventure game? This variable is going to hold the amount of gravity that affects and pushes the particle. It could be 0.7, which would mean that the particle is being pushed down every frame by almost one pixel, it could also be -3.7, which would push the particle up quite quickly as if it were underwater and filled with oxygen or something. Of course, this value is only realistic if we increase it over time - take this as an example: if you pick up an object in your room and drop it on the floor, it will start falling down slowly for a few milliseconds and then accelerate pretty fast. I will talk about this again and explain how exactly we are handling this physical property once it's time for us to actually do stuff with the variable.

Last, but not least, there is the variable "transparency" left, which I talked about earlier when giving you an example of a property. I already gave you information on how this will work, basically every particle is going get a random transparency-value and will thus be drawn with a specific amount of transparency. Of course, we will not change this value while the particle lives (this would cause the particle to flicker, because every new frame - 40 times a second, the amount of transparency for every particle would be changed), we will set this value once when we spawn the particle and then leave it at that, so that this particle sticks to its amount of transparency for its whole life. Because we will draw lots and lots of particles, some will appear to be further behind and others closer to the camera, just because the transparency makes our eyes believe that we see something in three dimensions when there are actually only two. One more thought on dimensions: we could also add some kind of scaling variable, that makes particles bigger or smaller. This would give it a real three-dimensional appearance, but that is too much for this article. If you understood the concepts here, it would be the perfect task to do and try on your own afterwards!

That closes the description of our particle-structure. One last thing left to mention is the last line in the code I gave you. Right after the definition of our structure, I defined our stars as an array. The array is called "star", it is of the type "particle" (obviously, because that's how we named our structure) and it contains not 1000 (as in our example before), but PARTICLES_MAX stars, which we defined as 1000 (I can literally hear you screaming out: hey, that's the same, isn't it? Well, as I said before, by using PARTICLES_MAX instead of a real number like 1000, we can change this number easily later on). Additionally, you can see another reason for why we named our structure not star but particle earlier - otherwise that name would've already been taken and we would need to call our objects sparks or something else. So, basically we now have the possibility to access our stars just like in those examples I gave you earlier when talking about arrays:

// change transparency value of star number 325 to 83

star[325].health = 83;

The implementation
What do we have so far? Some definitions of values (MAX_PARTICLES for example), a definition for our particle-structure and a definition for our star-array. What is missing? You could say the counterpart to definitions: implementations. Before, we only added elements together and made big plans - in our head. For example when I told you we'd need to increase the gravity variable over time so it looks more real. This doesn't happen by itself, we still need to implement it. In order to do this, we need three functions that all have their unique purpose.

The first function is called "create_star ( ... )". As the name implies, it's there to create (spawn, launch, give birth or whatever you want to call it) a new star. Without further complications, here it is:

function create_star ( float x, float y )

// creates a particle at a given location

{

    // define a variable for the id-number of the star, that we want to create

    int star_id;


    // use a backward while loop to determine which star should be created

    // we should then come up with the star with the smallest 'star_id' value

    // but which still has its framecounting time variable set to 0


    int i = PARTICLES_MAX - 1;

    while ( i > 0 )

    {

        if ( star[i].time == 0 )

        // if the framecounting time variable equals 0

        {

            // then we will mark this star

            star_id = i;

        }


        // continue the loop

        i--;

    }


    // now that we have the ID value for the star we want to create, we now have to

    // set some initial values / settings for it


    // set its x position to the x value this function gives us by parameter

    star[star_id].x = x;


    // set its y position to the y value this function gives us by parameter

    star[star_id].y = y;


    // set its sprite slot to random ( between 1000 and 1002 )

    star[star_id].sprite_slot = Random ( 2 ) + 1000;


    // set its gravity value to 0

    star[star_id].gravity = 0.0;


    // set its transparency value to something between 50 and 100

    star[star_id].transparency = Random ( 50 ) + 50;


    // and set its framecounting time variable to 1

    star[star_id].time = 1;


    // now choose a speed from 0 to 99

    star[star_id].speed = IntToFloat ( Random ( 100 ) + 50 );


    // and choose an angle from -1 to 1 ( in radians )

    star[star_id].angle = IntToFloat ( Random ( 100 ) * ( FloatToInt ( 2.0 * MATH_PI ) / 2 ) );


    // then set its velocity on the x-axis to its correct value

    star[star_id].velocity_x = Maths.Cos ( star[star_id].angle ) * star[star_id].speed;


    // and then set its velocity on the y-axis to its correct value

    star[star_id].velocity_y = Maths.Sin ( star[star_id].angle ) * star[star_id].speed;

}

That seems like quite a bunch of stuff, but it's actually not as bad. Let's talk this over, seperate it into parts and put it into context. The first thing we should notice is that our function takes two parameters called "x" and "y". Those will determine where exactly on the screen the new star is going to be created at, if that makes sense. This way, we don't have to worry about the starting position of the new particle inside this function and using those two parameters, we can decide the spot when calling our function. If we wanted to spawn a particle where the mouse is, we would call the function like this:

// spawn one star where the mouse is

create_star ( mouse.x, mouse.y );

However, we might also want to create a star at a given location on the screen. In this case, we could simple replace the two paramters ("mouse.x and "mouse.y" in the above example) with real numbers.

Another thing that we should think about before rushing into the function is the problem of randomness. For something as logical as a computer, nothing is random. Your computer can't give you a real random number. Nobody can tell your computer to give you a random number, while everybody can tell your computer to give you one specific number or all numbers from zero to ten or all prime numbers in a certain range - but there is no way how a computer could just print out a random number. That's because computers can't decide, which is what our brain is for. If you ask a human to say either "one" or "zero", he can decide for one of the two choices in your head and say aloud his result. The number is not even really random in this case, it's just a decision by that person, but since other people won't know what he thinks, you can assume the answer as random. Or we can roll a dice, the result is not really random - actually it's decided by the laws of physics (air pressure, forces etc.), but since nobody can control those laws enough, the result of a thrown dice can be considered random. Back to the computer: it can't do that. In order to decide something, you would always need to assign some kind of condition. For example, if you imagine a computer is asked for a "random number" every five or six months, the computer could possibly connect to the internet and search for the word "there" on some news title-page. It's likely that somewhere in one of the headlines, the word "there" exists, if it does the computer could return "one", if the word can't be found, the computer could return "zero". Let's imagine another situation, this time a computer with access to heat sensors could be asked for a random number every few weeks. This computer could scan the temperature and return "one" if it is above a certain level or "zero" if it is not. However, most computers don't have that functionality, some don't even have access to the internet (and of course those two computers would always return the same result atleast for one day, and we need different random numbers simultaneosly). What we need is some information that every computer can offer, but that nobody can know ahead of time. And there is: the date and time. You might say: hey, I know what date it is and I know what time it is, how can we turn that information into random numbers? You need to think in small units. In milliseconds for example. Imagine you take the time of the day with lots of numbers even after the dot (for example, you start our application or game exactly when it is 12:36.385632 - twelve hours and 36.385632 minutes after midnight) - nobody knows this exact number, it's very likely to be different every time the user starts our game and you could then assign a condition like this to our case: multiply the number of minutes by 1000000, which would give us 36385632 and then see whether that number is dividable by two without leaving a rest - in our case it is, so return "zero" and not "one". There we have a pseudo-random number, not really random, but to the user or player later, it seems random. Whether you have your time settings set correctly in your system or wrong (for example showing a wrong date or time of the day) doesn't matter in this case. This is how most, if not all random number generators work - and this is how we can get AGS to supply us with much-needed random numbers. Luckily, we don't need to do all this by ourselves, that functionality is already included in AGS. The function is called "Random ( int max_value )" and returns a random number between 0 and "max_value". We will use this function heavily, because when spawning a star, we will need lots of random influence (such as a random value for the amount of transparency for example).

Back to the function: in order to understand better what the function does, we will part it up. The following is part one out of two:

// define a variable for the id-number of the star, that we want to create

int star_id;


// use a backward while loop to determine which star should be created

// we should then come up with the star with the smallest 'star_id' value

// but which still has its framecounting time variable set to 0


int i = PARTICLES_MAX - 1;

while ( i > 0 )

{

    if ( star[i].time == 0 )

    // if the framecounting time variable equals 0

    {

        // then we will mark this star

        star_id = i;

    }


    // continue the loop

    i--;

}


What we do in those lines is a search for an id-number of a star. Remember, we defined an array of stars like this:

particle star[PARTICLES_MAX];

It is easier to imagine this array as a list of some sort. Just imagine that we have a huge list with PARTICLES_MAX entries (1000 in our case). Every entry is a star and has a unique number which we will call the id-number. Recognize the following line? I already used it before when we talked about arrays. The id-number of the star is 325 in the example.

// change transparency value of star number 325 to 83

star[325].health = 83;

Now, in part one of the function, we not only search for any id-number (which can be between "0" and "PARTICLES_MAX" or "1000" of course with "0" being the first entry in our list and "1000" the last), but we also want to find the id-number of a special star. Actually, what we need is the id-number of a star that is ready to be created - in order to be ready, we must check that it isn't currently in use. If we wouldn't do this, we'd run the risk of taking a star away from the screen that is being display currently and respawn it. What we want is find an inactive star and spawn it, however. But let's go step by step.

// define a variable for the id-number of the star, that we want to create

int star_id;

This first line simply creates an integer variable to hold the id-number that we are searching for. The star with this id-number is going to be created by our function.

// use a backward while loop to determine which star should be created

// we should then come up with the star with the smallest 'star_id' value

// but which still has its framecounting time variable set to 0


int i = PARTICLES_MAX - 1;

while ( i > 0 )

{

The following lines above are a little harder to understand. As the commentary says, we use a backward while-loop to find a good id-number of an inactive star. First we define "i" as the id-number of the last entry to our list of stars (the id-number of this last entry is actually not "PARTICLES_MAX" as you'd expect, but "PARTICLES_MAX - 1", because the first entry in our list is not "1" but "0" - we defined a list with 1000 entries, starting with "0" that gives us "999" as last entry - which again is "PARTICLES_MAX - 1"). Then we start our while-loop with the last entry and loop backwards through to the first entry (thus, it's called a backwards while-loop - it'd be forward if we started with the first entry and looped to the last). So, in the following pieces of code, we will have an index to the star that we are currently "taking a look at" called "i", what we do is investigate every single star in the list and check whether the id-number of the star is a "good" one - meaning whether it's inactive or not.

if ( star[i].time == 0 )

// if the framecounting time variable equals 0

{

    // then we will mark this star

    star_id = i;

}

This is what the check looks like. The star is active, if it's frame-counting time variable is set to "0". We could have added a flag (a boolean variable) to every star to see whether the star is active or not, but it's simply easier and better on our resources to use another variable for this - the "time" variable fits that purpose perfectly. Now we only need to keep in mind to always set the "time" variable of every inactive star to "0" later on in the code. To mark the current star's id-number as "good", we simply save "i" (which is the current id-number) to our "star_id" variable that we created before. But even if we found one working id-number, the loop and the checks will continue until we arrive at the very first entry in our list. The content of the "star_id" variable will be overwritten again and again whenever a new, "good" id-number comes along.

    // continue the loop

    i--;

}

In those last lines of part one, all we do is make sure the loop continues until we have checked the first entry and thus every single star. After that is done, we can now safely assume that the "star_id" variable now contains the smallest possible id-number that has a star associated to it, which is still inactive. It's the smallest possible number because we loop backwards through the list of stars. Imagine for a second that all 1000 stars would be active and drawn on the screen somewhere except the star with the id-number 988 and the star number 21. The looping check would start at the last star number 999. This one would fail the check, because it is active at the moment. Then it would continue with star 998, which would fail the check as well and continue. It would pass the check and not fail with star number 988, because it is still inactive and mark down id-number 988 as possible. Then, however, it will still continue running through the rest, failing all of them, except number 21. There it will pass the check again and now override our old possibility 988 with the new one, that is just as possible as the older number.

Okay, I hope I made everything clear to this point. Again, we can now assume "star_id" to be the id-number of the star that we want to create. This makes us ready for part two of the function:

// now that we have the ID value for the star we want to create, we now have to

// set some initial values / settings for it


// set its x position to the x value this function gives us by parameter

star[star_id].x = x;


// set its y position to the y value this function gives us by parameter

star[star_id].y = y;


// set its sprite slot to random ( between 1000 and 1002 )

star[star_id].sprite_slot = Random ( 2 ) + 1000;


// set its gravity value to 0

star[star_id].gravity = 0.0;


// set its transparency value to something between 50 and 100

star[star_id].transparency = Random ( 50 ) + 50;


// and set its framecounting time variable to 1

star[star_id].time = 1;


// now choose a speed from 0 to 99

star[star_id].speed = IntToFloat ( Random ( 100 ) + 50 );


// and choose an angle from -1 to 1 ( in radians )

star[star_id].angle = IntToFloat ( Random ( 100 ) * ( FloatToInt ( 2.0 * MATH_PI ) / 2 ) );


// then set its velocity on the x-axis to its correct value

star[star_id].velocity_x = Maths.Cos ( star[star_id].angle ) * star[star_id].speed;


// and then set its velocity on the y-axis to its correct value

star[star_id].velocity_y = Maths.Sin ( star[star_id].angle ) * star[star_id].speed;

Being done with the great search for a proper id-number, all that's left to do is to actually spawn and launch our star. In order to do that, we simply set all the different properties that every star has (which we defined in our particle-structure) to the inertial values we want for our star with the id-number "star_id".

The first two properties we set for our star is its position on both axis. Remember where "x" and "y" came from? They where the parameters from our function. We don't have to worry about the location, the person that calls our function later on will have to supply us with a location. In the first two lines, all we do is to place the star exactly where it should be (if the function is called like this: "create_star ( 49, 84 )", we would place the star at 49, 84 respectively).

Then we set the sprite-slot for our new star. By doing this, we decide how exactly it is going to look like. We drew three different looking variations and we need to choose one now. You could simply set this to the sprite-slot number of one of your graphics, but then all stars would look the same. So this is the first time that we will need random influence. We set the sprite slot to "Random ( 2 ) + 1000". "Random ( 2 )" can return 0, 1 or 2. We add 1000, so the result is either 1000, 1001 or 1002. Those numbers have to be the three sprite-slot numbers of our sprites. If your numbers are different or if you drew and imported more sprites, you will need to alter this piece of code. To find out what the sprite-slot of one of your graphics is, you need to click on Sprite manager in the AGS editor, then locate the sprite that you want to know the slot-number of and look what the number right underneath the image says. That's the sprite-slot. You can also change this number by right-clicking on the image and selecting Change sprite number, but that is not really a good idea, because it might mess up other stuff. Of course, you need to have all numbers lie in a row (like 1000, 1001 and 1002), but that should be automatically the case if you imported them one after the other.

After that, we simply set the gravity that currently affects our star to "0". This is not realistic of course, but for simplicity's sake we will just handle gravity a little bit different than we should and make the gravity start this way without force.

Then we decide how transparent the star will be drawn. We want to set the amount of transparency to some value inbetween 50 and 100 (in percent, where 100 actually means completly visible and 50 half-visible), because otherwise a lot of stars would get very transparent and hard to see, which would mean wasting resources.

In the following line, we set the framecounting variable "time" to 1. This means that our particle is now considered active and alive instead of inactive.

The next four lines set both speed and angle as well as the velocity on both axis to random values. The calculations made are pretty basic, it chooses a speed between 50 and 150 for the particle (you could change that to a speed between 0 and 10 if you wanted very slow particles etc.), a completly random angle (recalculated in degrees it would choose an angle between 0 and 360 degrees, so our particle can literally go anywhere - again, you could restrict that and only allow particles to be spawned approximately upwards in a 50 degree cone for example by using your knowledge about the "Random"-function as well as about the maths behind radians) and finally in the last two lines, we calculate the correct values for the velocity on both axis by taking the cosine and sine of the angle and multiplying this value by speed. If you don't understand the maths behind this, I'd urge you to get a basic grasp on trigonometry before trying to change those lines.

This pretty much sums up the discussion on our first function. We can now create stars as we like. However, we need more control - the next function is going to be called "handle_stars ( )" and is going to do exactly that - handling and taking a look over all our stars. Instead of being called whenever necessary (as it was with the first function - we only called it whenever we needed it meaning whenever we wanted to spawn a star), we will call this function every single frame. More on when to call the functions later, first comes the complete second function:

function handle_stars ( )

// this function needs to be called every single frame and handles the stars

{

    int i = 0;


    // loop through all stars

    while ( i < PARTICLES_MAX )

    {

        if ( star[i].time > 0 )

        // if the star is activated on the screen somewhere

        {

            // move it

            star[i].x += star[i].velocity_x * IntToFloat ( GetGameSpeed ( ) ) / 1000.0;

            star[i].y += star[i].velocity_y * IntToFloat ( GetGameSpeed ( ) ) / 1000.0 + star[i].gravity;


            // now actually draw the star at its position, using its own sprite slot and its own transparency value

            RawDrawImageTransparent ( FloatToInt ( star[i].x ), FloatToInt ( star[i].y ), star[i].sprite_slot, star[i].transparency );


            // then increase the gravity that affects the particle

            star[i].gravity += IntToFloat ( star[i].time ) / 100.0;


            // and then increase the framecounting time variable

            star[i].time++;


            if ( FloatToInt ( star[i].x ) < 0 || FloatToInt ( star[i].x ) > 320 || FloatToInt ( star[i].y ) > 200 )

            // if the star has left the screen

            {

                // then remove it

                remove_star ( i );

            }

        }


        // continue the loop

        i++;

    }

}

I know, it's a big one again. Don't worry though, we'll do things the same way we did them before with the other function. First things first: this function doesn't take any parameters, so we'll call it like this: "handle_stars ( )" later on. The actual purpose of the function is to check all active stars and reposition them and make sure they die somehow (otherwise they would never turn inactive again and could never be reborn).

As you may have noticed, this function is not quite as large as the other and we already have lots of stuff cleared and out of the way, so this time I don't think it's necessary or logical to part this function up again, let's rather just get started with a line-to-line analysis!

int i = 0;

// loop through all stars

while ( i < PARTICLES_MAX )

{

We start off by looping through all particles again. This time however, it's a simple forward loop, meaning we start with the first entry in our list and loop through until we reach the last entry - that's because the order we handle the stars doesn't matter anymore, when we did this earlier in the first function, the order did matter quite a lot.

if ( star[i].time > 0 )

// if the star is activated on the screen somewhere

{

The lines above are a check that is performed on every star (because we are looping). We are checking whether the "time" variable of the star is greater than "0" (which means that every star with a "time" variable of "1" or greater will pass the test). Actually, this means that we only continue examining all alive and active stars - the inactive stars aren't drawn to the screen and we don't need to care about those at all.

// move it

star[i].x += star[i].velocity_x * IntToFloat ( GetGameSpeed ( ) ) / 1000.0;

star[i].y += star[i].velocity_y * IntToFloat ( GetGameSpeed ( ) ) / 1000.0 + star[i].gravity;

This is what we first do with all active stars - we move them. To do this, we simply change their x- and y-position variables and add the velocity to it. In order to make things look better and smoother, we multiply the velocity on both axis by "GetGameSpeed ( ) / 1000" - GetGameSpeed ( ) returns how many frames are currently drawn to the screen per second, usually this should return 40 as that number is the AGS standard. This means we multiply the velocities by 40 / 1000 or 0.04. If you don't understand why exactly we do the multiplication, don't worry, just include them for now. After being done with everything, you can come back and erase the lines and you'll see what's happening, the velocities are just too big and in less than half-a-second all particles are out of the screen. Also, we take gravity into account by adding the amount of gravity to the vertical position of our particle. That is important, because if we wouldn't take gravity in this calculation, we could set it to any value we want, it wouldn't matter.

// now actually draw the star at his position, using his own sprite slot and his own transparency value

RawDrawImageTransparent ( FloatToInt ( star[i].x ), FloatToInt ( star[i].y ), star[i].sprite_slot, star[i].transparency );

In the above line, we finally draw the particle. It's important that this is still only done if our previous check passed (meaning we really only draw active and not the inactive particles - if we wouldn't do that, you would also see all kind of dead particles flying around). In order to draw, we call the function "RawDrawImageTransparent ( ... )". This function simply takes a sprite from the sprite manager of AGS and draws it to the screen. The first two parameters are the x- and y-position, the third one is the sprite-slot and the fourth is the amount of transparency for our star. We pass on all the information we have about the star to the function here. When we pass the position, we also convert the floating value variables "x" and "y" into integer variables by calling "FloatToInt ( ... )", which does exactly that. Remember how I told you that we would use floating values and imagine that our star can be not only at positions like 42, 524, but also at 65.62, 29.63? We used floating values all the time to do the calculations, but now the point in time has come to convert them into real coordinates, because the function "RawDrawImageTransparent" doesn't accept anything else than integer variables as x- and y-parameter - which is logical, because there's no way how this function could draw an image at location 65.62, 29.63 - there are only 320x200 pixels on the screen (if we are in that standard resolution).

// then increase the gravity that affects the particle

star[i].gravity += IntToFloat ( star[i].time ) / 100.0;


// and then increase the framecounting time variable

star[i].time++;

Next, we increase the gravity that affects the particle for the next frame. We calculate the next amount of gravity, by taking the amount of frames that the particle has been living, divide that by 100 and add it to the old value of gravity. This makes sure the gravity grows stronger and stronger as the particle lives and results in a realistic looking effect.

The second line is even simpler and just increases the time variable. Since we have handled this star, we can now make him one frame older.

if ( FloatToInt ( star[i].x ) < 0 || FloatToInt ( star[i].x ) > 320 || FloatToInt ( star[i].y ) > 200 )

// if the star has left the screen

{

    // then remove it

    remove_star ( i );

}

We're almost done with our function, the stars are handled, but one thing is still missing: the death of a star. We need to kill them in order to make sure that there are always enough dead and inactive particles in our list so that there are also enough when we need to spawn new ones. Thus, we perform a check to see whether the active star is still on the screen - if it has left the screen, we remove it by calling a function called "remove_star ( ... )", which we will talk about in just a few moments, important is, however, that we pass our index (the id-number to our current star) to the function, so that it removes exactly the star with this number. To check whether or not they have left the screen, we simply take the x- and y-position of our star and see whether those values are greater than the screen boundaries. If you used another resolution than the standard 320x200, then you'll need to change those values. We check for both the left and right, as well as the lower boundary. We do not check for the upper one, because imagine the user shoots out stars from somewhere on the screen, some of those stars might cross the upper screen boundary and would get deleted - but the user would expect them to come back and fall down because of the gravity we included. He won't expect them to come back from the sides or from the bottom of the screen, because our gravity only pulls down, however. We can only assume safely that the user doesn't see them anymore and doesn't want to see them anymore if they have left the side- or bottom-screen edge.

        }


        // continue the loop

        i++;

    }

}

The rest of the function just closes brackets and continues the loop by increasing "i". We are almost done with the implementation, the stars are being handled and we can create them, the only thing missing (we talked about it above) is removing them again. Here is the function called "remove_star ( ... )":

function remove_star ( int star_id )

// removes a star

{

    // set its framecounting time variable to 0

    star[star_id].time = 0;

}

Ah, isn't that refreshing? Finally, a short function. It's so short that you might even say: why did we even write a function for that? We could call the only line this function calls directly, couldn't we? The answer is yes, we could, but I thought it would just be tidier and cleaner to have a function for everything, creating, handling and removing a star.

// set its framecounting time variable to 0

star[star_id].time = 0;

As you can see, the only thing we do here, is set the "time" variable of the star that is supposed to be removed to "0". This means that it won't be drawn anymore and is thus marked inactive and dead again. We know which star it is that we want to remove, because the function gives us the id-number by parameter called "star_id", so that if somebody calls this function like this: "remove_star ( 322 )", we would automatically remove star number 322 and so on.

The rest
Allright, we are done with the definition and the implementation. We have our particle system realized and written, but we still need to try it out. In order to do this, we need to call our functions. Now the thing with the "repeated mode" comes into play, so let's make sure we all remember what we wanted to do. The user will be able to left- and right-click. Left-clicking will spawn a bunch of stars if we are not in "repeated mode", right-clicking will switch "repeated mode" on and off. In repeated mode, the stars will automatically spawn one every frame. Allright, this is the remaining code:

function repeatedly_execute ( )

// is called every frame

{

    // reset the screen every frame

    RawRestoreScreen ( );


    // choose white as color

    RawSetColorRGB ( 255, 255, 255 );


    // draw a message to the screen

    if ( !rep_mode )

    // if we aren't in "repeating mode"

    {

        // display first line of message

        RawPrint ( GetViewportX ( ) + 10, GetViewportY ( ) + 10, "LEFT-CLICK TO EMIT!" );

    }


    // display second line of message

    RawPrint ( GetViewportX ( ) + 10, GetViewportY ( ) + 20, "RIGHT-CLICK TO CHANGE MODE!" );


    if ( rep_mode )

    // if we are in "repeating mode"

    {

        // then spawn one star every frame

        create_star ( IntToFloat ( GetViewportX ( ) + mouse.x ), IntToFloat ( GetViewportY ( ) + mouse.y ) );

    }


    // handle the stars

    handle_stars ( );

}


function on_key_press ( int keycode )

{

    if ( keycode == 27 )

    // if "ESC" was pressed

        // quit the game

        QuitGame ( 0 );


    else if ( keycode == 434 )

    // if "F12" was pressed

        // save a screenshot

        SaveScreenShot ( "Screenshot.bmp" );

}


function on_mouse_click ( MouseButton button )

{

    if ( button == eMouseLeft )

    // if it was a left-click

    {

        if ( !rep_mode )

        // if we are not in "repeating mode"

        {

            int i = 0;


            while ( i < PARTICLES_NUM_CLICK )

            // loop through all stars that we want to create

            {

                // create the star at mouse coordinates

                create_star ( IntToFloat ( GetViewportX ( ) + mouse.x ), IntToFloat ( GetViewportY ( ) + mouse.y ) );


                // continue the loop

                i++;

            }

        }

    }

    else

    // if it was a right-click

    {

        // then change rep_mode around

        rep_mode = !rep_mode;

    }

}

I looks like a lot, but we'll do this quickly, because it really isn't all that complicated and you should have really gotten a good idea of what to do when by now. Those functions are not created by us, they are AGS functions. Remember what AGS made us start with? We changed that, but those functions where in there. Since now, they have all stayed empty, but that's being changed right now.

The first function is called "repeatedly_execute ( )" and is called every single frame by AGS as the name implies.

function repeatedly_execute ( )

// is called every frame

{

    // reset the screen every frame

    RawRestoreScreen ( );


    // choose white as color

    RawSetColorRGB ( 255, 255, 255 );


    // draw a message to the screen

    if ( !rep_mode )

    // if we aren't in "repeating mode"

    {

        // display first line of message

        RawPrint ( GetViewportX ( ) + 10, GetViewportY ( ) + 10, "LEFT-CLICK TO EMIT!" );

    }


    // display second line of message

    RawPrint ( GetViewportX ( ) + 10, GetViewportY ( ) + 20, "RIGHT-CLICK TO CHANGE MODE!" );


    if ( rep_mode )

    // if we are in "repeating mode"

    {

        // then spawn one star every frame

        create_star ( IntToFloat ( GetViewportX ( ) + mouse.x ), IntToFloat ( GetViewportY ( ) + mouse.y ) );

    }


    // handle the stars

    handle_stars ( );

}

The one function that we definately need to call here is "handle_stars ( )" obviously, because we already said we wanted and needed it called every frame. Again, this doesn't happen automatically, we need to do this. This function also draws messages to the screen explaining the mouse controls and does a few other things, let's go through this:

// reset the screen every frame

RawRestoreScreen ( );

The first thing we do every single frame is to call this AGS function. It simply clears (despite its name) the screen and makes sure it's fresh again, otherwise, every particle we drew would stay there forever, not being replaced and overwritten by the new images for every new frame but being drawing over the old frame.

// choose white as color

RawSetColorRGB ( 255, 255, 255 );


// draw a message to the screen

if ( !rep_mode )

// if we aren't in "repeating mode"

{

    // display first line of message

    RawPrint ( GetViewportX ( ) + 10, GetViewportY ( ) + 10, "LEFT-CLICK TO EMIT!" );

}


// display second line of message

RawPrint ( GetViewportX ( ) + 10, GetViewportY ( ) + 20, "RIGHT-CLICK TO CHANGE MODE!" );

Those lines draw the help-messages to the screen to explain the mouse-controls. Two lines are drawn, the first line displays the message "LEFT-CLICK TO EMIT!" whenever we are not in "repeating mode" (in "repeating mode" this statement isn't true because the user can't emit stars by left-clicking - they are being emitted automatically) and the second line "RIGHT-CLICK TO CHANGE MODE!" is always being displayed, because the control always works. But the first thing we do is call "RawSetColorRGB ( ... )". By calling this function we simply select a color for the text of the following messages. I chose white, you could change that. The three parameters this function takes are the RGB-values of the color you want to select. Pure red would be ( 255, 0, 0 ), pure green ( 0, 255, 0 ) and pure blue ( 0, 0, 255 ). You can play around with the values and come up with a better color if you like!

We then draw the messages (actually we first run through a check whether we are in "repeated mode" or not to decide whether we should print the first message or not) by using the AGS functions "RawPrint". The first two parameters those functions take is the position where the text is going to be drawn. "GetViewportX ( )" and "GetViewportY ( )" simply return the left and upper screen corner coordinates. We then add a few pixels to this corner, because we don't want the text to be exactly in the left-upper-corner but a little more to the right and lower. The third parameter is obviously the message itself.

if ( rep_mode )

// if we are in "repeating mode"

{

    // then spawn one star every frame

    create_star ( IntToFloat ( GetViewportX ( ) + mouse.x ), IntToFloat ( GetViewportY ( ) + mouse.y ) );

}

The above part of the function checks whether we are in "repeated mode". If we really are, then we automatically create one star every frame where the mouse is at. Since "mouse.x" and "mouse.y" are integer variables from AGS, we also convert them back into floating point values because that's how we worked and that's how our own "create_star ( ... )" function expects the two parameters for the location.

// handle the stars

handle_stars ( );

The last thing to do is to call our "handle_stars ( )" function. I already stated above that we wanted this function to be called every frame. Until now, AGS didn't know a thing about it, but now it is going to call it every frame and make sure all stars are handled! The upcomming function handles what happens when the user pushes a key on his or her keyboard.

function on_key_press ( int keycode )

{

    if ( keycode == 27 )

    // if "ESC" was pressed

        // quit the game

        QuitGame ( 0 );


    else if ( keycode == 434 )

    // if "F12" was pressed

        // save a screenshot

        SaveScreenShot ( "Screenshot.bmp" );

}

I will not go into great detail with this simple function called "on_key_press ( ... )". It's called automatically by AGS if the user hits a key. We check whether it was the escape-key, in which case we close the game by calling "QuitGame ( ... )", or whether it was the F12 key, in which case we take and save a screenshot by calling "SaveScreenShot ( ... )". If it was any other key, we don't care about it. That's quite minimalistic, but is enough for our simple application.

This brings us to the last function of our little project - "on_mouse_click ( ... )". This one is similar to the small function above, it just doesn't concern keys on the keyboard, but the mouse-buttons.

function on_mouse_click ( MouseButton button )

{

    if ( button == eMouseLeft )

    // if it was a left-click

    {

        if ( !rep_mode )

        // if we are not in "repeating mode"

        {

            int i = 0;


            while ( i < PARTICLES_NUM_CLICK )

            // loop through all stars that we want to create

            {

                // create the star at mouse coordinates

                create_star ( IntToFloat ( GetViewportX ( ) + mouse.x ), IntToFloat ( GetViewportY ( ) + mouse.y ) );


                // continue the loop

                i++;

            }

        }

    }

    else

    // if it was a right-click

    {

        // then change rep_mode around

        rep_mode = !rep_mode;

    }

}

It's a bit longer than the one before, because we have more events connected to the mouse than the keyboard.

if ( button == eMouseLeft )

// if it was a left-click

{

This is a simple check to see what mouse button was clicked. We only proceed if it was the left button.

if ( !rep_mode )

// if we are not in "repeating mode"

{

    int i = 0;


    while ( i < PARTICLES_NUM_CLICK )

    // loop through all stars that we want to create

    {

        // create the star at mouse coordinates

        create_star ( IntToFloat ( GetViewportX ( ) + mouse.x ), IntToFloat ( GetViewportY ( ) + mouse.y ) );


        // continue the loop

        i++;

    }

}

In the lines above, another check follows, this time we only proceed if we are not in "repeated mode". Thus, if the user left-clicked and we are not in "repeated mode", we create a star at the location of the mouse by calling our "create_star ( ... )" function. The loop is there to repeat the "create_star ( ... )" command until a bunch of stars are created. Remember that we defined "a bunch of stars" in the beginning of the article with PARTICLES_NUM_CLICK. So, overall this part above spawns a bunch of stars where the mouse is, if we left-click and are not in "repeated mode". That's one of the behaviours we thought about earlier.

else

// if it was a right-click

{

    // then change rep_mode around

    rep_mode = !rep_mode;

}

The other behaviour we wanted to add to our application, was that the user could switch modes by right-clicking. The lines above make that possible.

Now, we are really almost done. There are only two more little things left to do. The first thing is to click on Room editor in AGS, then double click on the room in the list on the right that we created in the beginning, now choose Room from the menu on the top of AGS and click on Room interactions or hit ctrl + i - double-click on Player enters room (after fadein) and select Run script from the upcomming drop-down menu. Next, hit the Edit script-button and add the following line to the editor window:

// script for Room: Player enters screen (after fadein)

RawSaveScreen ( );

This just makes sure that we are able to call the "RawRestoreScreen ( )" function from AGS, which we call in the "repeatedly_execute" function. The application would crash without this line.

The second thing I mentioned was that we need to select the cursor. When we imported sprites, we drew not only stars, but also a crosshair that we wanted to use as mouse cursor. In order to do that, click on Cursors on the right side in AGS, select Cursor 0: Walk to from the list and click on Change image - then just locate and double-click on your imported crosshair image. That should do the trick!

Conclusion
For your reference, and as promised, here is the zipped-version of the finished project for AGS 2.72 or newer, and following is the complete source code so you can see in which order you should place the functions:

#define MATH_PI             3.14159265358979323846 // little mathematical helper

#define PARTICLES_MAX       1000                   // maximum number of particles

#define PARTICLES_NUM_CLICK 50                     // how many particles will be shown when mouse is clicked


bool rep_mode; // keep track of whether we are in rep mode or not





// STRUCTURES

struct particle

{

    float x;          // the position on the x-axis ( in pixel )

    float y;          // the position on the y-axis ( in pixel )

    float velocity_x; // the velocity on the x-axis ( in pixel )

    float velocity_y; // the velocity on the y-axis ( in pixel )

    float speed;      // the speed of the particle ( in pixel )

    float angle;      // the angle of the particle ( in radians )

    int time;         // the number of frames it has been living ( in frames )

    int sprite_slot;  // the sprite slot of the particle sprite

    float gravity;    // the amount of gravity that affects the particle

    int transparency; // the amount of transparency for the particle ( in percent )

};


// define our stars through an array

particle star[PARTICLES_MAX];



// FUNCTIONS

function create_star ( float x, float y )

// creates a particle at a given location

{

    // define a variable for the id-number of the star, that we want to create

    int star_id;


    // use a backward while loop to determine which star should be created

    // we should then come up with the star with the smallest 'star_id' value

    // but which still has its framecounting time variable set to 0


    int i = PARTICLES_MAX - 1;

    while ( i > 0 )

    {

        if ( star[i].time == 0 )

        // if the framecounting time variable equals 0

        {

            // then we will mark this star

            star_id = i;

        }


        // continue the loop

        i--;

    }


    // now that we have the ID value for the star we want to create, we now have to

    // set some initial values / settings for it


    // set its x position to the x value this function gives us by parameter

    star[star_id].x = x;


    // set its y position to the y value this function gives us by parameter

    star[star_id].y = y;


    // set its sprite slot to random ( between 1000 and 1002 )

    star[star_id].sprite_slot = Random ( 2 ) + 1000;


    // set its gravity value to 0

    star[star_id].gravity = 0.0;


    // set its transparency value to something between 50 and 100

    star[star_id].transparency = Random ( 50 ) + 50;


    // and set its framecounting time variable to 1

    star[star_id].time = 1;


    // now choose a speed from 0 to 99

    star[star_id].speed = IntToFloat ( Random ( 100 ) + 50 );


    // and choose an angle from -1 to 1 ( in radians )

    star[star_id].angle = IntToFloat ( Random ( 100 ) * ( FloatToInt ( 2.0 * MATH_PI ) / 2 ) );


    // then set its velocity on the x-axis to its correct value

    star[star_id].velocity_x = Maths.Cos ( star[star_id].angle ) * star[star_id].speed;


    // and then set its velocity on the y-axis to its correct value

    star[star_id].velocity_y = Maths.Sin ( star[star_id].angle ) * star[star_id].speed;

}



function remove_star ( int star_id )

// removes a star

{

    // set its framecounting time variable to 0

    star[star_id].time = 0;

}



function handle_stars ( )

// this function needs to be called every single frame and handles the stars

{

    int i = 0;


    // loop through all stars

    while ( i < PARTICLES_MAX )

    {

        if ( star[i].time > 0 )

        // if the star is activated on the screen somewhere

        {

            // move it

            star[i].x += star[i].velocity_x * IntToFloat ( GetGameSpeed ( ) ) / 1000.0;

            star[i].y += star[i].velocity_y * IntToFloat ( GetGameSpeed ( ) ) / 1000.0 + star[i].gravity;


            // now actually draw the star at its position, using its own sprite slot and its own transparency value

            RawDrawImageTransparent ( FloatToInt ( star[i].x ), FloatToInt ( star[i].y ), star[i].sprite_slot, star[i].transparency );


            // then increase the gravity that affects the particle

            star[i].gravity += IntToFloat ( star[i].time ) / 100.0;


            // and then increase the framecounting time variable

            star[i].time++;


            if ( FloatToInt ( star[i].x ) < 0 || FloatToInt ( star[i].x ) > 320 || FloatToInt ( star[i].y ) > 200 )

            // if the star has left the screen

            {

                // then remove it

                remove_star ( i );

            }

        }


        // continue the loop

        i++;

    }

}



#sectionstart game_start

function game_start ( )

// is called on game start and before the first room loads

{


}

#sectionend game_start




#sectionstart repeatedly_execute

function repeatedly_execute ( )

// is called every frame

{

    // reset the screen every frame

    RawRestoreScreen ( );


    // choose white as color

    RawSetColorRGB ( 255, 255, 255 );


    // draw a message to the screen

    if ( !rep_mode )

    // if we aren't in "repeating mode"

    {

        // display first line of message

        RawPrint ( GetViewportX ( ) + 10, GetViewportY ( ) + 10, "LEFT-CLICK TO EMIT!" );

    }


    // display second line of message

    RawPrint ( GetViewportX ( ) + 10, GetViewportY ( ) + 20, "RIGHT-CLICK TO CHANGE MODE!" );


    if ( rep_mode )

    // if we are in "repeating mode"

    {

        // then spawn one star every frame

        create_star ( IntToFloat ( GetViewportX ( ) + mouse.x ), IntToFloat ( GetViewportY ( ) + mouse.y ) );

    }


    // handle the stars

    handle_stars ( );

}

#sectionend repeatedly_execute




#sectionstart on_key_press

function on_key_press ( int keycode )

{

    if ( keycode == 27 )

    // if "ESC" was pressed

        // quit the game

        QuitGame ( 0 );


    else if ( keycode == 434 )

    // if "F12" was pressed

        // save a screenshot

        SaveScreenShot ( "Screenshot.bmp" );

}

#sectionend on_key_press




#sectionstart on_mouse_click

function on_mouse_click ( MouseButton button )

{

    if ( button == eMouseLeft )

    // if it was a left-click

    {

        if ( !rep_mode )

        // if we are not in "repeating mode"

        {

            int i = 0;


            while ( i < PARTICLES_NUM_CLICK )

            // loop through all stars that we want to create

            {

                // create the star at mouse coordinates

                create_star ( IntToFloat ( GetViewportX ( ) + mouse.x ), IntToFloat ( GetViewportY ( ) + mouse.y ) );


                // continue the loop

                i++;

            }

        }

    }

    else

    // if it was a right-click

    {

        // then change rep_mode around

        rep_mode = !rep_mode;

    }

}

#sectionend on_mouse_click

Just remember that it's not going to work creating a new AGS game, inserting this source code and testing the game. You need to go through all the steps explained above, including importing the images, creating a room and adding that important line to the room script.

That's it. We're done, you can save your project and then run it. If there is any problem, feel free to contact me through our forums. I hope that I didn't make any mistakes, otherwise I might revise the text and change it. I know that this really is a lot of text to work through, and there is vital information here and there, but it really isn't quite as easy to write something on programming. I definately learned a lot by writing this myself, because there are just so many things that you get used to and not even think about, and when you get used to things, you start to forget why you did them in the first place. Although this might not be the uber-helpful learning resource, I hope it inspired you atleast to start becoming more involved in the technical aspects of programming.

Thanks to Misja from the team for the text-formatting!

Jan Simon - Juli 2006

Jan is the lead programmer for the project, and has already rebuilt the superb playable demo.