This post is a portion of Part 5 in my on going series about 2D Game Development using the Allegro 5 library. These posts are in course order. You can see all of the posts in this course by clicking the “2D Game Development” button at the top of this site.
- 5.0 – Our First Game
- 5.1 – Our First Game: The Player
- 5.2 – Our First Game: Timing and Movement
- 5.3 – Our First Game: The Projectiles
- 5.4 – Our First Game: The Enemies
- 5.5 – Our First Game: Collision Detection
- 5.6 – Our First Game: Game Logic
- 5.7 – Our First Game: Conclusion
We look at the last object in our game, the comet, and get it integrated. After this it is just collision and game logic remaining.
Full source can be found here.
Notice that the source code is slightly different from the video? Here is why.
Is there a part in your tutorials where you’ll teach us about using gamepads? I’m trying to use the xbox 360 controller right now, and the documentation is so awful it’s just a guessing game for me.
I could do that in a future video. I have a few other things to work on first though.
Awesome tutorials!
Just one question, is it really good to use global constants for stuff like width, or number of comets? Wouldn’t #define macros be better?
Oh yes. Using globals is generally never a great idea, If they are constant it usually isn’t a problem though. Mostly, I was going for the quickest and easiest implementation for the people learning. I didn’t want to bother getting into macros.
I disagree, even though globals are generally not a good idea, they are at least part of the language itself and not preprocessor macro bullshit.
Const stuff is the way to go, macros are just as global and have many inherent problems, just use them when you really need token expansion. (That’s like almost never)
Hi, I’m not native english so I have some difficulties understanding when you explain these 3 lines:
if(rand() % 500 == 0)
comets[i].y = 30 + rand() % (HEIGHT – 60);
srand(time(NULL));
Can you teach me here? Thanks.
Why there is an error “Run-Time Check Failure #2 – Stack around the variable ‘keys’ was corrupted.”. When i pressed the escape key during the game.
For some reason, when the comets hit the left side of the screen, they just stay there (half visible since the circle is drawn from the center), until another comet takes its place in the queue (after 10 comets are on the screen).
I can’t find where I went wrong with the code. I also tried a work-around by trying to draw a black circle in place of the the stuck comets, but it doesn’t seem to do anything.
So for now I made them go fully out of the screen (-20 instead of 0).
Fixed it. Ignore this ^
Thanks.
Glad you got it.
I’m actually having this exact problem, and I’m not able to fix it. hmmm….Any suggestions?
Here is my code, for some reason, the comets pause a bit before they regenerate. If someone can help me, i would really appreciate it. Thanks.
#include
#include
#include “objects.h”
//GLOBALS***********************************************************
const int WIDTH = 900;
const int HEIGHT = 600;
const int NUM_BULLETS = 5;
const int NUM_COMETS = 15;
enum KEYS{UP, DOWN, LEFT, RIGHT, SPACE};
bool keys[5] = {false, false, false, false, false};
//Prototypes – Space Ship
void InitShip(SpaceShip &ship);
void DrawShip(SpaceShip &ship);
void MoveShipUp(SpaceShip &ship);
void MoveShipDown(SpaceShip &ship);
void MoveShipLeft(SpaceShip &ship);
void MoveShipRight(SpaceShip &ship);
// Bullets
void InitBullet(Bullet bullet[], int size);
void DrawBullet(Bullet bullet[], int size);
void FireBullet(Bullet bullet[], int size, SpaceShip &ship);
void UpdateBullet(Bullet bullet[], int size);
void CollideBullet(Bullet bullet[], int bsize, Comet comets[], int csize);
// Comets
void InitComet(Comet comets[], int size);
void DrawComet(Comet comets[], int size);
void StartComet(Comet comets[], int size);
void UpdateComet(Comet comets[], int size);
void CollideComet(Comet comets[], int csize, SpaceShip &ship);
int main()
{
//primitive variable
bool done = false;
bool redraw = true;
const int FPS = 60;
//object variables
SpaceShip ship;
Bullet bullets[NUM_BULLETS];
Comet comets[NUM_COMETS];
//*******************************************************alegro variables
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
ALLEGRO_TIMER *timer = NULL;
//*******************************************************initialize
if(!al_init())
return -1;
display = al_create_display(WIDTH, HEIGHT);
if(!display)
return -1;
al_init_primitives_addon();
al_install_keyboard();
event_queue = al_create_event_queue();
timer = al_create_timer(1.0/FPS);
srand(time(NULL));
InitShip(ship);
InitBullet(bullets, NUM_BULLETS);
InitComet(comets, NUM_COMETS);
al_register_event_source(event_queue, al_get_keyboard_event_source());
al_register_event_source(event_queue, al_get_timer_event_source(timer));
al_register_event_source(event_queue, al_get_display_event_source(display));
//*******************************************************game starts
al_start_timer(timer);
while(!done)
{
ALLEGRO_EVENT ev;
al_wait_for_event(event_queue, &ev);
if(ev.type == ALLEGRO_EVENT_TIMER)
{
redraw = true;
if(keys[UP])
MoveShipUp(ship);
if(keys[DOWN])
MoveShipDown(ship);
if(keys[LEFT])
MoveShipLeft(ship);
if(keys[RIGHT])
MoveShipRight(ship);
UpdateBullet(bullets, NUM_BULLETS);
StartComet(comets, NUM_COMETS);
UpdateComet(comets, NUM_COMETS);
CollideBullet(bullets, NUM_BULLETS, comets, NUM_COMETS);
CollideComet(comets, NUM_COMETS, ship);
}
else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
{
done = true;
}
else if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
{
switch(ev.keyboard.keycode)
{
case ALLEGRO_KEY_ESCAPE:
done = true;
break;
case ALLEGRO_KEY_UP:
keys[UP] = true;
break;
case ALLEGRO_KEY_DOWN:
keys[DOWN] = true;
break;
case ALLEGRO_KEY_LEFT:
keys[LEFT] = true;
break;
case ALLEGRO_KEY_RIGHT:
keys[RIGHT] = true;
break;
case ALLEGRO_KEY_SPACE:
keys[SPACE] = true;
FireBullet(bullets, NUM_BULLETS, ship);
break;
}
}
else if(ev.type == ALLEGRO_EVENT_KEY_UP)
{
switch(ev.keyboard.keycode)
{
case ALLEGRO_KEY_ESCAPE:
done = false;
break;
case ALLEGRO_KEY_UP:
keys[UP] =false;
break;
case ALLEGRO_KEY_DOWN:
keys[DOWN] = false;
break;
case ALLEGRO_KEY_LEFT:
keys[LEFT] = false;
break;
case ALLEGRO_KEY_RIGHT:
keys[RIGHT] = false;
break;
case ALLEGRO_KEY_SPACE:
keys[SPACE] = false;
break;
}
}
if(redraw && al_is_event_queue_empty(event_queue))
{
redraw = false;
DrawShip(ship);
DrawBullet(bullets, NUM_BULLETS);
DrawComet(comets, NUM_COMETS);
al_flip_display();
al_clear_to_color(al_map_rgb(0,0,0));
}
}
al_destroy_display(display);
return 0;
}
void InitShip (SpaceShip &ship)
{
ship.x = 20;
ship.y = HEIGHT /2;
ship.ID = PLAYER;
ship.lives = 3;
ship.speed = 7;
ship.boundx = 6;
ship.boundy = 7;
ship.score = 0;
}
void DrawShip(SpaceShip &ship)
{
al_draw_filled_rectangle(ship.x, ship.y – 9, ship.x +10, ship.y – 7, al_map_rgb(255, 0, 0));
al_draw_filled_rectangle(ship.x, ship.y + 9, ship.x +10, ship.y + 7, al_map_rgb(255, 0, 0));
al_draw_filled_triangle(ship.x -12, ship.y -17, ship.x +12, ship.y, ship.x -12, ship.y +17, al_map_rgb(0, 255, 0));
al_draw_filled_rectangle(ship.x -12, ship.y -2, ship.x +15, ship.y +2, al_map_rgb(0,0,255));
}
//***********************************************************ship functions
void MoveShipUp(SpaceShip &ship)
{
ship.y -= ship.speed;
if(ship.y HEIGHT)
ship.y = HEIGHT;
}
void MoveShipLeft(SpaceShip &ship)
{
ship.x -= ship.speed;
if(ship.x WIDTH)
ship.x = WIDTH;
}
//*********************************************************bullet functions
void InitBullet(Bullet bullet[], int size)
{
for(int i = 0; i < size; i++)
{
bullet[i].ID = BULLET;
bullet[i].speed = 10;
bullet[i].live = false;
}
}
void DrawBullet(Bullet bullet[], int size)
{
for(int i = 0; i < size; i++)
{
if(bullet[i].live)
al_draw_filled_circle(bullet[i].x, bullet[i].y, 2, al_map_rgb(255,255,255));
}
}
void FireBullet(Bullet bullet[], int size, SpaceShip &ship)
{
for(int i = 0; i <size; i++)
{
if(!bullet[i].live)
{
bullet[i].x = ship.x + 17;
bullet[i].y = ship.y;
bullet[i].live = true;
break;
}
}
}
void UpdateBullet(Bullet bullet[], int size)
{
for(int i = 0; i WIDTH)
bullet[i].live = false;
}
}
}
void CollideBullet(Bullet bullet[], int bsize, Comet comets[], int csize)
{
for(int i = 0; i <bsize; i++)
{
if(bullet[i].live)
{
for(int j = 0; j (comets[j].x – comets[j].boundx) &&
bullet[i].x (comets[j].y – comets[j].boundy) &&
bullet[i].y < (comets[j].y + comets[j].boundy))
{
bullet[i].live = false;
comets[j].live = false;
}
}
}
}
}
//************************************************************comet functions
void InitComet(Comet comets[], int size)
{
for(int i = 0; i < size; i++)
{
comets[i].ID = ENEMY;
comets[i].live = false;
comets[i].speed = 5;
comets[i].boundx = 18;
comets[i].boundy = 18;
}
}
void DrawComet(Comet comets[], int size)
{
for(int i = 0; i < size; i++)
{
al_draw_filled_circle(comets[i].x, comets[i].y, 35, al_map_rgb(40,30, 43));
}
}
void StartComet(Comet comets[], int size)
{
for(int i = 0; i < size; i++)
{
if(!comets[i].live)
{
if(rand() % 500 == 0)
{
comets[i].live = true;
comets[i].x = WIDTH;
comets[i].y = 30 + rand()%(HEIGHT – 60);
break;
}
}
}
}
void UpdateComet(Comet comets[], int size)
{
for(int i = 0; i < size; i++)
{
if(comets[i].live)
{
comets[i].x -= comets[i].speed;
}
}
}
void CollideComet(Comet comets[], int csize, SpaceShip &ship)
{
for(int i = 0; i < csize; i++)
{
if(comets[i].live)
{
if(comets[i].x – comets[i].boundx ship.x – ship.boundx &&
comets[i].y – comets[i].boundy ship.y – ship.boundy)
{
ship.lives–;
comets[i].live = false;
}
else if(comets[i].x < 0)
{
comets[i].live = false;
ship.lives–;
}
}
}
}
i’m trying to make the comets move down the screen instead of across but its not working any ideas? on what code would work?
I would need more information. Feel free to email me your code.
Hello,
First of all, great tutorials!
Second I think I found something messy. You wrote in your code:
if (!comet[i].live)
{
if (rand() % 500 == 0)
{
comet[i].live = true;
comet[i].x = WIDTH;
comet[i].y = 30 + rand() % (HEIGHT - 60);
break;
}
}
And you said you are creating a comet once every 500. But in the if statement, if you do not find the number you want, it will go to the next live comet. This way, if there is no comet alive, you will create a comet ten times every 500. If you want to create a comet every 500 times, you should put the break outside the if rand statement:
if (!comet[i].live)
{
if (rand() % 500 == 0)
{
comet[i].live = true;
comet[i].x = WIDTH;
comet[i].y = 30 + rand() % (HEIGHT - 60);
//break; // Not here
}
break; // <-- Here
}
When I tried to do so, the game was so boring that I put the break where you wrote it and left the 500.
Also, please be careful with the naming conventions. When you created the bullets, you named the array bullets and in the procedures, they were called bullet. I like it that way, it’s easier to know when you are on the main function (bullets) and when you are in a procedure (bullet).
But when you created the comets, you gave them the same name, comets. I prefer to give them different names, what I would have done would be to call them bullets and b, but bullets and bullet is great too.
I’ll continue learning. Great work!
Regards,
David
Sorry, what I wanted to comment with the second point is that, whichever naming convention you pick, please be constant. If you want to call the variables with the same name always is allright, but do it that way always.
When I run these programs I get a noticeable lag consistently every 10 seconds. The game doesn’t lag itself, but the image gets very choppy for about 1-2 seconds. This has happened with every program I have made while following your tutorial, even just making a square move around the window.
Why is this happening? I don’t have any problem running computer games on this computer, and I wouldn’t think that such simple programs would create such a noticeable problem. After running the program for just a few minutes, the choppiness becomes so annoying that I don’t want to keep looking at it and have to exit. In effect, ruining the game. This happens whether or not I am moving the ship, and regardless of how many comets or bullets are on the screen.
Any advice? Any suggestions? How can I make it stop?
Hi, I see these lines of codes in different functions, but I don’t understand them:
-bullet[i].ID = BULLET;
-comets[i].ID = ENEMY;
What is the origin of ‘BULLET’ and ‘ENEMY’? Sorry, but I don’t have a great C++ knowledge.