Арканоид (JavaScript)

Материал из Department of Theoretical and Applied Mechanics
Версия от 17:29, 30 мая 2020; Verteletskaya.as (обсуждение | вклад) (Новая страница: «==Авторы== Вертелецкая Анастасия <br /> Камила Карамуллина ==Описание== Арканоид - это тип кл…»)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Авторы

Вертелецкая Анастасия
Камила Карамуллина

Описание

Арканоид - это тип классических компьютерных игр, целью в которых является уничтожение всех игровых блоков отскакивающим шариком.
Именно к этому типу видеоигр относится этот проект.
Для реализации культовой игры были задействованы языки программирования: JavaScript, HTML, CSS.
Данный проект отличается от своей классической версии - в управление были добавлены некоторые особые функции, перечисленные ниже. Также дизайн работы был вдохновлен борьбой с пандемией COVID-19.

Особенности управления

  • Подсчет очков
  • Подсчет времени
  • Возможность менять скорость таблетки, нажав "вверх" или "вниз".
  • Запуск анимации блоков при нажатии "Ctrl"

Игровой процесс


  1. На игровом поле расположены блоки-вирусы, шарик-таблетка, список игровых клавиш и платформа.
  2. Игрок начинает игру по кнопке "Space". После нажатия шарик отрывается от платформы и начинает полет.
  3. При столкновении шарика с блоком, блок-вирус пропадает и игрок зарабатывает установленное количество очков.
  4. Игра продолжается до момента попадания шарика-таблетки на нижний край игрового поля или до полного уничтожения блоков.


Далее реализуется два сценария:

  • При попадании шарика-таблетки на нижний край игрового поля выводится экран поражения. Игрок наблюдает время прохождения и количество заработанных очков.
  • При полном уничтожении блоков выводится экран победы. Таймер останавливается и игрок может наблюдать свои итоговые результаты.

Визуализация

Код игры

