Posts
Search
Contact
Cookies
About
RSS

Creating a simple game (part 3)

Added 30 Jun 2019, 11:51 a.m. edited 18 Jun 2023, 7:16 p.m.

The series starts here

Last time we saw how a player could be handled by using a very simple state machine, this time we pick up the pace and make things more interesting (and complex!) by implementing some aliens... Lets start by looking at the different states an alien can be in.

am_Enter = 0, am_Form, am_Dive, am_Explode, am_Dead, am_SgoL, am_SgoR

I decided right off the bat, to make things simple (but a lot less flexible) and stick with a fixed number of aliens, held in a simple array. The first mode, am_Enter is used then the alien is entering into the formation, its worth looking at how we handle this, as horror of horrors there's some maths involved, but raylib makes even that easy!

        // rotation from alien to player
        aliens[i].rot = Vector2Angle(aliens[i].pos, player.pos) - 90.0;
        Vector2 ftarg; // formation target
        ftarg.y = formationPos[i].y;
        // formation is going in two different directions at the same time...
        if (i>7) {
            ftarg.x = formOffset-formationPos[i].x;
        } else {
            ftarg.x = (((gameScreenWidth/ 4.0)*8)-formOffset)-formationPos[i].x;
        }

raylib has a handy header raymath.h which gives us some easy to use and handy functions. First we rotate the alien to face the player, which is as simple as using Vector2Angle. As our formation is moving in two different directions with the top and bottom ranks moving opposite to each other the position we're targeting is moving, once we have our target calculated, we can head off in that direction

        // move to the formation point or if there change mode
        Vector2 dir = Vector2Subtract(ftarg, aliens[i].pos);
        float l = Vector2Length(dir);
        if (l<8) {
            aliens[i].mode = am_Form; 
        } else { 
            dir = Vector2Normalize(dir); 
            dir = Vector2Scale(dir, 6); 
            aliens[i].pos = Vector2Add(aliens[i].pos, dir); 
        }

Subtracting two positions (the alien and target) we can determine how far away the alien is, if its close enough we just set the aliens mode to am_Form the alien is then in its formation position, otherwise we take the direction vector and normalize it (make its length 1.0 while retaining its direction) after scaling this vector we know how far to move, to get us a step closer to the aliens formation position.

While the rest of the mode handling code is more complex than the player code, by tackling each mode one at a time, you only need to take into account a small chunk of code at a time. Just as with the player many of the modes rely on each other and in combination with a simple counter, make for some interesting behavior, with the capability to add new behaviors made straight forward.

You'll notice however that there is a rigid maximum number of aliens with one reserved for the saucer that runs along the top of the screen. Worse still the array is traversed not with a define or constant for its maximum range, but a magic value littered all through the code, which is far from ideal practice! We'll introduce a much more flexible way of handling collections of objects later when we implement shooting...

Every time we loop through the alien array we check to see if the aliens mode is am_Dead if all our aliens are dead, this leaves a flag (allDead) true, meaning we need to start a new wave of aliens, this is just the point where we could set off all sorts of different types of alien waves. However for the sake of simplicity we just have one type of attack wave.

As the player has no way to destroy aliens yet, there is some test code to destroy a random alien in the formation, when the space key is pressed. This allows you to see a new formation enter the game once all the aliens have been destroyed.

I've not detailed every mode so as to give you the opportunity to dig through the code in aliens.c and discover for yourself how it works, while at first look it seems complex, don't forget just take one mode at a time and you greatly reduce the amount of code you're looking at.

Once you think you understand whats going on why not have a go at implementing am_Dive here's what you need to do:

There's a lot look at so go ahead and grab the code here

Once you're happy you know whats going on, the rest of the tutorial continues here.