Skip to content

Instantly share code, notes, and snippets.

@straker
Last active September 3, 2023 21:29
  • Star 21 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?
Basic Bomberman HTML and JavaScript Game

Basic Bomberman HTML and JavaScript Game

This is a basic implementation of the NES game Bomberman, but it's missing a few things intentionally and they're left as further exploration for the reader.

Further Exploration

  • Player death
    • The player should die when it is hit by an explosion from a bomb
  • Powerups
    • Add powerups that increase the bomb size and the number of bombs the player can place. The powerups should spawn randomly after a soft wall is blown up
  • 2nd Player
    • Add a 2nd player to the game in the opposite corner and add additional controls so they can be played by someone else

Important note: I will answer questions about the code but will not add more features or answer questions about adding more features. This series is meant to give a basic outline of the game but nothing more.

License

(CC0 1.0 Universal) You're free to use this game and code in any project, personal or commercial. There's no need to ask permission before using these. Giving attribution is not required, but appreciated.

Other Basic Games

Support

Basic HTML Games are made possible by users like you. When you become a Patron, you get access to behind the scenes development logs, the ability to vote on which games I work on next, and early access to the next Basic HTML Game.

Top Patrons

  • Karar Al-Remahy
  • UnbrandedTech
  • Innkeeper Games
  • Nezteb
