Bomberman Game

This project creates a basic Bomberman game. Bomberman involves a grid of blocks and multiple players who can navigate through the map and place bombs to destroy the blocks. The blocks will drop upgrades. Players will die if they get to close to the explosives and the goal of the game is to be the last player standing.

/media/uploads/apcastelein/20161028_030201.jpg

Here I've created a 2 player version on a 7 by 7 grid. Player 1 is controlled by a joystick and player 2 is controlled by a capacitive touch sensor. The game is displayed on an lcd and it makes sounds for explosions using a speaker along with a class D amp. The game loads the sound and the graphic from a micro sd card.

Import programbomberman

A implementation of a simple bomberman game

bomberman main.cpp

#include "mbed.h"
#include "SDFileSystem.h"
#include "wave_player.h"
#include "myBMP.h"
#include <string>
#include "navSwitch.h"
#include <mpr121.h>
#include "rtos.h"

float gameTime = 0.0;

string startText = "Loading";

bool gameRestart = false;

SDFileSystem sd(p11, p12, p13, p16, "sd"); //SD card

AnalogOut DACout(p18);

wave_player waver(&DACout);

uLCD_4DGL uLCD(p28,p27,p30);

int BACK = 0x2E0059;

char grid[7][7];

int o_p1I=0, o_p1J=0, o_p2I=6, o_p2J=6;
int p1I=0, p1J=0, p2I=6, p2J=6;

Nav_Switch myNav( p17, p6, p7, p5, p8);
bool navUpPress = false, navLeftPress = false,
     navRightPress = false, navDownPress = false, navFirePress = false;

I2C i2c(p9, p10);
Mpr121 pad(&i2c, Mpr121::ADD_VSS);

bool padUpPress = false, padLeftPress = false,
     padRightPress = false, padDownPress = false, padFirePress = false;

struct Bomb {
    float createTime;
    int playerId;
    int startI, startJ;
    int power;
    int minX, maxX, minY, maxY;
    bool exploded, soundPlayed;
    float cookTime, expTime;
    int decay,color;
    int o_decay;
};

Bomb bombs[10];
int bombIndex=0, numBombs = 0;
int bufferSize = 10;

int p1Bombs=1, p2Bombs=1;
int p1Power=1, p2Power=1;

void resetVars(){
    gameRestart = false;
    
    o_p1I=0;
    o_p1J=0;
    o_p2I=6; 
    o_p2J=6;
    p1I=0;
    p1J=0;
    p2I=6;
    p2J=6;
    
    numBombs = 0;
    bombIndex = 0;
    
    p1Bombs = 1;
    p1Power = 1;
    p2Bombs = 1;
    p2Power = 1;
}

bool outOfBounds(int i, int j){
   return i < 0 || i > 6 || j < 0 || j > 6;
}

bool valid(int i, int j)
{
    if(outOfBounds(i,j)){
        return false;    
    }

    return grid[i][j] != 'b' && grid[i][j] != 's';
}

int getX(int j)
{
    return 8+16*j;
}

int getY(int i)
{
    return 8+16*i;
}

void createBomb(int pId, int i, int j)
{
    Bomb *b = &bombs[(bombIndex + numBombs)%bufferSize];
    numBombs++;

    b->playerId = pId;
    b->createTime = gameTime;
    b->startI = i;
    b->startJ = j;
    b->power = (pId == 1)? p1Power : p2Power;
    if(pId == 1) {
        p1Bombs--;
    } else {
        p2Bombs--;
    }
    b->exploded = false;
    b->cookTime = 1.5;
    b->expTime = .8;
    b->decay = 0;
    b->o_decay = 0;
    b->soundPlayed = false;
}

void resetBlock(int i, int j){
    if(grid[i][j] == ' '){
        grid[i][j] = 'c';
        return;
    }
    int rr = (int)(gameTime * 1000) + 1023 * i + 523 * j;
    if(rr%3 == 0){
        grid[i][j] = 'k';
    }else if(rr%3 == 1){
        grid[i][j] = 'd';
    }else{
        grid[i][j] = 'c'; 
    }
}

