2D Game Dev – Part 5.3: Our First Game – The Projectiles

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.

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.

Posted in 2D Game Dev, Allegro, C++, Code, Game Dev, Part 5, Tutorial
18 Comments » for 2D Game Dev – Part 5.3: Our First Game – The Projectiles
  1. Ryan says:

    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?

  2. Ryan says:

    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.

    • Mike Geig says:

      Both work for me. Can we see your code?

    • Mike Geig says:

      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.

      • nocrej says:

        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.

  3. Ryan says:

    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;
    }
    }
    }

  4. Ryan says:

    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;
    }
    }
    }

  5. Xilef says:

    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?

    • Mike says:

      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.

  6. nocrej says:

    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?

  7. MarioMX says:

    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;
    }

    • MarioMX says:

      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

      • MarioMX says:

        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.

  8. mar says:

    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;
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>