<!DOCTYPE html>
<html>
<head>
<title>Basic Bomberman HTML Game</title>
<meta charset="UTF-8">
<style>
html, body {
height: 100%;
margin: 0;
}
body {
background: black;
display: flex;
align-items: center;
justify-content: center;
}
canvas {
background: forestgreen;
}
</style>
</head>
<body>
<canvas width="960" height="832" id="game"></canvas>
<script>
const canvas = document.getElementById('game');
const context = canvas.getContext('2d');
const grid = 64;
const numRows = 13;
const numCols = 15;
// create a new canvas and draw the soft wall image. then we can use this
// canvas to draw the images later on
const softWallCanvas = document.createElement('canvas');
const softWallCtx = softWallCanvas.getContext('2d');
softWallCanvas.width = softWallCanvas.height = grid;
softWallCtx.fillStyle = 'black';
softWallCtx.fillRect(0, 0, grid, grid);
softWallCtx.fillStyle = '#a9a9a9';
// 1st row brick
softWallCtx.fillRect(1, 1, grid - 2, 20);
// 2nd row bricks
softWallCtx.fillRect(0, 23, 20, 18);
softWallCtx.fillRect(22, 23, 42, 18);
// 3rd row bricks
softWallCtx.fillRect(0, 43, 42, 20);
softWallCtx.fillRect(44, 43, 20, 20);
// create a new canvas and draw the soft wall image. then we can use this
// canvas to draw the images later on
const wallCanvas = document.createElement('canvas');
const wallCtx = wallCanvas.getContext('2d');
wallCanvas.width = wallCanvas.height = grid;
wallCtx.fillStyle = 'black';
wallCtx.fillRect(0, 0, grid, grid);
wallCtx.fillStyle = 'white';
wallCtx.fillRect(0, 0, grid - 2, grid - 2);
wallCtx.fillStyle = '#a9a9a9';
wallCtx.fillRect(2, 2, grid - 4, grid - 4);
// create a mapping of object types
const types = {
wall: '▉',
softWall: 1,
bomb: 2
};
// keep track of all entities
let entities = [];
// keep track of what is in every cell of the game using a 2d array. the
// template is used to note where walls are and where soft walls cannot spawn.
// '▉' represents a wall
// 'x' represents a cell that cannot have a soft wall (player start zone)
let cells = [];
const template = [
['▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉'],
['▉','x','x', , , , , , , , , ,'x','x','▉'],
['▉','x','▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉','x','▉'],
['▉','x', , , , , , , , , , , ,'x','▉'],
['▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉'],
['▉', , , , , , , , , , , , , ,'▉'],
['▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉'],
['▉', , , , , , , , , , , , , ,'▉'],
['▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉'],
['▉','x', , , , , , , , , , , ,'x','▉'],
['▉','x','▉', ,'▉', ,'▉', ,'▉', ,'▉', ,'▉','x','▉'],
['▉','x','x', , , , , , , , , ,'x','x','▉'],
['▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉','▉']
];
// populate the level with walls and soft walls
function generateLevel() {
cells = [];
for (let row = 0; row < numRows; row++) {
cells[row] = [];
for (let col = 0; col < numCols; col++) {
// 90% chance cells will contain a soft wall
if (!template[row][col] && Math.random() < 0.90) {
cells[row][col] = types.softWall;
}
else if (template[row][col] === types.wall) {
cells[row][col] = types.wall;
}
}
}
}
// blow up a bomb and its surrounding tiles
function blowUpBomb(bomb) {
// bomb has already exploded so don't blow up again
if (!bomb.alive) return;
bomb.alive = false;
// remove bomb from grid
cells[bomb.row][bomb.col] = null;
// explode bomb outward by size
const dirs = [{
// up
row: -1,
col: 0
}, {
// down
row: 1,
col: 0
}, {
// left
row: 0,
col: -1
}, {
// right
row: 0,
col: 1
}];
dirs.forEach((dir) => {
for (let i = 0; i < bomb.size; i++) {
const row = bomb.row + dir.row * i;
const col = bomb.col + dir.col * i;
const cell = cells[row][col];
// stop the explosion if it hit a wall
if (cell === types.wall) {
return;
}
// center of the explosion is the first iteration of the loop
entities.push(new Explosion(row, col, dir, i === 0 ? true : false));
cells[row][col] = null;
// bomb hit another bomb so blow that one up too
if (cell === types.bomb) {
// find the bomb that was hit by comparing positions
const nextBomb = entities.find((entity) => {
return (
entity.type === types.bomb &&
entity.row === row && entity.col === col
);
});
blowUpBomb(nextBomb);
}
// stop the explosion if hit anything
if (cell) {
return;
}
}
});
}
// bomb constructor function
function Bomb(row, col, size, owner) {
this.row = row;
this.col = col;
this.radius = grid * 0.4;
this.size = size; // the size of the explosion
this.owner = owner; // which player placed this bomb
this.alive = true;
this.type = types.bomb;
// bomb blows up after 3 seconds
this.timer = 3000;
// update the bomb each frame
this.update = function(dt) {
this.timer -= dt;
// blow up bomb if timer is done
if (this.timer <= 0) {
return blowUpBomb(this);
}
// change the size of the bomb every half second. we can determine the size
// by dividing by 500 (half a second) and taking the ceiling of the result.
// then we can check if the result is even or odd and change the size
const interval = Math.ceil(this.timer / 500);
if (interval % 2 === 0) {
this.radius = grid * 0.4;
}
else {
this.radius = grid * 0.5;
}
};
// render the bomb each frame
this.render = function() {
const x = (this.col + 0.5) * grid;
const y = (this.row + 0.5) * grid;
// draw bomb
context.fillStyle = 'black';
context.beginPath();
context.arc(x, y, this.radius, 0, 2 * Math.PI);
context.fill();
// draw bomb fuse moving up and down with the bomb size
const fuseY = (this.radius === grid * 0.5 ? grid * 0.15 : 0);
context.strokeStyle = 'white';
context.lineWidth = 5;
context.beginPath();
context.arc(
(this.col + 0.75) * grid,
(this.row + 0.25) * grid - fuseY,
10, Math.PI, -Math.PI / 2
);
context.stroke();
};
}
// explosion constructor function
function Explosion(row, col, dir, center) {
this.row = row;
this.col = col;
this.dir = dir;
this.alive = true;
// show explosion for 0.3 seconds
this.timer = 300;
// update the explosion each frame
this.update = function(dt) {
this.timer -= dt;
if (this.timer <=0) {
this.alive = false;
}
};
// render the explosion each frame
this.render = function() {
const x = this.col * grid;
const y = this.row * grid;
const horizontal = this.dir.col;
const vertical = this.dir.row;
// create a fire effect by stacking red, orange, and yellow on top of
// each other using progressively smaller rectangles
context.fillStyle = '#D72B16'; // red
context.fillRect(x, y, grid, grid);
context.fillStyle = '#F39642'; // orange
// determine how to draw based on if it's vertical or horizontal
// center draws both ways
if (center || horizontal) {
context.fillRect(x, y + 6, grid, grid - 12);
}
if (center || vertical) {
context.fillRect(x + 6, y, grid - 12, grid);
}
context.fillStyle = '#FFE5A8'; // yellow
if (center || horizontal) {
context.fillRect(x, y + 12, grid, grid - 24);
}
if (center || vertical) {
context.fillRect(x + 12, y, grid - 24, grid);
}
};
}
// player character (just a simple circle)
const player = {
row: 1,
col: 1,
numBombs: 1,
bombSize: 3,
radius: grid * 0.35,
render() {
const x = (this.col + 0.5) * grid;
const y = (this.row + 0.5) * grid;
context.save();
context.fillStyle = 'white';
context.beginPath();
context.arc(x, y, this.radius, 0, 2 * Math.PI);
context.fill();
}
}
// game loop
let last;
let dt;
function loop(timestamp) {
requestAnimationFrame(loop);
context.clearRect(0,0,canvas.width,canvas.height);
// calculate the time difference since the last update. requestAnimationFrame
// passes the current timestamp as a parameter to the loop
if (!last) {
last = timestamp;
}
dt = timestamp - last;
last = timestamp;
// update and render everything in the grid
for (let row = 0; row < numRows; row++) {
for (let col = 0; col < numCols; col++) {
switch(cells[row][col]) {
case types.wall:
context.drawImage(wallCanvas, col * grid, row * grid);
break;
case types.softWall:
context.drawImage(softWallCanvas, col * grid, row * grid);
break;
}
}
}
// update and render all entities
entities.forEach((entity) => {
entity.update(dt);
entity.render();
});
// remove dead entities
entities = entities.filter((entity) => entity.alive);
player.render();
}
// listen to keyboard events to move the snake
document.addEventListener('keydown', function(e) {
let row = player.row;
let col = player.col;
// left arrow key
if (e.which === 37) {
col--;
}
// up arrow key
else if (e.which === 38) {
row--;
}
// right arrow key
else if (e.which === 39) {
col++;
}
// down arrow key
else if (e.which === 40) {
row++;
}
// space key (bomb)
else if (
e.which === 32 && !cells[row][col] &&
// count the number of bombs the player has placed
entities.filter((entity) => {
return entity.type === types.bomb && entity.owner === player
}).length < player.numBombs
) {
// place bomb
const bomb = new Bomb(row, col, player.bombSize, player);
entities.push(bomb);
cells[row][col] = types.bomb;
}
// don't move the player if something is already at that position
if (!cells[row][col]) {
player.row = row;
player.col = col;
}
});
// start the game
generateLevel();
requestAnimationFrame(loop);
</script>
</body>
</html>
@They-Got-Offline
Copy link

