cReTzUUUU 245 Posted July 19, 2020 Share Posted July 19, 2020 Bomberman <!DOCTYPE html> <html> <head> <title></title> <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> Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.