├── 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 | 
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 |
--------------------------------------------------------------------------------