├── README.md ├── game.html └── js └── arkanoid.js /README.md: -------------------------------------------------------------------------------- 1 | arkanoid-js 2 | =========== 3 | 4 | JavaScript version of arkanoid game based on html canvas 5 | 6 | ![ScreenShot](http://1.bp.blogspot.com/-1N5994piu1c/UkxyJ4XMjJI/AAAAAAAAAVQ/_TdGJLKofUg/s1600/arkanoid_game.png "Variant of game level colors") 7 | -------------------------------------------------------------------------------- /game.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Arkanoid Game 4 | 5 | 11 | 12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /js/arkanoid.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------------------------------------- 2 | // Arkanoid game 3 | // 4 | // Author: delimitry 5 | //----------------------------------------------------------------------------------------------------------- 6 | 7 | function Paddle(x, y, width, height) { 8 | this.x = x; 9 | this.y = y; 10 | this.width = width; 11 | this.height = height; 12 | }; 13 | 14 | var BallDirs = { 15 | NONE : 0, 16 | LEFT : 1, 17 | RIGHT : 2, 18 | UP : 4, 19 | DOWN : 8 20 | }; 21 | 22 | function Ball(x, y, radius, dir, speed) { 23 | this.x = x; 24 | this.y = y; 25 | this.radius = radius; 26 | this.dir = BallDirs.NONE; 27 | this.speed = speed; 28 | } 29 | 30 | var BricksTypes = { 31 | DEFAULT : 1, 32 | ICE : 1, 33 | WOOD : 2, 34 | STONE : 3, 35 | IRON : 4, 36 | STEEL : 5 37 | }; 38 | 39 | function Brick(x, y, width, height, type) { 40 | this.x = x; 41 | this.y = y; 42 | this.width = width; 43 | this.height = height; 44 | this.lifes = type; 45 | } 46 | 47 | function Bricks(hor_num, vert_num, brick_width, brick_height) { 48 | this.bricks = new Array(); 49 | for (var i = 0; i < vert_num; i++) { 50 | this.bricks[i] = new Array(); 51 | for (var j = 0; j < hor_num; j++) { 52 | this.bricks[i][j] = new Brick(j * brick_width, i * brick_height, brick_width, brick_height, BricksTypes.DEFAULT); 53 | } 54 | } 55 | } 56 | 57 | //----------------------------------------------------------------------------------------------------------- 58 | // Arkanoid Game class 59 | //----------------------------------------------------------------------------------------------------------- 60 | function ArkanoidGame(canvas, context) { 61 | 62 | var PADDLE_WIDTH = 60; 63 | var PADDLE_HEIGHT = 10; 64 | var PADDLE_SPEED = 1; 65 | var BALL_RADIUS = 3; 66 | var BALL_DEFAULT_SPEED = 3; 67 | var BALL_MAX_SPEED = 6; 68 | var BRICK_WIDTH = 80; 69 | var BRICK_HEIGHT = 35; 70 | var BRICK_SCORE = 100; 71 | 72 | this.level = 0; 73 | this.lifes = 3; 74 | this.score = 0; 75 | this.paddle = new Paddle(canvas.width / 2 - PADDLE_WIDTH / 2, canvas.height - 20, PADDLE_WIDTH, PADDLE_HEIGHT); 76 | this.ball = new Ball(canvas.width / 2, canvas.height / 2, BALL_RADIUS, BallDirs.NONE, BALL_DEFAULT_SPEED); 77 | this.gameOver = false; 78 | this.gameWin = false; 79 | this.gamePaused = false; 80 | this.bricks = new Bricks(8, 2, BRICK_WIDTH, BRICK_HEIGHT); 81 | 82 | this.init = function() { 83 | this.level = 0; 84 | this.lifes = 3; 85 | this.score = 0; 86 | this.gameOver = false; 87 | this.gameWin = false; 88 | this.gamePaused = false; 89 | this.ball.dir = BallDirs.NONE; 90 | this.initLevel(this.level); 91 | } 92 | 93 | this.initLevel = function(level) { 94 | switch (level) { 95 | case 0: 96 | this.bricks = new Bricks(8, 2, BRICK_WIDTH, BRICK_HEIGHT); 97 | for (var i = 0; i < this.bricks.bricks.length; i++) { 98 | for (var j = 0; j < this.bricks.bricks[i].length; j++) { 99 | this.bricks.bricks[i][j].lifes = BricksTypes.DEFAULT; 100 | } 101 | } 102 | break; 103 | 104 | case 1: 105 | this.bricks = new Bricks(8, 2, BRICK_WIDTH, BRICK_HEIGHT); 106 | for (var i = 0; i < this.bricks.bricks.length; i++) { 107 | for (var j = 0; j < this.bricks.bricks[i].length; j++) { 108 | this.bricks.bricks[i][j].lifes = BricksTypes.DEFAULT + i; 109 | } 110 | } 111 | break; 112 | 113 | case 2: 114 | this.bricks = new Bricks(8, 4, BRICK_WIDTH, BRICK_HEIGHT); 115 | for (var i = 0; i < this.bricks.bricks.length; i++) { 116 | for (var j = 0; j < this.bricks.bricks[i].length; j++) { 117 | this.bricks.bricks[i][j].lifes = BricksTypes.DEFAULT + i; 118 | } 119 | } 120 | break; 121 | 122 | case 3: 123 | this.bricks = new Bricks(8, 5, BRICK_WIDTH, BRICK_HEIGHT); 124 | for (var i = 0; i < this.bricks.bricks.length; i++) { 125 | for (var j = 0; j < this.bricks.bricks[i].length; j++) { 126 | this.bricks.bricks[i][j].lifes = BricksTypes.DEFAULT + i; 127 | } 128 | } 129 | break; 130 | 131 | default: 132 | break; 133 | } 134 | } 135 | 136 | this.drawBall = function() { 137 | context.beginPath(); 138 | context.arc(this.ball.x, this.ball.y, this.ball.radius, 0, 2 * Math.PI, false); 139 | context.fillStyle = 'yellow'; 140 | context.fill(); 141 | } 142 | 143 | this.drawBricks = function() { 144 | for (var i = 0; i < this.bricks.bricks.length; i++) { 145 | for (var j = 0; j < this.bricks.bricks[i].length; j++) { 146 | if (this.bricks.bricks[i][j].lifes > 0) { 147 | switch (this.bricks.bricks[i][j].lifes) { 148 | case BricksTypes.ICE: context.fillStyle = 'rgba(220,220,255,0.9)'; break; 149 | case BricksTypes.WOOD: context.fillStyle = 'rgb(245,155,25)'; break; 150 | case BricksTypes.STONE: context.fillStyle = 'rgb(55,55,55)'; break; 151 | case BricksTypes.IRON: context.fillStyle = 'rgb(25,25,85)'; break; 152 | case BricksTypes.STEEL: context.fillStyle = 'rgb(15,225,255)'; break; 153 | case BricksTypes.DEFAULT: context.fillStyle = 'rgba(220,220,255,0.9)'; break; 154 | default: context.fillStyle = 'rgb(255,0,0)'; 155 | } 156 | context.fillRect(this.bricks.bricks[i][j].x, this.bricks.bricks[i][j].y, this.bricks.bricks[i][j].width - 2, this.bricks.bricks[i][j].height - 2); 157 | } 158 | } 159 | } 160 | } 161 | 162 | this.draw = function() { 163 | context.fillStyle = 'rgb(0,10,0)'; 164 | context.fillRect(0, 0, canvas.width, canvas.height); 165 | 166 | this.drawBall(); 167 | 168 | // draw paddle 169 | context.fillStyle = 'rgb(155,110,5)'; 170 | context.fillRect(this.paddle.x, this.paddle.y, this.paddle.width, this.paddle.height); 171 | 172 | this.drawBricks(); 173 | 174 | if (this.gamePaused && !this.gameWin && !this.gameOver) { 175 | context.fillStyle = 'rgb(255,255,0)'; 176 | context.font = 'bold 20px Arial'; 177 | context.fillText('Pause', canvas.width / 2 - 30, canvas.height / 2); 178 | } 179 | 180 | if (this.gameOver) { 181 | context.fillStyle = 'rgb(255,255,0)'; 182 | context.font = 'bold 20px Arial'; 183 | context.fillText('Game Over', canvas.width / 2 - 40, canvas.height / 2); 184 | } 185 | 186 | if (this.gameWin) { 187 | context.fillStyle = 'rgb(255,255,0)'; 188 | context.font = 'bold 20px Arial'; 189 | context.fillText('You Win', canvas.width / 2 - 40, canvas.height / 2); 190 | } 191 | 192 | context.fillStyle = 'rgb(255,255,220)'; 193 | context.font = 'bold 15px Arial'; 194 | context.fillText('Score: ' + this.score, 5, 15); 195 | 196 | context.fillStyle = 'rgb(255,255,220)'; 197 | context.font = 'bold 15px Arial'; 198 | context.fillText('Lifes: ' + this.lifes, 5, 35); 199 | } 200 | 201 | this.update = function() { 202 | if (this.gamePaused || this.gameWin || this.gameOver) return; 203 | 204 | // update ball pos 205 | if (this.ball.dir & BallDirs.RIGHT) this.ball.x += this.ball.speed; 206 | else if (this.ball.dir & BallDirs.LEFT) this.ball.x -= this.ball.speed; 207 | if (this.ball.dir & BallDirs.UP) this.ball.y -= this.ball.speed; 208 | else if (this.ball.dir & BallDirs.DOWN) this.ball.y += this.ball.speed; 209 | 210 | // ball bounce from paddle 211 | if ((this.ball.x + this.ball.radius > this.paddle.x && this.ball.x - this.ball.radius < this.paddle.x + this.paddle.width) && 212 | (this.ball.y + this.ball.radius > this.paddle.y)) { 213 | if (this.ball.speed < BALL_MAX_SPEED) this.ball.speed += 0.5; 214 | if (this.ball.dir & BallDirs.DOWN) { 215 | this.ball.dir = this.ball.dir - BallDirs.DOWN + BallDirs.UP; 216 | } else if (this.ball.dir & BallDirs.UP) { 217 | this.ball.dir = this.ball.dir - BallDirs.UP + BallDirs.DOWN; 218 | } 219 | } 220 | 221 | // update ball 222 | if (this.ball.x - this.ball.radius < 0) { 223 | this.ball.x = this.ball.radius; 224 | this.ball.dir = this.ball.dir - BallDirs.LEFT + BallDirs.RIGHT; 225 | } 226 | if (this.ball.x + this.ball.radius > canvas.width) { 227 | this.ball.x = canvas.width - this.ball.radius; 228 | this.ball.dir = this.ball.dir - BallDirs.RIGHT + BallDirs.LEFT; 229 | } 230 | if (this.ball.y - this.ball.radius < 0) { 231 | this.ball.y = this.ball.radius; 232 | this.ball.dir = this.ball.dir - BallDirs.UP + BallDirs.DOWN; 233 | } 234 | 235 | if (this.ball.y + this.ball.radius > canvas.height) { 236 | // lost one life 237 | this.lifes--; 238 | this.ball.speed = BALL_DEFAULT_SPEED; 239 | if (this.lifes == 0) { 240 | this.gameOver = true; 241 | } else { 242 | this.ball.x = canvas.width / 2; 243 | this.ball.y = canvas.height / 2; 244 | this.ball.dir = BallDirs.NONE; 245 | } 246 | } 247 | 248 | if (this.ball.dir == BallDirs.NONE) { 249 | this.ball.x = this.paddle.x + this.paddle.width / 2; 250 | this.ball.y = this.paddle.y - this.ball.radius * 2; 251 | } 252 | 253 | // bounces 254 | for (var i = 0; i < this.bricks.bricks.length; i++) { 255 | for (var j = 0; j < this.bricks.bricks[i].length; j++) { 256 | if (this.bricks.bricks[i][j].lifes > 0) { 257 | if (this.ball.dir == BallDirs.LEFT + BallDirs.UP) { 258 | if (this.isPointInRect(this.ball.x - this.ball.speed, this.ball.y - 0, this.bricks.bricks[i][j].x, this.bricks.bricks[i][j].y, this.bricks.bricks[i][j].width, this.bricks.bricks[i][j].height)) { 259 | this.ball.x = this.bricks.bricks[i][j].x + this.bricks.bricks[i][j].width + this.ball.speed; 260 | this.ball.dir = this.ball.dir - BallDirs.LEFT + BallDirs.RIGHT; 261 | this.bricks.bricks[i][j].lifes--; 262 | this.score += BRICK_SCORE; 263 | return; 264 | } 265 | if (this.isPointInRect(this.ball.x - 0, this.ball.y - this.ball.speed, this.bricks.bricks[i][j].x, this.bricks.bricks[i][j].y, this.bricks.bricks[i][j].width, this.bricks.bricks[i][j].height)) { 266 | this.ball.y = this.bricks.bricks[i][j].y + this.bricks.bricks[i][j].height + this.ball.speed; 267 | this.ball.dir = this.ball.dir - BallDirs.UP + BallDirs.DOWN; 268 | this.bricks.bricks[i][j].lifes--; 269 | this.score += BRICK_SCORE; 270 | return; 271 | } 272 | } else if (this.ball.dir == BallDirs.LEFT + BallDirs.DOWN) { 273 | if (this.isPointInRect(this.ball.x - this.ball.speed, this.ball.y + 0, this.bricks.bricks[i][j].x, this.bricks.bricks[i][j].y, this.bricks.bricks[i][j].width, this.bricks.bricks[i][j].height)) { 274 | this.ball.x = this.bricks.bricks[i][j].x + this.bricks.bricks[i][j].width + this.ball.speed; 275 | this.ball.dir = this.ball.dir - BallDirs.LEFT + BallDirs.RIGHT; 276 | this.bricks.bricks[i][j].lifes--; 277 | this.score += BRICK_SCORE; 278 | return; 279 | } 280 | if (this.isPointInRect(this.ball.x - 0, this.ball.y + this.ball.speed, this.bricks.bricks[i][j].x, this.bricks.bricks[i][j].y, this.bricks.bricks[i][j].width, this.bricks.bricks[i][j].height)) { 281 | this.ball.y = this.bricks.bricks[i][j].y - this.ball.speed; 282 | this.ball.dir = this.ball.dir - BallDirs.DOWN + BallDirs.UP; 283 | this.bricks.bricks[i][j].lifes--; 284 | this.score += BRICK_SCORE; 285 | return; 286 | } 287 | } else if (this.ball.dir == BallDirs.RIGHT + BallDirs.UP) { 288 | if (this.isPointInRect(this.ball.x + this.ball.speed, this.ball.y - 0, this.bricks.bricks[i][j].x, this.bricks.bricks[i][j].y, this.bricks.bricks[i][j].width, this.bricks.bricks[i][j].height)) { 289 | this.ball.x = this.bricks.bricks[i][j].x - this.ball.speed; 290 | this.ball.dir = this.ball.dir - BallDirs.RIGHT + BallDirs.LEFT; 291 | this.bricks.bricks[i][j].lifes--; 292 | this.score += BRICK_SCORE; 293 | return; 294 | } 295 | if (this.isPointInRect(this.ball.x + 0, this.ball.y - this.ball.speed, this.bricks.bricks[i][j].x, this.bricks.bricks[i][j].y, this.bricks.bricks[i][j].width, this.bricks.bricks[i][j].height)) { 296 | this.ball.y = this.bricks.bricks[i][j].y + this.bricks.bricks[i][j].height + this.ball.speed; 297 | this.ball.dir = this.ball.dir - BallDirs.UP + BallDirs.DOWN; 298 | this.bricks.bricks[i][j].lifes--; 299 | this.score += BRICK_SCORE; 300 | return; 301 | } 302 | } else if (this.ball.dir == BallDirs.RIGHT + BallDirs.DOWN) { 303 | if (this.isPointInRect(this.ball.x + this.ball.speed, this.ball.y + 0, this.bricks.bricks[i][j].x, this.bricks.bricks[i][j].y, this.bricks.bricks[i][j].width, this.bricks.bricks[i][j].height)) { 304 | this.ball.x = this.bricks.bricks[i][j].x - this.ball.speed; 305 | this.ball.dir = this.ball.dir - BallDirs.RIGHT + BallDirs.LEFT; 306 | this.bricks.bricks[i][j].lifes--; 307 | this.score += BRICK_SCORE; 308 | return; 309 | } 310 | if (this.isPointInRect(this.ball.x + 0, this.ball.y + this.ball.speed, this.bricks.bricks[i][j].x, this.bricks.bricks[i][j].y, this.bricks.bricks[i][j].width, this.bricks.bricks[i][j].height)) { 311 | this.ball.y = this.bricks.bricks[i][j].y - this.ball.speed; 312 | this.ball.dir = this.ball.dir - BallDirs.DOWN + BallDirs.UP; 313 | this.bricks.bricks[i][j].lifes--; 314 | this.score += BRICK_SCORE; 315 | return; 316 | } 317 | } 318 | } 319 | } 320 | } 321 | 322 | full_level_life = 0; 323 | for (var i = 0; i < this.bricks.bricks.length; i++) { 324 | for (var j = 0; j < this.bricks.bricks[i].length; j++) { 325 | full_level_life += this.bricks.bricks[i][j].lifes 326 | } 327 | } 328 | 329 | if (full_level_life == 0) { 330 | if (this.level < 3) { 331 | this.ball.dir = BallDirs.NONE; 332 | this.level++; 333 | this.initLevel(this.level); 334 | } else this.gameWin = true; 335 | } 336 | } 337 | 338 | 339 | this.isPointInRect = function(x, y, rect_x, rect_y, rect_width, rect_height) { 340 | if ((x > rect_x && x < rect_x + rect_width) && 341 | (y > rect_y && y < rect_y + rect_height)) 342 | return true; 343 | return false; 344 | } 345 | 346 | this.isBallIntersectBrick = function(i, j) { 347 | if ((this.ball.x + this.ball.radius > this.bricks.bricks[i][j].x && 348 | this.ball.x - this.ball.radius < this.bricks.bricks[i][j].x + this.bricks.bricks[i][j].width) && 349 | (this.ball.y + this.ball.radius > this.bricks.bricks[i][j].y && 350 | this.ball.y - this.ball.radius < this.bricks.bricks[i][j].y + this.bricks.bricks[i][j].height)) 351 | return true; 352 | return false; 353 | } 354 | 355 | this.render = function() { 356 | context.clearRect(0, 0, canvas.width, canvas.height); 357 | this.update(); 358 | this.draw(); 359 | } 360 | 361 | this.togglePause = function() { 362 | this.gamePaused = !this.gamePaused; 363 | } 364 | 365 | this.movePaddleLeft = function() { 366 | if (this.paddle.x > 0) 367 | this.paddle.x -= 10 * PADDLE_SPEED; 368 | } 369 | 370 | this.movePaddleRight = function() { 371 | if (this.paddle.x < canvas.width - this.paddle.width) 372 | this.paddle.x += 10 * PADDLE_SPEED; 373 | } 374 | 375 | this.setPaddlePos = function(x) { 376 | if (this.gamePaused || this.gameWin || this.gameOver) return; 377 | if (x < 0) x = 0; 378 | if (x > canvas.width - this.paddle.width) x = canvas.width - this.paddle.width; 379 | this.paddle.x = x; 380 | } 381 | 382 | this.startGame = function() { 383 | if (this.gamePaused) return; 384 | if (this.ball.dir == BallDirs.NONE) { 385 | this.ball.dir = BallDirs.RIGHT + BallDirs.UP; 386 | } 387 | } 388 | }; 389 | 390 | 391 | function getRandomRange(min, max) { 392 | return Math.random() * (max - min + 1) + min; 393 | } 394 | 395 | var arkanoidGame; 396 | 397 | function render() { 398 | arkanoidGame.render(); 399 | } 400 | 401 | function checkCanvasIsSupported() { 402 | canvas = document.getElementById("gameCanvas"); 403 | canvas.width = 640; 404 | canvas.height = 480; 405 | canvas.style.cursor = "none"; 406 | if (canvas.getContext) { 407 | context = canvas.getContext('2d'); 408 | 409 | arkanoidGame = new ArkanoidGame(canvas, context); 410 | arkanoidGame.init(); 411 | 412 | setInterval(render, 1000 / 60); 413 | } else { 414 | alert("Sorry, but your browser doesn't support a canvas."); 415 | } 416 | } 417 | 418 | var KeyCodes = { 419 | SPACE : 32 420 | }; 421 | 422 | document.onkeydown = function(event) { 423 | var keyCode; 424 | if (event == null) { 425 | keyCode = window.event.keyCode; 426 | } else { 427 | keyCode = event.keyCode; 428 | } 429 | switch (keyCode) { 430 | case KeyCodes.SPACE: 431 | arkanoidGame.togglePause(); 432 | break; 433 | default: 434 | break; 435 | } 436 | } 437 | 438 | document.onmousemove = function(event) { 439 | arkanoidGame.setPaddlePos(event.pageX); 440 | } 441 | 442 | document.ontouchmove = function(event) { 443 | arkanoidGame.setPaddlePos(event.touches[0].clientX); 444 | } 445 | 446 | document.onclick = function(){ 447 | arkanoidGame.startGame(); 448 | } 449 | --------------------------------------------------------------------------------