They-Got-Offline commented Apr 6, 2020

Help! There's a problem: The player doesn't die if it's hit by a bomb

@Pro496951
Copy link

Help! There's a problem: The player doesn't die if it's hit by a bomb

They-got-offline Player death is in futher exploration.you have to add it yourself!

@voxxal
Copy link

voxxal commented Apr 17, 2020

Wow this is really good! I'm going to spend so much time dissecting them. can you try doing space invaders next?
thanks

@straker
Copy link
Author

straker commented Apr 17, 2020

@v0Xal sure, I could get that going for the next one.

@23sottry
Copy link

could you try doing Oregon trail at some point in the future if at all possible?
also what scripting engine would recommend for someone still new to HTML?

@straker
Copy link
Author

straker commented Apr 20, 2020

@23sottry Oregon trail is probably too complex for this series unfortunately. I'm not sure what a scripting engine would be in terms of the web.

Copy link

ghost commented Jun 4, 2020

this is all i see and when i click them it does not do anything Help!
image

@straker
Copy link
Author

straker commented Jun 4, 2020

@pusheen13 what are you trying to click? The game is controlled with the arrow keys and the spacebar.

Copy link

ghost commented Jun 5, 2020

Thanks, can you make a piano with html, css, js.?

@straker
Copy link
Author

straker commented Jun 5, 2020

@pusheen13 it's a bit outside the scope of this project, though I'm sure someone has to have done it already.

@voxxal
Copy link

voxxal commented Jun 5, 2020

@UnfinishedComb
Copy link

How would I add a victory thing? Like, if you win some text saying 'victory' appears?

@Kingybu
Copy link

Kingybu commented Sep 13, 2020

I LOVE your other games and put them on my website (i have linked you ❤️)

@straker
Copy link
Author

straker commented Sep 14, 2020

@Kingybu glad you liked them. Thanks for the link back.

@Kingybu
Copy link

Kingybu commented Sep 15, 2020

is there a Chance to make flappy bird? :D

@straker
Copy link
Author

straker commented Sep 15, 2020

Sure, I can look into that one

@The-Frenchy
Copy link

Hi !
Thanks you a lot for the game you offer us !
I added a second player, manage their death and also added 2 basic power ups.
If you want i can send it to you. (I never used JS before so i could hurt your eyes, warned you ahah)

@Dheirya
Copy link

Dheirya commented Oct 23, 2020

How do you add death? I'm still stuck.

@wilsoncolin544
Copy link

Hello when i started it show only blank space anyone can help me

@wilsoncolin544
Copy link

16123276746862133788927481841687
Only like this anyone can help me

@wilsoncolin544
Copy link

@staker when i started the application my screen is blank please help me

@straker
Copy link
Author

straker commented Feb 3, 2021

@wilsoncolin544 Is there a JavaScript error in the devtools? I can't think of a reason it wouldn't work off the top of my head.

@wilsoncolin544
Copy link

ok, thank you @straker I will check

@OzzyCantu
Copy link

@straker how do I replace the up, down, left, right keys with the WASD keys?

@straker
Copy link
Author

straker commented Apr 8, 2021

@OzzyCantu you can replace these key code numbers with the key code numbers of WASD. I like to use https://keycode.info/ to find the key code associated with a key

@Jackson595
Copy link

GREAT GAME!

@Jackson595
Copy link

How do you die?

@straker
Copy link
Author

straker commented Apr 26, 2021

@Jackson595 having the player die has ben intentionally left out.

@Jackson595
Copy link

thank you!

@aizigao
Copy link

aizigao commented Nov 23, 2022

nice!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment