Galaga

Introduction

This page shows the code and pin connections for a retro style Galaga game. The game consists of a space ship with horizontal capability of moving left and right and the ability to shoot missiles at enemy intruders. The intruders start at the top of the screen and make their way towards the space craft. The goal is to shoot your missiles and destroy your enemies before your enemies get to you.

Devices used:

  • SD Card
  • Class D Amplifier
  • uLCD
  • Haptic Feedback Motor
  • 2N3904 NPN BJT (including 1Kohm resistor)
  • Potentiometer

Pin connections made

mbedDevice
p18Class D + In
p27uLCD RX
p29uLCD Reset
p28uLCD TX
p6SD DO
p7SD SCK
p8SD CS
p6SD DO
p5SD DI
p16Nav U
p15Nav C
p14Nav L
p13Nav D
p12Nav R
p21Transistor Base in series with 1Kohm
Class D AmpDevice
Out +Speaker +
Out-Speaker -
Pwr -Gnd
Pwr+VCC
Vol(3)Potentiometer
Haptic FeedbackConnection
Red wireVCC
Blue wireBJT Collector
GndBJT Emitter

Import programGalaga

Mbed Galaga Game

Program Code

Mbed Galaga

#include "mbed.h"
#include "uLCD_4DGL.h"
#include "wave_player.h"
#include "SDFileSystem.h"
//#include "enemies.h"
#include <vector>
#include "rtos.h"
uLCD_4DGL uLCD(p28,p27,p29);
AnalogOut DACout(p18);
DigitalOut vib(p21);
wave_player waver(&DACout);
SDFileSystem sd(p5, p6, p7, p8, "sd"); // mosi, miso, sck, cs
BusOut mbedleds(LED1,LED2,LED3,LED4);
//BusOut/In is faster than multiple DigitalOut/Ins
int playLaser = 0;int playEnemyExplode = 0;int playerExplode = 0;//variables to trigger the speaker to play sounds
void sdcard_thread(void const *argument)//thread to play the sounds
{
    while(1) {

        if(playLaser){//play sound if player fires
            FILE *wave_file;
            wave_file=fopen("/sd/laser.wav","r");
            waver.play(wave_file);
            fclose(wave_file);
            playLaser = 0;
        }
        if(playerExplode) { //play sound if player dies
            FILE *wave_file;
            wave_file=fopen("/sd/Bomb.wav","r");
            waver.play(wave_file);
            fclose(wave_file);
            playerExplode = 0;
        }

        if(playEnemyExplode){//play sound if enemies get hit
            FILE *wave_file;
            wave_file=fopen("/sd/Bombnew.wav","r");
            waver.play(wave_file);
            fclose(wave_file);
            playEnemyExplode = 0;
        }    
        Thread::wait(50);
    }
}

class Nav_Switch
{

public:

    Nav_Switch(PinName up,PinName down,PinName left,PinName right,PinName fire);
    int read();
//boolean functions to test each switch
    bool up();
    bool down();
    bool left();
    bool right();
    bool fire();
//automatic read on RHS
    operator int ();
//index to any switch array style

    bool operator[](int index) {

        return _pins[index];

    };

private:

    BusIn _pins;



};

Nav_Switch::Nav_Switch (PinName up,PinName down,PinName left,PinName right,PinName fire):

    _pins(up, down, left, right, fire)

{

    _pins.mode(PullUp); //needed if pullups not on board or a bare nav switch is used - delete otherwise

    wait(0.001); //delays just a bit for pullups to pull inputs high

}

inline bool Nav_Switch::up()

{

    return !(_pins[0]);

}

inline bool Nav_Switch::down()

{

    return !(_pins[1]);

}

inline bool Nav_Switch::left()

{

    return !(_pins[2]);

}

inline bool Nav_Switch::right()

{

    return !(_pins[3]);

}

inline bool Nav_Switch::fire()

{

    return !(_pins[4]);

}

inline int Nav_Switch::read()

{

    return _pins.read();

}

inline Nav_Switch::operator int ()

{

    return _pins.read();

}

// Declar player dimensions

int playerWidth = 7;

int playerHeight = 3;

int playerDelta = 3;

int playerXpos = 64;

int playerYpos = 113;

#define maxNumPlayerMissiles  5

#define numEnemies  10

//struct for missiles

typedef struct {

    int x;                   ///< The x-coordinate of missile current position

    int y;                   ///< The y-coordinate of missile current position

    int status;   ///< The missile status, see MISSILE_STATUS

} playerMissile; // infomration about missile position and status

//struct for enemies

typedef struct {

    int x;

    int y;

    int size;

    int status;

} enemy;

enemy enemies[numEnemies];//create a struct array of enemies

// initialize the player's position, missile status, draw player, 

void player_draw(int x, int y, int playerColor) {

    uLCD.filled_rectangle(x, y, x+playerWidth, y+playerHeight, playerColor); 

    uLCD.filled_rectangle(x+playerDelta, y-playerDelta, x+playerWidth-playerDelta, y+playerHeight, playerColor);  

}

playerMissile playerMissiles[maxNumPlayerMissiles]; //struct array of player missiles

//function to fire a missile

void player_fire(void){

    playerMissile PM;

    PM.x = playerXpos + (playerWidth/2);

    PM.y = playerYpos-playerDelta;

    PM.status = 1;

    for(int i=0;i<maxNumPlayerMissiles;i++){

        if(playerMissiles[i].status==0){

            playerMissiles[i]=PM;

            break;

        }

    }

}

//function to update all parts of the game

void updateGame(){

    for(int i=0;i<maxNumPlayerMissiles;i++){

        // update all missiles

        if(playerMissiles[i].status == 1){

            uLCD.circle(playerMissiles[i].x,playerMissiles[i].y,1,0x0);

            playerMissiles[i].y-=5;

            uLCD.circle(playerMissiles[i].x,playerMissiles[i].y,1,0xFF0000);

        }

        if(playerMissiles[i].y==0 || playerMissiles[i].status==0){

            uLCD.circle(playerMissiles[i].x,playerMissiles[i].y,1,0x0);

           playerMissiles[i].status = 0;

        }

        //update all enemies

        for(int j=0;j<numEnemies;j++){

            if(playerMissiles[i].status==1 && enemies[j].status==1 && playerMissiles[i].x>enemies[j].x && playerMissiles[i].x<enemies[j].x+enemies[j].size && playerMissiles[i].y>enemies[j].y && playerMissiles[i].y<enemies[j].y+enemies[j].size){

                enemies[j].status = 0;

                playerMissiles[i].status = 0;

                playEnemyExplode=1;

                uLCD.filled_rectangle(enemies[j].x,enemies[j].y,enemies[j].x+enemies[j].size,enemies[j].y+enemies[j].size,0x0);

            }

        }

    }   

}

//function to set initial position of enemies

void enemies_init(void){

    enemies[0].x = 1;enemies[0].y = 1;enemies[0].size = 8;

    enemies[1].x = 26;enemies[1].y = 1;enemies[1].size = 8;

    enemies[2].x = 51;enemies[2].y = 1;enemies[2].size = 8;

    enemies[3].x = 76;enemies[3].y = 1;enemies[3].size = 8;

    enemies[4].x = 101;enemies[4].y = 1;enemies[4].size = 8;

    enemies[5].x = 1;enemies[5].y = 20;enemies[5].size = 8;

    enemies[6].x = 26;enemies[6].y = 20;enemies[6].size = 8;

    enemies[7].x = 51;enemies[7].y = 20;enemies[7].size = 8;

    enemies[8].x = 76;enemies[8].y = 20;enemies[8].size = 8;

    enemies[9].x = 101;enemies[9].y = 20;enemies[9].size = 8;

}

Nav_Switch myNav( p15, p12, p13, p11, p14); //pin order on Sparkfun breakout



int main()

{

    Thread thread1(sdcard_thread);

    int ctr = 0;int distance=5;int vertical = 1;int win = 1;int numDeadEnemies=0;

    //initialize enemy statuses

    enemies[0].status = 1;enemies[1].status = 1;enemies[2].status = 1;enemies[3].status = 1;enemies[4].status = 1;

    enemies[5].status = 1;enemies[6].status = 1;enemies[7].status = 1;enemies[8].status = 1;enemies[9].status = 1;

    enemies_init();

    //draw enemies

    for(int i=0;i<numEnemies;i++){

        uLCD.filled_rectangle(enemies[i].x,enemies[i].y,enemies[i].x+enemies[i].size,enemies[i].y+enemies[i].size,0x00FF00);

    }

    int i;

    while(1) {

        //with pullups a button hit is a "0" - "~" inverts data to leds

        if(ctr%10==0){

            for(i=0;i<numEnemies;i++){

                if(enemies[i].status!=0){

                    //update enemy positions

                    uLCD.filled_rectangle(enemies[i].x,enemies[i].y,enemies[i].x+enemies[i].size,enemies[i].y+enemies[i].size,0x0);

                    if(vertical<0){enemies[i].x+=distance;}else{enemies[i].y+=5;}

                    uLCD.filled_rectangle(enemies[i].x,enemies[i].y,enemies[i].x+enemies[i].size,enemies[i].y+enemies[i].size,0x00FF00);

                }

            }

            if(vertical<0){distance*=-1;}vertical*=-1;

        }

        mbedleds = ~(myNav & 0x0F); //update leds with nav switch direction inputs

       if(myNav.fire()){//fire missile

            vib = 1;

            Thread::wait(100);

            vib=0;

            playLaser = 1;

            player_fire();

            mbedleds = 0x0F; //special all leds on case for fire (center button)

        }

       //or use - if(myNav[4]==0) mbedleds = 0x0F; //can index a switch bit like this

        if(myNav.right() && playerXpos <= 110){//player moves right

            player_draw(playerXpos,playerYpos,0x0);

            playerXpos+=5;

        }

        if(myNav.left() && playerXpos >= 0){//player moves left

            player_draw(playerXpos,playerYpos,0x0);

            playerXpos-=5;

        }

        player_draw(playerXpos,playerYpos,0xFFFF00);//draw the player

        updateGame();

        //check if player has been hit

        for(i=0;i<numEnemies;i++){

            if(enemies[i].status==1 && playerXpos>enemies[i].x && playerXpos<enemies[i].x+enemies[i].size && playerYpos>enemies[i].y && playerYpos<enemies[i].y+enemies[i].size){   

                  player_draw(playerXpos,playerYpos,0x0);win=0;playerExplode=1;vib=1;

            }

            if(enemies[i].status==0)numDeadEnemies++;

            if(enemies[i].y>128)enemies[i].y=1;

        } 

        // check for end game conditions

        if(numDeadEnemies==numEnemies){Thread::wait(1000);break;}

        if(!win){Thread::wait(1000);vib=0;playerExplode=0;break;}

        numDeadEnemies = 0;

        ctr++;

        Thread::wait(10);

    }

    uLCD.locate(5,5);

    if(win){

        uLCD.printf("You Win!!");

    }else{

        uLCD.printf("GAME OVER");

    }

}

Breadboard Overview

/media/uploads/amostafa9/breadboard.png

YouTube Link


Please log in to post comments.