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
In this video we begin looking at our projectile object. Different from the player object, the projectile needs to handle being updated automatically.
Full source can be found here.
Notice that the source code is slightly different from the video? Here is why.

Great tutorial, I just have a quick question. I noticed that when I run my game, if I am moving the ship diagonally, it won’t fire bullets. Is this just because of the simplified game loop, or did I make a mistake?
Ok, edit, to my question. It doesn’t fire when moving Up/left or down/right diagonally. It works with up/right and down/left.
Both work for me. Can we see your code?
When you were unable to shoot while moving diagonally, had you already fired 5 shots? Remember, only 5 shots can exist at a time. When I tried this hitting the space bar quickly, I was unable to shoot at a certain point because there were already 5 live bullets.
Hi Mike,
I’m also having the same problem when I try to shoot some bullets while moving up/left diagonally. That’s the only problem I noticed.
It doesn’t seem to be because there are too many bullets on the screen. Here’s my code:
#include
#include
#include “gameIDS.h”
//global
const int WIDTH = 800;
const int HEIGHT = 400;
enum KEYS {UP, DOWN, LEFT, RIGHT, SPACE};
bool keys[5] = {false, false, false, false, false};
const int NUM_BULLETS = 5;
const int NUM_COMETS = 10;
//prototypes
void init_ship(spaceship &ship);
void draw_ship(spaceship &ship);
void move_left(spaceship &ship);
void move_right(spaceship &ship);
void move_up(spaceship &ship);
void move_down(spaceship &ship);
void init_bullet(Bullet bullet[], int size);
void draw_bullet(Bullet bullet[], int size);
void fire_bullet(Bullet bullet[], int size, spaceship &ship);
void update_bullet(Bullet bullet[], int size);
int main(void) {
// primitive variables
bool done = false;
bool redraw = true;
const int FPS = 45;
//object variables
spaceship ship;
Bullet bullets[5];
//allegro variables
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *queue = NULL;
ALLEGRO_TIMER *timer = NULL;
//init functions
if(!al_init()) {
return -1;}
display = al_create_display(WIDTH, HEIGHT);
if(!display) {
return -1;}
queue = al_create_event_queue();
if(!queue) {
return -1;}
timer = al_create_timer(1.0 / FPS);
al_init_primitives_addon();
al_install_keyboard();
init_ship(ship);
init_bullet(bullets, NUM_BULLETS);
al_register_event_source(queue, al_get_keyboard_event_source());
al_register_event_source(queue, al_get_timer_event_source(timer));
al_register_event_source(queue, al_get_display_event_source(display));
al_start_timer(timer);
while(!done) {
ALLEGRO_EVENT ev;
al_wait_for_event(queue, &ev);
if (ev.type == ALLEGRO_EVENT_TIMER) {
redraw = true;
if(keys[UP]) {
move_up(ship);}
if(keys[DOWN]) {
move_down(ship);}
if(keys[LEFT]) {
move_left(ship);}
if(keys[RIGHT]) {
move_right(ship);}
update_bullet(bullets, NUM_BULLETS);
}
else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
done = true;
break;
}
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;
fire_bullet(bullets, NUM_BULLETS, ship);
break;
}
}
else if(ev.type == ALLEGRO_EVENT_KEY_UP)
{
switch(ev.keyboard.keycode) {
case ALLEGRO_KEY_ESCAPE:
done = true;
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(queue)) {
redraw = false;
draw_ship(ship);
draw_bullet(bullets, NUM_BULLETS);
al_flip_display();
al_clear_to_color(al_map_rgb(0,0,0));
}
}
al_destroy_timer(timer);
al_destroy_display(display);
al_destroy_event_queue(queue);
return 0;
}
void init_ship(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 draw_ship(spaceship &ship) {
al_draw_filled_rectangle(ship.x, ship.y – 9, ship.x + 10, ship.y – 7, al_map_rgb(255,255,255));
al_draw_filled_rectangle(ship.x, ship.y + 9, ship.x + 10, ship.y + 7, al_map_rgb(255,255,255));
al_draw_filled_triangle(ship.x – 12, ship.y – 17, ship.x + 12, ship.y, ship.x – 12, ship.y + 17, al_map_rgb(255,255,255));
al_draw_filled_rectangle(ship.x – 12, ship.y – 2, ship.x + 15, ship.y + 2, al_map_rgb(255,0,0));
}
void move_left(spaceship &ship){
ship.x -= ship.speed;
if(ship.x 300) {
ship.x = 300;}
}
void move_up(spaceship &ship){
ship.y -= ship.speed;
if (ship.y HEIGHT) {
ship.y = HEIGHT;}
}
void init_bullet(Bullet bullet[], int size) {
for(int i = 0; i < size; i++) {
bullet[i].ID = BULLET;
bullet[i].speed = 12;
bullet[i].live = false;
}
}
void draw_bullet(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, 4, al_map_rgb(255,0,0)); }
}
}
void fire_bullet(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 update_bullet(Bullet bullet[], int size){
for(int i = 0; i WIDTH)
bullet[i].live = false;
}
}
}
by the way, the “update bullet” function actually looks like this. the comment messed it up.
void update_bullet(Bullet bullet[], int size){
for(int i = 0; i WIDTH)
bullet[i].live = false;
}
}
}
What OS are you using?
Copy and paste butchered your code. Could you email it to me (check contacts page) and I can get a better look at it?
I was wondering: Why doesn’t holding the space bar down result in multiple shots being fired. From my understanding of the code it should.
Doesn’t the event ALLEGRO_EVENT_KEY_DOWN fire every tick as long as you press the space bar, and shouldn’t that result in several shots?
If not; what kind of modification would make it possible to fire rapidly only by holding the space bar?
The event only fires off once, when the key is pressed. If you would like continuous state, just store it in an array like I do with the rest of the keys.
I tried typing the code in again from scratch, but I’m still experiencing the same problem when I try to shoot bullets while moving up/left(diagonal). Is there any particular reason that would happen Mike? Is there any particular solution?
Someone had that issue before and we fixed it, but I don’t remember how.
That’s alright. Thanks anyway =)
I modified my code to use mouse input instead.
void init_bullet(Bullet bullet[], int size) {
for(int i = 0; i < size; i++) {
bullet[i].ID = BULLET;//I really dont know where this BULLET variable is declared?
bullet[i].speed = 12;
bullet[i].live = false;
}
Oh, i see, BULLET=1 from the .h file
Jeje I´m having some problems because
I´m coding all with variables in spanish
and a change in the function names :p
México
Sorry for the 3 post, I cant edit
I have the problem of shoot while moving diagonally too,
only works up/right :O i think its keyboard problem,
like sometimes you cant press more than 3 keys.
hi mike, that is the way how to let the enemies shot directly to your ship, modified bresenhalm algorithm, extremely fast bullet trajectory computing. i can share with you my entire solution, maybe i miss something, it’s old code, send me mail. so you can use it in your tutorials. maybe allegro have better routines, i don’t know. anyway keep up good work.
typedef struct SDeltaGen
{
public:
BOOL fSwap;
int xf;
int yf;
int dx;
int dy;
int d;
int incx;
int incy;
int inc1;
int inc2;
} *LPDELTAGEN;
void DeltaGen( LPDELTAGEN pdg, int* px, int* py )
{
static int* pnT;
if( pdg->fSwap )
{
pnT = px;
px = py;
py = pnT;
}
*px += pdg->incx;
if ( pdg->d d += pdg->inc1;
else
{
*py += pdg->incy;
pdg->d += pdg->inc2;
}
}
BOOL CSprite::DeltaGenInit( LPDELTAGEN pdg, int x0, int y0, int xf, int yf, int* px, int* py )
{
int nT;
pdg->xf = xf;
pdg->yf = yf;
if( x0 == xf && y0 == yf )
return FALSE;
if( xf >= x0 )
pdg->incx = 1;
else
pdg->incx = -1;
if(yf >= y0)
pdg->incy = 1;
else
pdg->incy = -1;
pdg->dx = (xf – x0) * pdg->incx;
pdg->dy = (yf – y0) * pdg->incy;
if( pdg->dy > pdg->dx )
{
nT = pdg->dy;
pdg->dy = pdg->dx;
pdg->dx = nT;
nT = pdg->incx;
pdg->incx = pdg->incy;
pdg->incy = nT;
pdg->fSwap = TRUE;
}
else
pdg->fSwap = FALSE;
pdg->inc1 = pdg->dy <inc2 = (pdg->dy – pdg->dx) <d = pdg->inc1 – pdg->dx;
pdg->xf = xf;
pdg->yf = yf;
*px = x0;
*py = y0;
return TRUE;
}