void clearArea(Bomb *b)
{
    //find minX
    int p = b->power;
    int i = b->startI;
    int j = b->startJ;
    while(p>0) {
        if(outOfBounds(i,j-1) || grid[i][j-1] == 's') {
            break;
        }           
        resetBlock(i,j-1);
        j--;
        p--;
    }
    b->minX = getX(j) + ((p==0)?3:0);

    //find maxX
    p = b->power;
    i = b->startI;
    j = b->startJ;
    while(p>0) {
        if(outOfBounds(i,j+1) || grid[i][j+1] == 's') {
            break;
        }
        resetBlock(i,j+1);
        j++;
        p--;
    }
    b->maxX = getX(j+1)+((p==0)?-3:0);

    //find minY
    p = b->power;
    i = b->startI;
    j = b->startJ;
    while(p>0) {
        if(outOfBounds(i-1,j) || grid[i-1][j] == 's') {
            break;
        }
        resetBlock(i-1,j);
        i--;
        p--;
    }
    b->minY = getY(i) + ((p==0)?3:0);

    //find maxY
    p = b->power;
    i = b->startI;
    j = b->startJ;
    while(p>0) {
        if(outOfBounds(i+1,j) || grid[i+1][j] == 's') {
            break;
        }
        resetBlock(i+1,j);
        i++;
        p--;
    }
    b->maxY = getY(i+1) + ((p==0)?-3:0);
}

void playExplosion()
{
    while(true){
        bool expFound = false;
        while(!expFound){
            for(int i = 0; i < numBombs; i++) {
                Bomb *b = &bombs[(bombIndex + i)%bufferSize]; 
                if(b->exploded && !b->soundPlayed){
                    expFound = true;
                    b->soundPlayed = true;
                }
            }
            Thread::wait(100);
        }
        FILE *explosion;
        explosion=fopen("/sd/explosion.wav","r");
        waver.play(explosion);
        fclose(explosion);
    }
}

void updateBombs()
{
    for(int i = 0; i < numBombs; i++) {
        Bomb *b = &bombs[(bombIndex + i)%bufferSize];
        if(!b->exploded && (gameTime - b->createTime) > b->cookTime) {
            b->exploded = true;
            clearArea(b);
            
        }
        if(!b->exploded) {
            int index = (int)((gameTime - b->createTime)/.15);
            if((index & 0x11) == 0) {
                b->color = WHITE;
            } else {
                b->color = BLACK;
            }
        } else {
            int index = (int)((gameTime - b->createTime - b->cookTime)/.2);
            if(index == 0) {
                b->color = WHITE;
            }
            if(index == 1) {
                b->color = 0xFFFF00;
            }
            if(index == 2) {
                b->color = 0xFF8800;
            }
            if(index >= 3) {
                b->color = RED;
            }
            index = (int)((gameTime - b->createTime - b->cookTime)/.25);
            b->decay = index;
        }
    }
}

void drawBombs()
{
    for(int i = 0; i < numBombs; i++) {
        Bomb *b = &bombs[(bombIndex + i)%bufferSize];
        int x = getX(b->startJ);
        int y = getY(b->startI);
        if(!b->exploded) {
            if(b->color == WHITE) {
                uLCD.filled_circle(x+8,y+8,5, WHITE);
                uLCD.filled_rectangle(x+7,y+1,x+9,y+3, WHITE);
            } else {
                uLCD.filled_circle(x+8,y+8,5, BLACK);
                uLCD.filled_rectangle(x+7,y+1,x+9,y+3, 0xFFFF00);
            }
        } else {
            if(b->o_decay != b->decay){
                b->o_decay = b->decay;
                uLCD.filled_rectangle(b->minX,y+3,b->maxX,y+13, BACK);
                uLCD.filled_rectangle(x+3,b->minY,x+13,b->maxY, BACK);
            }

            if(gameTime - b->createTime < (b->cookTime + b->expTime)) {
                uLCD.filled_rectangle(b->minX+b->decay,y+3+b->decay,b->maxX-b->decay,y+13-b->decay, b->color);
                uLCD.filled_rectangle(x+3+b->decay,b->minY+b->decay,x+13-b->decay,b->maxY-b->decay, b->color);
            } else {
                uLCD.filled_rectangle(b->minX,y+3,b->maxX,y+13, BACK);
                uLCD.filled_rectangle(x+3,b->minY,x+13,b->maxY, BACK);
                if(b->playerId == 1) {
                    p1Bombs++;
                } else {
                    p2Bombs++;
                }
                bombIndex=(bombIndex+1)%bufferSize;
                numBombs--;
            }
        }
    }
}

void generateGrid()
{
    //init empty
    for(int i = 0; i < 7; i++) {
        for(int j = 0; j < 7; j++) {
            grid[i][j] = ' ';
        }
    }
    //generate solids
    for(int i = 1; i < 7; i+=2) {
        for(int j = 1; j < 7; j+=2) {
            grid[i][j] = 's';
        }
    }
    //generate breakables
    for(int i = 0; i < 7; i++) {
        for(int j = 0; j < 7; j++) {
            if(grid[i][j] == 's') {
                continue;
            }
            bool hasBreakable = (i*17+j*349)%4 != 0;
            if(hasBreakable) {
                grid[i][j] = 'b';
            }
        }
    }
    //clear player regions
    grid[0][0] = ' ';
    grid[1][0] = ' ';
    grid[0][1] = ' ';
    grid[6][6] = ' ';
    grid[5][6] = ' ';
    grid[6][5] = ' ';
}

void drawImg(string imgName, int x, int y)
{
    RGBApixel *Colors = new RGBApixel [2];
    string file = "/sd/" + imgName;
    ReadBMPFromFile(x,y, file.c_str(), Colors, &uLCD);
}

void drawPlayer(int id)
{
    int col_prim, col_sec, x, y;
    if(id == 1) {
        col_prim = 0xFFD8B2;
        col_sec = 0xFFB2B2;
        x = getX(p1J);
        y = getY(p1I);
    }
    if(id == 2) {
        col_prim = 0xC6B2FF;
        col_sec = 0xFFB2FF;
        x = getX(p2J);
        y = getY(p2I);
    }
    uLCD.filled_rectangle(x+3,y+3,x+13,y+10, col_prim);
    uLCD.filled_rectangle(x+5,y+5,x+6,y+8, BLACK);
    uLCD.filled_rectangle(x+10,y+5,x+11,y+8, BLACK);
    uLCD.filled_rectangle(x+5,y+10,x+7,y+13, col_sec);
    uLCD.filled_rectangle(x+9,y+10,x+11,y+13, col_sec);
}

void initialDraw()
{
    uLCD.baudrate(3000000);
    uLCD.filled_rectangle(0,0,128,128, BACK);
    uLCD.text_width(2);
    uLCD.text_height(2);
    uLCD.color(WHITE);
    uLCD.locate(1,4);
    uLCD.printf("%s",startText);
    for(int i = -1; i < 8; i++){
        for(int j = -1; j < 8; j++){
            string img;
            int c = -1;
            if(i == -1 || j == -1 || i == 7 || j == 7){
               img = "wall.bmp";
               //c = BLACK;
            }else{
                if(grid[i][j] == ' '){
                    c = BACK;
                }
                if(grid[i][j] == 'b'){
                    img = "break.bmp";
                    //c = 0x888888;
                }
                if(grid[i][j] == 's'){
                    img = "wall.bmp";
                    //c = BLACK;
                }
            }
            int x = getX(j);
            int y = getY(i);
            if(c != -1){
                uLCD.filled_rectangle(x,y,x+16,y+16, c);
            }else{
                drawImg(img, x, y);
            }
        
        }
    }
    drawPlayer(1);
    drawPlayer(2);
}


void updateP1()
{
    if(!navUpPress && myNav.up()) {
        p1I++;
        navUpPress = true;
    }
    if(!navDownPress && myNav.down()) {
        p1I--;
        navDownPress = true;
    }
    if(!navLeftPress && myNav.left()) {
        p1J++;
        navLeftPress = true;
    }
    if(!navRightPress && myNav.right()) {
        p1J--;
        navRightPress = true;
    }
    if(!navFirePress && myNav.fire() && p1Bombs >0) {
        createBomb(1,p1I,p1J);
        navFirePress = true;
    }

    if(!valid(p1I,p1J)) {
        p1I = o_p1I;
        p1J = o_p1J;
    }
    
    if(grid[p1I][p1J] == 'f'){
        p1Power++;   
        grid[p1I][p1J] = 'c'; 
    }
    if(grid[p1I][p1J] == 'i'){
        p1Bombs++;    
        grid[p1I][p1J] = 'c';
    }

    if(!myNav.up()) {
        navUpPress = false;
    }
    if(!myNav.down()) {
        navDownPress = false;
    }
    if(!myNav.left()) {
        navLeftPress = false;
    }
    if(!myNav.right()) {
        navRightPress = false;
    }
    if(!myNav.fire()) {
        navFirePress = false;
    }
}

