From 7493713529a8c59a8a0aae587d3054d5f9dcfaf2 Mon Sep 17 00:00:00 2001 From: JDG Date: Sun, 14 Nov 2021 23:49:49 +0100 Subject: [PATCH] Added the bricks (and improve the bouncing) --- Ball.js | 175 +++++++++++++++++++++++++++++------------- Bar.js | 5 +- Board.js | 6 +- Bricks.js | 30 ++++++++ GameOver.js | 31 +++++--- GamePlay.js | 54 ++++++++++--- Key.js => Keyboard.js | 2 +- Levels.js | 39 ++++++++++ Lives.js | 20 ++--- Score.js | 20 ++--- index.html | 9 ++- index.js | 23 ++++-- 12 files changed, 303 insertions(+), 111 deletions(-) create mode 100644 Bricks.js rename Key.js => Keyboard.js (96%) create mode 100644 Levels.js diff --git a/Ball.js b/Ball.js index e0acddd..5dae42d 100644 --- a/Ball.js +++ b/Ball.js @@ -1,51 +1,53 @@ class Ball { - constructor(ctx, bar) { - this.ctx = ctx; - this.bar = bar; - this.size = 15; + constructor() { + this.size = 10; + this.moving = false; - this.x = this.bar.x + (this.bar.w) / 2; - this.y = this.bar.y - this.size -1; - - - this.speed = 5; + this.speed = 7; // this.angle = 90; - this.bounce(220,340); + this.setAngle(180 +60, 360 - 60); this.color = 'red'; + this.limits = null; + + this.angleTR = this.g2r(360); + this.angleBR = this.g2r(90); + this.angleBL = this.g2r(180); + this.angleTL = this.g2r(270); - this.limits = { - l: this.size, - t: this.size, - r: this.ctx.canvas.width - this.size, - b: this.ctx.canvas.height - } } start() { this.moving = true; } - update() { - if (this.move()) { - this.draw(); + update(ctx, x, y) { + this.limits ??= { + l: this.size, + t: this.size, + r: ctx.canvas.width - this.size, + b: ctx.canvas.height + }; + + if (this.move(x,y)) { + this.draw(ctx); return true; } return false; } - draw() { - this.ctx.beginPath(); - this.ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI, false); - this.ctx.fillStyle = this.color; - this.ctx.fill(); - this.ctx.lineWidth = 1; - this.ctx.strokeStyle = '#003300'; - this.ctx.stroke(); + draw(ctx) { + ctx.beginPath(); + ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI, false); + ctx.fillStyle = this.color; + ctx.fill(); + ctx.lineWidth = 1; + ctx.strokeStyle = '#003300'; + ctx.stroke(); } - move() { + move(x,y) { if (this.moving) { this.x += this.speed * Math.cos(this.angle); this.y += this.speed * Math.sin(this.angle); @@ -55,43 +57,108 @@ class Ball { this.moving = false; return false; } - if (this.y > this.bar.y ) return true; // Ball is lost, don't check anything else - if ( (this.y + this.size) > this.bar.y && this.x > this.bar.x && this.x < (this.bar.x + this.bar.w) ) { - // Down (Bar) - this.bounce(220,340); - } else { - if ( this.x < this.limits.l ) { - // Left wall - if (this.angle this.limits.r ) { - // Right wall - if (this.angle= x1 ) { + this.bounceR(r); + return true; + } + if (this.y <= y0 ) { + this.bounceT(r); + return true; + } + if (this.y >= y1 ) { + this.bounceB(r); + return true; + } + return false; + } + /* + TL 270 TR + 180 0 + BL 90 BR + + + B + --------- + R | | L + --------- + T + + */ + + collide(x0,y0,x1,y1) { // 0 = hit Left/Right, 1 = hit Up/Down + let r = 20; + if (this.x>=x0 && this.x<=x1 && (this.y+this.size)>=y0 && (this.y+this.size)=x0 && this.x<=x1 && (this.y-this.size)<=y1 && (this.y-this.size)>y0) { + this.bounceT(r); + return true; + } + + if (this.y>=y0 && this.y<=y1 && (this.y+this.size)>=x0 && (this.y-this.size)=y0 && this.y<=y1 && (this.y-this.size)<=x1 && (this.y-this.size)>x0) { + this.bounceL(r); + return true; + } + + return false; + } + + setAngle(min, max) { + this.angle = this.g2r(Math.floor(Math.random() * (max - min)) + min); } g2r(deg) { - return ( ((360+deg)%360) * Math.PI) / 180.0; + return (((360 + deg) % 360) * Math.PI) / 180.0; } r2g(rad) { - return rad*180 / Math.PI; + return rad * 180 / Math.PI; } } \ No newline at end of file diff --git a/Bar.js b/Bar.js index 91ffc8c..894ba03 100644 --- a/Bar.js +++ b/Bar.js @@ -3,14 +3,15 @@ class Bar { this.ctx = ctx; this.key = key; - this.w = 100; - this.h = 20; + this.w = 80; + this.h = 15; this.speed = 10; // Target Speed this._speed = 0; // Current Speed and direction this.xLimit = (ctx.canvas.width - this.w); this.reset(); } + reset() { this.x = (this.ctx.canvas.width - this.w) / 2; this._y = (this.ctx.canvas.height - this.h * 2); diff --git a/Board.js b/Board.js index cb78045..5ddbb45 100644 --- a/Board.js +++ b/Board.js @@ -29,13 +29,17 @@ class Board { } next(nextStage) { + this.loopStop(); + this.resolve(nextStage); + } + + loopStop() { this.stop = true; if (this.requestID) { cancelAnimationFrame(this.requestID); this.requestID = null; } this.ctx.clearRect(0, 0, this.w, this.h); - this.resolve(nextStage); } loop() { diff --git a/Bricks.js b/Bricks.js new file mode 100644 index 0000000..0ad6e1a --- /dev/null +++ b/Bricks.js @@ -0,0 +1,30 @@ +class Brick { + constructor(type, column, row) { + this.type = type; + this.row = row; + this.column = column; + + this.vspace = 2; + this.hspace = 2; + + this.w = (360/8) -this.hspace; + this.h = (20) - this.vspace; + this.x = (this.w +this.hspace)*column; + this.y = 80 + (this.h +this.vspace)*row; + + this.alive = true; + } + + update(ctx) { + if (!this.alive) return false; + + switch(this.type) { + case 1: + ctx.fillStyle = 'blue'; + ctx.fillRect(this.x+1, this.y, this.w, this.h); + break; + } + return true; + } + +} \ No newline at end of file diff --git a/GameOver.js b/GameOver.js index 495aa01..0a06333 100644 --- a/GameOver.js +++ b/GameOver.js @@ -1,16 +1,25 @@ class GameOver { - constructor(ctx) { - this.ctx = ctx; - this.x = ctx.canvas.width; - this.y = ctx.canvas.height / 2 - 48; + constructor() { + this.w = 240; + this.h = 120; + + this.cx = 360 / 2; + this.cy = 640 / 2; + + this.x = this.cx - this.w/2; + this.y = this.cy - this.h/2; } - update() { - this.centerText('GAME OVER', this.y, '48px', 'Consolas', 'Black'); + update(ctx) { + ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; + ctx.fillRect(this.x, this.y, this.w, this.h); + + this.centerText(ctx, 'GAME OVER', this.cy, '48px', 'Consolas', 'Black'); + this.centerText(ctx, '(Press "N" to start)', this.cy + 48, '24px', 'Consolas', 'Black'); } - centerText(txt, y, s, f, c) { - this.ctx.font = s + ' ' + f; - this.ctx.fillStyle = 'Black'; - let x = (this.ctx.canvas.width - this.ctx.measureText(txt).width) / 2; - this.ctx.fillText(txt, x, y); + centerText(ctx, txt, y, s, f, c) { + ctx.font = s + ' ' + f; + ctx.fillStyle = 'Black'; + let x = (ctx.canvas.width - ctx.measureText(txt).width) / 2; + ctx.fillText(txt, x, y); } } diff --git a/GamePlay.js b/GamePlay.js index d83d328..bd1d45f 100644 --- a/GamePlay.js +++ b/GamePlay.js @@ -4,39 +4,73 @@ class GamePlay extends Board { this.controls = { 'KeyX': ()=>{ - this.balls.push(new Ball(this.ctx, this.bar)); + let b = new Ball(); + b.update(this.ctx, this.bar.x + this.bar.w/2, this.bar.y); + + this.balls.push(b); this.balls[this.balls.length - 1].start(); }, 'Space': ()=>{ this.balls[0].moving = true; + }, + 'KeyN': ()=>{ + if(this.lives.get()==0) this.next(1); } } - this.score = new Score(ctx); - this.lives = new Lives(ctx); + this.gameOver = new GameOver(); + this.score = new Score(); + this.lives = new Lives(); this.bar = new Bar(ctx, key); + this.levels = new Levels(); this.newGame(); } newGame() { this.lives.reset(); this.score.reset(); + this.nextLevel(1); + } + + nextLevel(lvl) { + this.level = lvl; + this.bricks = this.levels.load(lvl); this.bar.reset(); this.balls = []; - this.balls.push(new Ball(this.ctx, this.bar)); + this.balls.push(new Ball()); } update() { if(this.lives.get()==0) { - gameOver.update(); + this.loopStop(); + this.gameOver.update(this.ctx); +// this.next(2); } else { - this.balls = this.balls.filter(ball => ball.update()); - if (this.balls.length==0) { - if ( !this.lives.lost() ) this.balls.push(new Ball(this.ctx, this.bar)); + this.balls = this.balls.filter(ball => { + let r = ball.update(this.ctx, this.bar.x + this.bar.w/2, this.bar.y); + ball.collide( this.bar.x, this.bar.y, this.bar.x + this.bar.w, this.bar.y + this.bar.h ); + this.bricks.forEach(b=>{ + if(b.alive){ + if ( ball.collide(b.x,b.y,b.x+b.w,b.y+b.h) ) { + this.score.add(1); + b.alive = false; + } + + }}); + return r; + } + ); + if (this.bricks.length==0) { + this.nextLevel(++this.level); } + if (this.balls.length==0) { + if ( !this.lives.lost() ) this.balls.push(new Ball()); + } + this.bricks = this.bricks.filter(brick => brick.update(this.ctx)); +// if ( this.bricks.length == 0 ) this.nextLevel(); this.bar.update(); } - this.score.update(); - this.lives.update(); + this.score.update(this.ctx); + this.lives.update(this.ctx); } } diff --git a/Key.js b/Keyboard.js similarity index 96% rename from Key.js rename to Keyboard.js index fecd33f..a813bc8 100644 --- a/Key.js +++ b/Keyboard.js @@ -1,4 +1,4 @@ -class Key { +class Keyboard { constructor(onKeydown) { this._pressed = {}; this.cb_onKeydown = onKeydown; diff --git a/Levels.js b/Levels.js new file mode 100644 index 0000000..e6b276f --- /dev/null +++ b/Levels.js @@ -0,0 +1,39 @@ +class Levels { + load(lvl) { + let map = []; + switch (+lvl) { + case 1: + map = [].concat( + this.row(0, [1, 0, 1, 0, 0, 1, 0, 1]), + this.row(1, [1, 1, 1, 1, 1, 1, 1, 1]), + this.row(3, [0, 1, 1, 1, 1, 1, 1, 0]), + this.row(4, [1, 1, 1, 1, 1, 1, 1, 1]) + ); + break; + default: + map = [].concat( + this.row(0, [1, 1, 1, 1, 1, 1, 1, 1]), + this.row(1, [1, 1, 1, 1, 1, 1, 1, 1]), + this.row(2, [1, 1, 1, 1, 1, 1, 1, 1]), + this.row(3, [1, 1, 1, 1, 1, 1, 1, 1]), + this.row(4, [1, 1, 1, 1, 1, 1, 1, 1]) + ); + break; + } + return this.toBricks(map); + } + + row(r, bricksTypes) { + let row = []; + for (var i = 0; i < bricksTypes.length; i++) row.push([bricksTypes[i], i, r]); + return row; + } + + toBricks(map) { + let bricks = []; + map.forEach(b => { + if (b[0] > 0) bricks.push(new Brick(b[0], b[1], b[2])); + }); + return bricks; + } +} \ No newline at end of file diff --git a/Lives.js b/Lives.js index f163ac2..b14326b 100644 --- a/Lives.js +++ b/Lives.js @@ -1,15 +1,10 @@ class Lives { - constructor(ctx) { - this.ctx = ctx; + constructor() { this.reset(); - - this.ctx.font = "30px Consolas"; - let m = ctx.measureText('Score: 00000'); - this.x = ctx.canvas.width - m.width; - this.y = 20; } reset() { this.lives = 3; + this.y = 10; } lost() { this.lives--; @@ -18,14 +13,15 @@ class Lives { get() { return this.lives; } - update() { - if (this.y != 60) this.y++; + update(ctx) { + if (this.y != 48) this.y++; if (this.y > 0) { let txt = (String.fromCharCode(parseInt('26A1', 16))+" ").repeat(this.lives); - this.ctx.font = "30px Consolas"; - this.ctx.fillStyle = 'Green'; - this.ctx.fillText(txt, this.x, this.y); + ctx.font = "18px Consolas"; + ctx.fillStyle = 'Green'; + this.x = ctx.canvas.width - ctx.measureText(txt).width; + ctx.fillText(txt, this.x, this.y); } } } diff --git a/Score.js b/Score.js index 474d733..60b42db 100644 --- a/Score.js +++ b/Score.js @@ -1,25 +1,21 @@ class Score { - constructor(ctx) { - this.ctx = ctx; + constructor() { this.reset(); - - this.ctx.font = "30px Consolas"; - let m = ctx.measureText('Score: 00000'); - this.x = ctx.canvas.width - m.width; - this.y = -10; } reset() { this.points = 0; + this.x = 235; + this.y = -10; } add(x) { this.points += x; } - update() { - if (this.y != 30) this.y++; + update(ctx) { + if (this.y != 20) this.y++; if (this.y > 0) { - this.ctx.font = "30px Consolas"; - this.ctx.fillStyle = 'Black'; - this.ctx.fillText('Score: ' + this.points, this.x, this.y); + ctx.font = "20px Consolas"; + ctx.fillStyle = 'Black'; + ctx.fillText('Score: ' + this.points, this.x, this.y); } } } diff --git a/index.html b/index.html index 602f477..81d8050 100644 --- a/index.html +++ b/index.html @@ -5,14 +5,21 @@ body { margin: 0; padding: 0; + align-items: center; + justify-content: center; + display: flex; } + canvas { border:1px solid black; } - + + + + diff --git a/index.js b/index.js index af406ff..5199fa9 100644 --- a/index.js +++ b/index.js @@ -7,20 +7,29 @@ document.addEventListener('DOMContentLoaded', init); function init() { let ctx, canvas = document.createElement("canvas"); - canvas.width = window.innerWidth - canvas.height = window.innerHeight + canvas.width = 360; // window.innerWidth + canvas.height = 640; // window.innerHeight ctx = canvas.getContext('2d'); document.body.insertBefore(canvas, document.body.childNodes[0]); + let container = document.querySelector("body"); + let resize = (e) => { + container.clientWidth / container.clientHeight > 1 + ? (canvas.style.height = "100vh") && (canvas.style.width = "auto") + : (canvas.style.height = "auto") && (canvas.style.width = "100vw"); + }; + resize(); + container.onresize = resize; - let key = new Key(), board; + + let key = new Keyboard(), board; function runBoard(stage) { - switch(stage) { - case 1: board = new GamePlay(ctx, key); break; - default: board = new GameIntro(ctx,key); break; + switch (stage) { + case 1: board = new GamePlay(ctx, key); break; + default: board = new GameIntro(ctx, key); break; } board .run() - .then( stage=>runBoard(stage), e=>{} );; + .then(stage => runBoard(stage), e => { });; } runBoard(0); }