diff --git a/assets/js/index.js b/assets/js/index.js index 660a2c6..1d162f2 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -1,4 +1,10 @@ window.addEventListener('load', () => { // iteration - 1: create & start the game + const game = new Game('canvas-game'); + game.start(); + // iteration - 2: add key listeners to the game + document.addEventListener("keydown", event => game.onKeyEvent(event)); + document.addEventListener("keyup", event => game.onKeyEvent(event)); + }); diff --git a/assets/js/models/brackgorund.js b/assets/js/models/background.js similarity index 66% rename from assets/js/models/brackgorund.js rename to assets/js/models/background.js index 401d439..8121374 100644 --- a/assets/js/models/brackgorund.js +++ b/assets/js/models/background.js @@ -5,19 +5,22 @@ class Background { // positions this.x = 0; this.y = 434; + this.vx = 3; + this.width = this.ctx.canvas.width; this.height = this.ctx.canvas.height; this.bgImg = new Image(); this.bgImg.src = 'assets/img/game-bg.png'; + // set image dimensions this.bgImg.width = this.width; this.bgImg.height = this.height; - this.footerImg = new Image(); this.footerImg.src = 'assets/img/game-bg-footer.png'; + // set image dimensions this.footerImg.width = this.width; this.footerImg.height = 64; @@ -26,11 +29,20 @@ class Background { draw() { // iteration 1: draw the static backgorund img - + this.ctx.drawImage(this.bgImg, 0, 0, this.bgImg.width, this.bgImg.height); + // iteration 1: draw footer img twice + this.ctx.drawImage(this.footerImg, this.x, this.y, this.footerImg.width, this.footerImg.height); + this.ctx.drawImage(this.footerImg, this.x + this.footerImg.width, this.y, this.footerImg.width, this.footerImg.height); + } move() { // iteration 1: move the ground + this.x -= this.vx; + // iteration 1: check bounds and reset position + if (this.x + this.width <= 0) { + this.x = 0; + } } } diff --git a/assets/js/models/flappybird.js b/assets/js/models/flappybird.js index e4460af..e9f72ab 100644 --- a/assets/js/models/flappybird.js +++ b/assets/js/models/flappybird.js @@ -9,11 +9,13 @@ class FlappyBird { this.sprite = new Image(); this.sprite.src = 'assets/img/bird.png'; + // sprite setup this.sprite.horizontalFrameIndex = 0; this.sprite.verticalFrameIndex = 0; this.sprite.horizontalFrames = 3; this.sprite.verticalFrames = 1; + this.sprite.onload = () => { this.sprite.frameWith = Math.floor(this.sprite.width / this.sprite.horizontalFrames); this.sprite.frameHeight = Math.floor(this.sprite.height / this.sprite.verticalFrames); @@ -29,25 +31,59 @@ class FlappyBird { switch (event.keyCode) { case KEY_UP: // iteration 2: jump! if necessary =D + if (isJumping) { + this.y -= this.jumpImpulse; + } + break; } } draw() { // draw sprite - + this.ctx.drawImage( + this.sprite, + (this.sprite.horizontalFrameIndex / this.sprite.horizontalFrames) * this.sprite.width, + 0, + this.sprite.width / this.sprite.horizontalFrames, + this.sprite.height, + this.x, + this.y, + this.width, + this.height + ) + + this.drawCount++; + // animate sprite this.animate(); } animate() { // iteration 2: configure frame animation + + if (this.drawCount > 10) { + this.drawCount = 0; + this.sprite.horizontalFrameIndex++; + + if (this.sprite.horizontalFrameIndex > this.sprite.horizontalFrames - 1) { + this.sprite.horizontalFrameIndex = 0; + } + } } move() { - // iteration 2: move the y + // iteration 2: move the y´ + this.y += this.vy; } collides(element) { // iteration 3: check collisions (true|false) + const colX = this.x + this.width > element.x + && this.x < element.x + element.width; + const colY = this.y + this.height > element.y + && this.y < element.y + element.height; + + return colX && colY; } + } diff --git a/assets/js/models/game.js b/assets/js/models/game.js index 43081e1..a7affdc 100644 --- a/assets/js/models/game.js +++ b/assets/js/models/game.js @@ -9,54 +9,103 @@ class Game { this.drawIntervalId = undefined; this.fps = 1000 / 60; - // iteration 1: setup the background - + // iteration 1: setup the backgroundo + this.background = new Background(this.ctx); + // iteration 2: setup the flappy + this.flappybird = new FlappyBird(this.ctx, 50, Math.floor(this.canvas.height / 2)); this.pipes = []; this.drawPipesCount = 0; this.pipesFrequency = 100; // bonus: setup the score + this.score = 0; + this.onGameEnd = onGameEnd; } onKeyEvent(event) { // iteration 2: link flappy key events + this.flappybird.onKeyEvent(event); + } start() { if (!this.drawIntervalId) { this.drawIntervalId = setInterval(() => { // Iteration 1: each 60f clear - move - draw - [next iterations: addPipes - checkCollisions - checkScore] + this.clear(); + this.move(); + this.draw(); + this.drawPipesCount++; + + if ( this.drawPipesCount === this.pipesFrequency ) { + this.drawPipesCount = 0; + + this.addPipes(); + this.pipes.forEach((pipe) => { + pipe.draw(); + pipe.move(); + }) + } + this.checkCollisions(); + this.checkScore(); + }, this.fps); } } stop() { // Iteration 1: stop the game + clearInterval(this.drawIntervalId); } restart() { // Bonus: restart on demand + this.pipes = []; + this.flappybird.x = 50; + this.flappybird.y = Math.floor(this.canvas.height / 2); + this.score = 0; + this.start(); } end() { // Iteration 4: stop the game and setup score + this.stop(); + + const restartLogo = new Image(); + restartLogo.src = 'assets/img/restart.png'; + + //onload to make sure the image is loaded + restartLogo.onload = () => { + this.ctx.drawImage( + restartLogo, + Math.floor((this.canvas.width - restartLogo.width) / 2), + Math.floor((this.canvas.height - restartLogo.height) / 2)); + }; } clear() { // Iteration 1: clean the screen + this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); + //Remove the pipes with x less than 0 + this.pipes = this.pipes.filter((pipe) => pipe.x + pipe.width >= 0); } move() { // Iteration 1: move the background + this.background.move(); // Iteration 2: move the flappy + this.flappybird.move(); // Iteration 3: move the pipes + this.pipes.forEach((pipe) => pipe.move()); } addPipes() { // Iteration 3: each draw pipes frequency cycles concat a pair of pipes to the pipes array and reset the draw cycle + this.pipes = this.pipes.concat(this.randPairOfPipes()); + this.drawPipesCount = 0; } randPairOfPipes() { @@ -64,24 +113,57 @@ class Game { const gap = (this.flappybird.height * 2) + this.flappybird.jumpImpulse; const topSize = Math.floor(Math.random() * (space - gap) * 0.75) const bottomSize = space - topSize - gap; - // Iteration 3: return two new pipes one at the top and other at the bottom - return [] + return [ + new Pipe(this.ctx, this.canvas.width, 0, topSize, 'top'), + new Pipe(this.ctx, this.canvas.width, this.canvas.height - this.background.footerImg.height - bottomSize, bottomSize, 'bottom'), + ] } - checkCollisions() { // Iteration 4: check pipes collisions among flappy and end game if any pipe collides with the bird + this.pipes.forEach(pipe => { + if (this.flappybird.collides(pipe)) { + this.end(); + } + }) + + if ((this.flappybird.y + this.flappybird.height) >= this.background.y || this.flappybird.y <= 0) { + this.end(); + } + + } checkScore() { // Bonus + for (let i = 0; i < this.pipes.length / 2; i += 2) { + const pipe = this.pipes[i]; + //isScored() checks if these pipes has been scored + if (this.flappybird.x > pipe.x + pipe.width && !pipe.isScored) { + this.score++; + pipe.isScored = true; + this.pipes[i+1].isScored = true; + } + } } draw() { // Iteration 1: draw the background + this.background.draw(); + // Iteration 2: draw the flappy + this.flappybird.draw(); + // Iteration 2: draw the pipes + this.pipes.forEach(pipe => pipe.draw()); + this.drawPipesCount++; + + this.ctx.save(); // Bonus: draw the score + this.ctx.fillStyle = "white"; + this.ctx.font = "25px FlappyFont"; + this.ctx.fillText(this.score, 15, 30); + + this.ctx.restore(); - this.drawPipesCount++; } } diff --git a/assets/js/models/pipe.js b/assets/js/models/pipe.js index cf42cd8..dfbc574 100644 --- a/assets/js/models/pipe.js +++ b/assets/js/models/pipe.js @@ -11,16 +11,45 @@ class Pipe { this.img = new Image(); // iteration 3: load the source checking the mode and setup this.with (must be the image with) this.img.src = `assets/img/pipe-${mode}.png`; + this.img.onload = () => { this.width = this.img.width; } + + this.isScored = false; //To check the score in the game } draw() { // iteration 3: draw the pipe don't worry if looks unscaled. You can start drawing a green rectangle + let imgCropY = 0; + if (this.img.complete) { + switch(this.mode) { + case 'top': + //If height parameter is less than the height of the image, crop the img + imgCropY = (this.height <= this.img.height) ? this.img.height - this.height : 0; + break; + case 'bottom': + imgCropY = 0; //In Bottom Mode don't crop the image + break; + } + + this.ctx.drawImage( + this.img, + 0, + imgCropY, + this.width, + this.height, + this.x, + this.y, + this.width, + this.height + ) + } + } move () { // iteration 3: move the pipe + this.x -= this.vx; } } diff --git a/index.html b/index.html index 9a40904..5f72f6f 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@

Flappybird

- +