bool isOne(int val, int pos)
{
    return val >> pos & 0x01;
}

void updateP2()
{
    int value=pad.read(0x00);
    value +=pad.read(0x01)<<8;

    bool up =  isOne(value,5);
    bool down =  isOne(value,7);
    bool left = isOne(value,10);
    bool right = isOne(value,2);
    bool fire = isOne(value,6);

    if(!padUpPress && up) {
        p2I--;
        padUpPress = true;
    }
    if(!padDownPress && down) {
        p2I++;
        padDownPress = true;
    }
    if(!padLeftPress && left) {
        p2J--;
        padLeftPress = true;
    }
    if(!padRightPress && right) {
        p2J++;
        padRightPress = true;
    }
    if(!padFirePress && fire && p2Bombs > 0) {
        createBomb(2,p2I,p2J);
        padFirePress = true;
    }

    if(!valid(p2I,p2J)) {
        p2I = o_p2I;
        p2J = o_p2J;
    }
    
    if(grid[p2I][p2J] == 'f'){
        p2Power++;   
        grid[p2I][p2J] = 'c'; 
    }
    if(grid[p2I][p2J] == 'i'){
        p2Bombs++;    
        grid[p2I][p2J] = 'c';
    }
    
    if(!up) {
        padUpPress = false;
    }
    if(!down) {
        padDownPress = false;
    }
    if(!left) {
        padLeftPress = false;
    }
    if(!right) {
        padRightPress = false;
    }
    if(!fire) {
        padFirePress = false;
    }
}

void draw()
{
    for(int i = 0; i < 7; i++){
        for(int j = 0; j < 7; j++){
            if(grid[i][j] == 'c' || grid[i][j] == 'd' || grid[i][j] == 'k'){
                if(grid[i][j] == 'c') grid[i][j] = ' ';
                if(grid[i][j] == 'd') grid[i][j] = 'f';
                if(grid[i][j] == 'k') grid[i][j] = 'i';
                uLCD.filled_rectangle(getX(j),getY(i),getX(j+1),getY(i+1), BACK);
            }
            if(grid[i][j] == 'f'){
                uLCD.filled_circle(getX(j)+8,getY(i)+8,3,RED);
            }
            if(grid[i][j] == 'i'){
                uLCD.filled_circle(getX(j)+8,getY(i)+8,3,0xFFFF00);
            }
        }    
    }
    if(p1I != o_p1I || p1J != o_p1J) {
        int o_x = getX(o_p1J);
        int o_y = getY(o_p1I);
        uLCD.filled_rectangle(o_x,o_y,o_x+16,o_y+16, BACK);
        o_p1I = p1I;
        o_p1J = p1J;
    }
    if(p2I != o_p2I || p2J != o_p2J) {
        int o_x = getX(o_p2J);
        int o_y = getY(o_p2I);
        uLCD.filled_rectangle(o_x,o_y,o_x+16,o_y+16, BACK);
        o_p2I = p2I;
        o_p2J = p2J;
    }
    drawBombs();
    drawPlayer(1);
    drawPlayer(2);
}

void checkPlayerDeath(int pid){
    if(gameRestart) return;
    int centerX = getX((pid == 1)?(p1J):(p2J))+8;
    int centerY = getY((pid == 1)?(p1I):(p2I))+8;
    for(int i = 0; i < numBombs; i++) {
        Bomb *b = &bombs[(bombIndex + i)%bufferSize];
        if(b->exploded){
            int bx = getX(b->startJ);
            int by = getY(b->startI);
            if(b->minX < centerX && centerX < b->maxX && by < centerY && centerY < by+16){
                gameRestart = true;
            }
            if(b->minY < centerY && centerY < b->maxY && bx < centerX && centerX < bx+16){
                gameRestart = true;
            }
        }
    }
    if(gameRestart){
        if(pid == 1){
            startText = "P1 Died";
        }else{
            startText = "P2 Died";
        }
    }
}

int main()
{
    Thread thr(playExplosion);
    while(true){
        resetVars();
        generateGrid();
        initialDraw();
        while(!gameRestart) {
            updateP1();
            updateP2();
            updateBombs();
            checkPlayerDeath(1);
            checkPlayerDeath(2);
            draw();
            gameTime+=.02;
            wait(.02);
        }
        wait(3);
    }
}


Please log in to post comments.