diff --git a/BrickBreakerGame/.codesandbox/workspace.json b/BrickBreakerGame/.codesandbox/workspace.json new file mode 100755 index 0000000..e7d06a2 --- /dev/null +++ b/BrickBreakerGame/.codesandbox/workspace.json @@ -0,0 +1,20 @@ +{ + "responsive-preview": { + "Mobile": [ + 320, + 675 + ], + "Tablet": [ + 1024, + 765 + ], + "Desktop": [ + 1400, + 800 + ], + "Desktop HD": [ + 1920, + 1080 + ] + } +} \ No newline at end of file diff --git a/BrickBreakerGame/README.md b/BrickBreakerGame/README.md new file mode 100755 index 0000000..62cc16a --- /dev/null +++ b/BrickBreakerGame/README.md @@ -0,0 +1,4 @@ +# BrickBreaker +Created with CodeSandbox works in Parcel + +![brickbreaker](https://user-images.githubusercontent.com/60597318/118232580-36b1e400-b4aa-11eb-968c-0ecc3c8ecae8.gif) diff --git a/BrickBreakerGame/assets/images/ball.png b/BrickBreakerGame/assets/images/ball.png new file mode 100644 index 0000000..6c00647 Binary files /dev/null and b/BrickBreakerGame/assets/images/ball.png differ diff --git a/BrickBreakerGame/assets/images/brick.png b/BrickBreakerGame/assets/images/brick.png new file mode 100644 index 0000000..a5d6cee Binary files /dev/null and b/BrickBreakerGame/assets/images/brick.png differ diff --git a/BrickBreakerGame/index.html b/BrickBreakerGame/index.html new file mode 100755 index 0000000..e58239f --- /dev/null +++ b/BrickBreakerGame/index.html @@ -0,0 +1,25 @@ + + + + + + + JavaScript Game + + + + + brick + + + + diff --git a/BrickBreakerGame/package.json b/BrickBreakerGame/package.json new file mode 100755 index 0000000..3292777 --- /dev/null +++ b/BrickBreakerGame/package.json @@ -0,0 +1,33 @@ +{ + "name": "brickbreaker", + "version": "1.0.0", + "description": "JavaScript example starter project", + "main": "index.html", + "scripts": { + "start": "parcel index.html --open", + "build": "parcel build index.html" + }, + "dependencies": { + "parcel-bundler": "^1.6.1" + }, + "devDependencies": { + "@babel/core": "7.2.0" + }, + "resolutions": { + "@babel/preset-env": "7.13.8" + }, + "keywords": [ + "javascript", + "starter" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/AbdulAziz0682/BrickBreaker.git" + }, + "author": "Abdul Aziz Khoso", + "license": "ISC", + "bugs": { + "url": "https://github.com/AbdulAziz0682/BrickBreaker/issues" + }, + "homepage": "https://github.com/AbdulAziz0682/BrickBreaker#readme" +} diff --git a/BrickBreakerGame/src/ball.js b/BrickBreakerGame/src/ball.js new file mode 100755 index 0000000..7d6fa4d --- /dev/null +++ b/BrickBreakerGame/src/ball.js @@ -0,0 +1,41 @@ +import { detectCollision } from "./collisionDetection"; +export default class Ball { + constructor(game) { + this.image = document.getElementById("ballImg"); + this.size = 16; + this.gameHeight = game.gameHeight; + this.gameWidth = game.gameWidth; + this.game = game; + this.reset(); + } + reset(){ + this.pos = { x: 10, y: 500 }; + this.speed = { x: 4, y: -4 }; + console.log('Ball pos: '+this.pos.x+","+this.pos.y); + } + draw(ctx) { + ctx.drawImage(this.image, this.pos.x, this.pos.y, this.size, this.size); + } + update() { + this.pos.x += this.speed.x; + this.pos.y += this.speed.y; + //left and right walls + if (this.pos.x + this.size > this.gameWidth || this.pos.x < 0) { + this.speed.x = -this.speed.x; + } + //top + if (this.pos.y < 0) { + this.speed.y = -this.speed.y; + } + //bottom + if(this.pos.y + this.size >= this.gameHeight){ + this.game.lives--; + this.reset(); + } + //collision with paddle + if (detectCollision(this, this.game.paddle)) { + this.speed.y = -this.speed.y; + this.pos.y = this.game.paddle.pos.y - this.size; + } + } +} diff --git a/BrickBreakerGame/src/brick.js b/BrickBreakerGame/src/brick.js new file mode 100755 index 0000000..b2bb553 --- /dev/null +++ b/BrickBreakerGame/src/brick.js @@ -0,0 +1,20 @@ +import { detectCollision } from "./collisionDetection"; +export default class Brick { + constructor(game, position) { + this.image = document.getElementById("brickImg"); + this.pos = position; + this.game = game; + this.width = 80; + this.height = 25; + this.markForDeletion = false; + } + update() { + if (detectCollision(this.game.ball, this)) { + this.game.ball.speed.y = -this.game.ball.speed.y; + this.markForDeletion = true; + } + } + draw(ctx) { + ctx.drawImage(this.image, this.pos.x, this.pos.y, this.width, this.height); + } +} diff --git a/BrickBreakerGame/src/collisionDetection.js b/BrickBreakerGame/src/collisionDetection.js new file mode 100755 index 0000000..9829101 --- /dev/null +++ b/BrickBreakerGame/src/collisionDetection.js @@ -0,0 +1,20 @@ +export function detectCollision(ball, gameObject) { + let bottomOfBall = ball.pos.y + ball.size; + let topOfBall = ball.pos.y; + + let topOfObject = gameObject.pos.y; + let leftSideOfObject = gameObject.pos.x; + let rightSideoOfObject = gameObject.pos.x + gameObject.width; + let bottomOfObject = gameObject.pos.y + gameObject.height; + + if ( + bottomOfBall >= topOfObject && + topOfBall <= bottomOfObject && + ball.pos.x >= leftSideOfObject && + ball.pos.x <= rightSideoOfObject + ) { + return true; + } else { + return false; + } +} diff --git a/BrickBreakerGame/src/game.js b/BrickBreakerGame/src/game.js new file mode 100755 index 0000000..5397c70 --- /dev/null +++ b/BrickBreakerGame/src/game.js @@ -0,0 +1,94 @@ +import Paddle from "./paddle"; +import InputHandler from "./input"; +import Ball from "./ball"; +import { buildLevel, level1, level2 } from "./levels"; + +const GAME_STATE = { + PAUSED: 0, + RUNNING: 1, + MENU: 2, + GAMEOVER: 3, + NEWLEVEL: 4 +}; + +export default class Game { + constructor(gameWidth, gameHeight) { + this.gameWidth = gameWidth; + this.gameHeight = gameHeight; + this.gameState = GAME_STATE.MENU; + this.paddle = new Paddle(this); + this.ball = new Ball(this); + this.lives = 3; + this.bricks = []; + new InputHandler(this.paddle, this); + this.gameObjects = []; + + this.levels = [level1, level2]; + this.currentLevel = 0; + } + start() { + if(this.gameState !== GAME_STATE.MENU && this.gameState !== GAME_STATE.NEWLEVEL) return; + this.bricks = buildLevel(this, this.levels[this.currentLevel]); + this.ball.reset(); + this.gameObjects = [this.paddle, this.ball]; + this.gameState = GAME_STATE.RUNNING; + } + update(ctx) { + if(this.lives == 0) this.gameState = GAME_STATE.GAMEOVER; + if ((this.gameState == GAME_STATE.PAUSED) || (this.gameState == GAME_STATE.MENU) || (this.gameState == GAME_STATE.GAMEOVER)) return; + if(this.bricks.length === 0){ + this.currentLevel++; + this.gameState = GAME_STATE.NEWLEVEL; + this.start(); + + } + [...this.gameObjects, ...this.bricks].forEach((obj) => { + obj.update(ctx); + }); + this.bricks = this.bricks.filter((obj) => !obj.markForDeletion); + } + draw(ctx) { + [...this.gameObjects, ...this.bricks].forEach((obj) => { + obj.draw(ctx); + }); + if(this.gameState == GAME_STATE.PAUSED){ + ctx.rect(0, 0, this.gameWidth, this.gameHeight); + ctx.fillStyle = "rgb(0, 0, 5)"; + ctx.fill(); + + ctx.font = "30px Arial"; + ctx.fillStyle = "white"; + ctx.textAlign = 'center'; + ctx.fillText("Paused", this.gameWidth/2, this.gameHeight/2); + } + if(this.gameState == GAME_STATE.MENU){ + ctx.rect(0, 0, this.gameWidth, this.gameHeight); + ctx.fillStyle = "rgb(0, 0, 5)"; + ctx.fill(); + + ctx.font = "30px Arial"; + ctx.fillStyle = "white"; + ctx.textAlign = 'center'; + ctx.fillText("Press Space to Start", this.gameWidth/2, this.gameHeight/2); + } + if(this.gameState == GAME_STATE.GAMEOVER){ + ctx.rect(0, 0, this.gameWidth, this.gameHeight); + ctx.fillStyle = "rgb(0, 0, 5)"; + ctx.fill(); + + ctx.font = "30px Arial"; + ctx.fillStyle = "white"; + ctx.textAlign = 'center'; + ctx.fillText("GAME OVER", this.gameWidth/2, this.gameHeight/2); + } + } + togglePause() { + + if (this.gameState == GAME_STATE.PAUSED) { + console.log('Pause func') + this.gameState = GAME_STATE.RUNNING; + } else { + this.gameState = GAME_STATE.PAUSED; + } + } +} diff --git a/BrickBreakerGame/src/index.js b/BrickBreakerGame/src/index.js new file mode 100755 index 0000000..7a6add0 --- /dev/null +++ b/BrickBreakerGame/src/index.js @@ -0,0 +1,17 @@ +import Game from "./game"; + +const GAME_WIDTH = 800; +const GAME_HEIGHT = 600; +let canvas = document.getElementById("gameScreen"); +let ctx = canvas.getContext("2d"); + +let game = new Game(GAME_WIDTH, GAME_HEIGHT); + +function gameloop(timestamp) { + ctx.clearRect(0, 0, GAME_WIDTH, GAME_HEIGHT); + game.update(); + game.draw(ctx); + requestAnimationFrame(gameloop); +} + +gameloop(); diff --git a/BrickBreakerGame/src/input.js b/BrickBreakerGame/src/input.js new file mode 100755 index 0000000..7f4f898 --- /dev/null +++ b/BrickBreakerGame/src/input.js @@ -0,0 +1,34 @@ +export default class InputHandler { + constructor(paddle, game) { + document.addEventListener("keydown", (e) => { + switch (e.keyCode) { + case 37: + paddle.moveLeft(); + break; + case 39: + paddle.moveRight(); + break; + case 27: + game.togglePause(); + break; + case 32: + game.start(); + break; + default: + break; + } + }); + document.addEventListener("keyup", (e) => { + switch (e.keyCode) { + case 37: + if (paddle.speed < 0) paddle.stop(); + break; + case 39: + if (paddle.speed > 0) paddle.stop(); + break; + default: + break; + } + }); + } +} diff --git a/BrickBreakerGame/src/levels.js b/BrickBreakerGame/src/levels.js new file mode 100755 index 0000000..4efb92b --- /dev/null +++ b/BrickBreakerGame/src/levels.js @@ -0,0 +1,31 @@ +import Brick from "./brick"; + +export function buildLevel(game, level) { + let bricks = []; + level.forEach((row, rowIndex) => { + row.forEach((element, index) => { + if (element === 1) { + let position = { + x: index * 80, + y: 100 + 25 * rowIndex + }; + bricks.push(new Brick(game, position)); + } + }); + }); + return bricks; +} + +export const level1 = [ + [0, 1, 0, 1, 1, 0, 0, 0, 1, 1], + [1, 1, 1, 1, 0, 0, 0, 1, 1, 1], + [0, 1, 0, 1, 0, 1, 1, 0, 0, 0], + [0, 1, 1, 1, 0, 0, 1, 1, 0, 0] +]; + +export const level2 = [ + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 0, 1, 0, 1, 1, 0, 0, 1], + [0, 1, 1, 1, 1, 0, 1, 1, 0, 1] +]; diff --git a/BrickBreakerGame/src/paddle.js b/BrickBreakerGame/src/paddle.js new file mode 100755 index 0000000..4288552 --- /dev/null +++ b/BrickBreakerGame/src/paddle.js @@ -0,0 +1,32 @@ +export default class Paddle { + constructor(game) { + this.width = 150; + this.height = 25; + this.maxSpeed = 5; + this.speed = 0; + this.pos = { + x: game.gameWidth / 2 - this.width / 2, + y: game.gameHeight - this.height - 10 + }; + + this.draw = (ctx) => { + ctx.fillStyle = "#0ff"; + ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height); + }; + this.moveLeft = () => { + this.speed += -this.maxSpeed; + }; + this.moveRight = () => { + this.speed += this.maxSpeed; + }; + this.stop = () => { + this.speed = 0; + }; + this.update = () => { + this.pos.x += this.speed; + if (this.pos.x < 0) this.pos.x = 0; + if (this.pos.x + this.width > game.gameWidth) + this.pos.x = game.gameWidth - this.width; + }; + } +}