Код программы на языке JavaScript:
  1 const GAME_RUN= 1;
  2 const GAME_LOST= 2;
  3 const GAME_WIN= 3;
  4 
  5 const KEY_START=32;
  6 const KEY_LEFT=37;
  7 const KEY_RIGHT=39;
  8 const KEY_UP=38;
  9 const KEY_DOWN=40;
 10 const KEY_ANIM=17;
 11 
 12 var game = {
 13 	state: GAME_RUN,
 14 	width: 640,
 15 	height: 400,
 16 	score: 0,
 17 	sprites: { platform: undefined, background: undefined, block: undefined, pill: undefined, bg_lost: undefined, bg_win:undefined},
 18 	pill: {}, 
 19 	platform: {},
 20 	blocks: [],
 21 	rows: 5, // 5,
 22 	cols: 8, // 8,
 23 	startTime: null,
 24 	blockAnimFreq: 400,
 25 	needAnimBlocks: false, 
 26 	needPaddleSidesEval: false, //true= Если мячик ударился в левую половину платформы, то отразится он влево (откуда бы ни прилетел), если в правую - отразится вправо
 27 	init: function(){
 28 		this.ctx = document.getElementById("mycanvas").getContext("2d");
 29 		this.ctx.font = '16px Roboto';
 30 		this.ctx.fillStyle = '#177398';
 31 		window.addEventListener('keydown', function(e){
 32 			if ( e.keyCode == KEY_START ) {
 33 				if (game.state==GAME_RUN){
 34 					game.platform.releaseBall();
 35 				} else {
 36 					window.location.reload(); 
 37 				}
 38 		
 39 			} else if ( e.keyCode == KEY_LEFT ) {
 40 				game.platform.dx = -game.platform.velocity;
 41 			} else if ( e.keyCode == KEY_RIGHT ) {
 42 				game.platform.dx = game.platform.velocity;
 43 			} else if ( e.keyCode == KEY_UP ) {
 44 				game.pill.incSpeed();
 45 			} else if ( e.keyCode == KEY_DOWN ) {
 46 				game.pill.decSpeed();
 47 			} else if ( e.keyCode == KEY_ANIM ) {
 48 				game.animateBlocks();
 49 			}
 50 
 51 		});
 52 		window.addEventListener('keyup', function(e){
 53 			game.platform.stop();
 54 		});
 55 	},
 56 	preload: function(){
 57 		for (var key in this.sprites) {
 58 			this.sprites[key] = new Image();
 59 			this.sprites[key].src = 'images/' + key + '.png';
 60 		}
 61 	},
 62 	create: function(){
 63 		for ( var row = 0; row < this.rows; row++ ) {
 64 			for ( var col = 0; col < this.cols; col++ ) {
 65 				this.blocks.push({
 66 					x: 68 * col + 50,
 67 					y: 36 * row + 35,
 68 					width: 64,
 69 					height: 32,
 70 					frame: getRandomInt(0, 3), 
 71 					isAlive: true
 72 				});
 73 			}
 74 		}
 75 	},
 76 	update: function() {
 77 		if ( this.pill.collide(this.platform) ) {
 78 			this.pill.bumpPuddle(this.platform);
 79 		}
 80 
 81 		if( this.pill.dx || this.pill.dy ) {
 82 			this.pill.move();
 83 		}
 84 
 85 		if( this.platform.dx ) {
 86 			this.platform.move();
 87 		}
 88 
 89 		this.blocks.forEach(function(element, index){
 90 			if ( element.isAlive ) {
 91 				if( this.pill.collide(element) ) {
 92 					this.pill.bumpBlock(element);
 93 				}
 94 			}
 95 		}, this);
 96 
 97 		this.pill.checkBounds();
 98 	},
 99 	render: function() {
100 		this.ctx.clearRect(0, 0, this.width, this.height);
101 		this.ctx.drawImage(this.sprites.background, 0, 0);
102 		this.ctx.drawImage(this.sprites.pill, this.pill.width * this.pill.frame, 0, this.pill.width, this.pill.height, this.pill.x, this.pill.y, this.pill.width, this.pill.height);
103 		this.ctx.drawImage(this.sprites.platform, this.platform.x, this.platform.y);
104 
105 		this.blocks.forEach(function(element){
106 			if ( element.isAlive ) {
107 				this.ctx.drawImage(this.sprites.block, element.width * element.frame, 0, element.width, element.height, element.x, element.y, element.width, element.height);
108 
109 			}
110 		}, this);
111 
112 		this.drawCounters();
113 
114 	},
115 
116 
117 	run: function() {
118 		switch (this.state){
119 			case GAME_RUN:
120 				this.update();
121 				this.render();
122 				window.requestAnimationFrame(function(){
123 					game.run();
124 				});				
125 				break;
126 			case GAME_LOST:
127 				this.showLostScreen();
128 				break;
129 			case GAME_WIN:
130 				this.showWinScreen();
131 				break;
132 		}
133 	},
134 
135 	start: function(){
136 		this.init();
137 		this.preload();
138 		this.create();
139 		this.run();
140 	},
141 
142 	startTimer: function(){
143 		this.startTime = new Date();
144 	},
145 	
146 	animateBlocks: function (){
147 		if (!this.needAnimBlocks){
148 			this.needAnimBlocks=true;
149 			this.blocks.forEach(function(element, index){
150 				if ( element.isAlive ) {
151 					element.animation = setInterval(function(){
152 						++element.frame;
153 						if ( element.frame > 2 ) {
154 							element.frame = 0;
155 						}
156 					}, this.blockAnimFreq);
157 				}
158 			}, this);
159 		} else {
160 			this.needAnimBlocks=false;
161 			this.blocks.forEach(function(element, index){
162 				if ( element.isAlive ) {
163 					clearInterval(element.animation);
164 				}
165 			}, this);
166 		}
167 	},
168 
169 	drawCounters(){
170 		this.ctx.fillText('Score: ' + this.score, 15, this.height - 20);
171 		this.ctx.fillText('Speed: ' + this.pill.velocity, 15, this.height - 5);
172 		this.ctx.fillText('Time: ' + getTimeStr(game.startTime), 530, this.height - 5);
173 	},
174 
175 	showWinScreen(){
176 		this.ctx.clearRect(0, 0, this.width, this.height);
177 		this.ctx.drawImage(this.sprites.bg_win, 0, 0);
178 
179 	
180 		this.drawCounters();
181 		
182 	},
183 	showLostScreen(){
184 		this.ctx.clearRect(0, 0, this.width, this.height);
185 		this.ctx.drawImage(this.sprites.bg_lost, 0, 0);
186 
187 		
188 		this.drawCounters();
189 	}
190 
191 };
192 
193 
194 game.pill = {
195 	frame: 0,
196 	x: 310,
197 	y: game.height-72, //288,
198 	velocity: 3,
199 	vel_step: 1,
200 	dx: 0,
201 	dy: 0,
202 	width: 22,
203 	height: 22,
204 	move: function(){
205 		this.x += this.dx;
206 		this.y += this.dy;
207 	},
208 	animate: function(){
209 		var self = this;
210 		this.animation = setInterval(function(){
211 			++self.frame;
212 			if ( self.frame > 3 ) {
213 				self.frame = 0;
214 			}
215 		}, 100);
216 	},
217 	jump: function(){
218 		this.dy = this.dx = -this.velocity;
219 		this.animate();
220 	},
221 	collide: function(element) {
222 		var x = this.x + this.dx;
223 		var y = this.y + this.dy;
224 
225 		if ( x + this.width > element.x &&
226 			x < element.x + element.width &&
227 			y + this.height > element.y &&
228 			y < element.y + element.height) {
229 			return true;
230 		}
231 		return false;
232 	},
233 	onTheLeftSide: function(platform) {
234 		return (this.x + this.width / 2) < (platform.x + platform.width / 2);
235 	},
236 	checkBounds: function(){
237 		var x = this.x + this.dx;
238 		var y = this.y + this.dy;
239 
240 		if ( x < 0  ) {
241 			this.x = 0;
242 			this.dx = this.velocity;
243 		} else if ( x + this.width > game.width ) {
244 			this.x = game.width - this.width;
245 			this.dx = -this.velocity;
246 		} else if ( y < 0 ) {
247 			this.y = 0;
248 			this.dy = this.velocity;
249 		} else if( this.y + this.height >= game.height ) {
250 			this.over(GAME_LOST);
251 		}
252 	},
253 	bumpPuddle: function(platform){
254 		this.dy = -this.velocity;
255 		if (game.needPaddleSidesEval){
256 			this.dx = this.onTheLeftSide(platform) ? -this.velocity : this.velocity;
257 		}
258 	},
259 	bumpBlock: function(block){
260 		block.isAlive = false;
261 		this.dy *= -1;
262 		++game.score;
263 		if ( game.score == game.blocks.length ) {
264 			this.over(GAME_WIN);
265 		}
266 	},
267 	over: function(state) {
268 		game.state = state;
269 	},
270 	incSpeed: function() {
271 		this.velocity=this.velocity + this.vel_step;
272 		if (this.dx!=0) {this.dx=this.dx > 0 ? this.dx+this.vel_step : this.dx-this.vel_step;}
273 		if (this.dy!=0) {this.dy=this.dy > 0 ? this.dy+this.vel_step : this.dy-this.vel_step;}
274 	},
275 	decSpeed: function() {
276 		if (this.velocity>this.vel_step){
277 			this.velocity=this.velocity- this.vel_step;
278 			if (this.dx!=0) {this.dx=this.dx > 0 ? this.dx-this.vel_step : this.dx+this.vel_step;}
279 			if (this.dy!=0) {this.dy=this.dy > 0 ? this.dy-this.vel_step : this.dy+this.vel_step;}
280 		}
281 	}
282 };
283 
284 
285 game.platform = {
286 	x: 270,
287 	y: game.height-50, //310,
288 	dx: 0,
289 	velocity: 6,
290 	width: 104,
291 	height: 12,
292 	pill: game.pill,
293 	stop: function(){
294 		this.dx = 0;
295 
296 		if ( this.pill ) {
297 			this.pill.dx = 0;
298 		}
299 	},
300 	move: function(){
301 		var stop_pill=false;
302 		this.x += this.dx;
303 		if ( this.x < 0  ) {
304 			this.x = 0; stop_pill=true;
305 		} else if ( this.x + this.width > game.width ) {
306 			this.x = game.width - this.width;
307 			stop_pill=true;
308 		}
309 
310 		if ( this.pill && !stop_pill ) {
311 			this.pill.x += this.dx;
312 		}
313 	},
314 	releaseBall: function(){
315 		if ( this.pill ) {
316 			game.startTimer();
317 			this.pill.jump();
318 			this.pill = false;
319 		}
320 	}
321 };
322 
323 
324 function getRandomInt(min, max) {
325 	return Math.floor(Math.random() * (max - min)) + min;
326 }
327 
328 function getTimeStr(startDate) {
329 	var out="0:00:00";
330 	if (startDate) {
331 	 	var dateObj = new Date();
332 		var time=dateObj.getTime()-startDate.getTime();
333 
334 		var seconds = Math.floor(time / 1000);
335 		var minutes = Math.floor(time / (1000 * 60));
336 		var hours = Math.floor(time / (1000 * 60 * 60));
337 
338 		if (minutes < 10) minutes = "0" + minutes;
339 		if (seconds < 10) seconds = "0" + seconds;
340 		out = hours + ":" + minutes + ":" + seconds;
341 	}
342 	return out;
343 }
344 
345 
346 window.addEventListener("load",function() {
347 	game.start();
348 });
Код программы на языке HTML:
 1 <html>
 2 <head>
 3 <title>Kill the virus</title>
 4 <link rel="icon" href="images/ico.png">
 5 <link rel="stylesheet" type="text/css" href="style.css" >
 6 </head>
 7 <body>
 8 <center>
 9 <br/>
10 <img src="images/game.png">
11 <table width=500>
12 <tr><td><font color=red>Platform:</font></td><td><font color=red>Pill Speed:</font></td><td><font color=red>Animation:</font></td></tr>
13 <tr><td>Arrow LEFT, Arrow RIGHT</td><td>Arrow UP, Arrow DOWN</td><td>CTRL</td></tr>
14 </table>
15 
16 <h3>Press SPACE to START</h3>
17 <canvas id="mycanvas" width="640" height="400" ></canvas>
18 </center>
19 <script src="game.js" type="text/javascript"></script>
20 </body>
21 </html>
Код программы на языке CSS:
 1 body {
 2 	background:white;
 3 	padding: 0;
 4 	margin: 0;
 5 }
 6 canvas {
 7 	//width: 100%;
 8 }
 9 h3 {
10 	color: #9d0c15; // white;
11 	font-family: "Times New Roman", Georgia, Serif";
12 	line-height: 0.5em;
13 	font-size: 1em;
14 }
15 table {
16 	color: #9d0c15;
17 	font-family: "Times New Roman", Georgia, Serif";
18 	line-height: 0.5em;
19 	font-size: 0.9em;
20 }