HTML5 - Aliens Attack - levels


Tulista: 'x', liigu: nooleklahvid

Täiendame ja parandame varem loodud mängu Aliens Attack. Mäng on huvitavam, kui vaenlasi on mitmesuguseid - uuel tasemel ilmuvad uued animeeritud vaenlased

Tasemed

Lisame mängu initsialiseerimisel mõned uued muutujad:

	nExplosions = 40;
	nEnemies = 30;
	var level = 0;
	var maxLevels = 3; //tasemete arv
		

Animeeritud vaenlased

Tasemetel 1 ja 2 tulevad juba animeeritud vaenlased, s.t. loodud konstruktorfunktsiooniga animated; vastavad spriidilehed tuleb laadida koos teiste piltidega mängu algul. Kuna selle funktsiooni eelnev versioon oli plahvatuste, seega ühekordse animatsiooni mängimiseks - kui animatsioon jõudis lõppuni this.activeFrame == this.coords.length, seati this.state = "hidden";. Vaenlaste animatsioone tuleb mängida korduvalt, sellepärast lisame funktisoonile animated veel ühe formaalse parameetri repeat (animatsiooni mängimise kordused: 0 - uuesti, 1 - lõpetab)ja täiendame funktsiooni animated alamfunktsiooni draw:

if (this.repeat == 1){
	this.state = "hidden";  //end animation

Et kontruktorfunktsioon enemy saaks spriidi vastavalt tasemele (tase 0: animeerimata pilt, tasemetel 2,3 - animeeritud), lisame funktsiooni, mis loob vastava spriidid:

var enemySprite = function() {
		switch(level)
			{
			case 0:
			  return images.enemy;
			  break;
			case 1:
			 return new animated(images.enemy1,[[0,0],[32,0]],32,32,"active",8,0);
			 break;
		  case 2:
			return new animated(images.enemy2,[[0,0],[38,0],[76,0]],38,48,"active",8,0);
			 break;
		  default:
			//announce("Victory!");
			//clearInterval(int);
			break;
			}
		}

Funktsioonis enemy määratakse spriit ja joonistamine käskudega

this.sprite = enemySprite(); //images.enemy;
	  this.width = this.sprite.width; //32;
	  this.height = this.sprite.height; //32;
	  this.y = - this.height;
	  this.state = "hidden";
	  this.draw = function() {
	  if (this.state =="active") {
		if(level == 0)
			ctx.drawImage(this.sprite, this.x, this.y);
		else {
			this.sprite.x = this.x;
			this.sprite.y = this.y;
			this.sprite.draw();
			}
		}

Vaenlased muutuvad tigedamaks, kui neid tapetakse - lisame parameetri, mis suurendab nende allalangemise kiirust:

this.increase = 0.1;
	  this.vy = 2 + player.kills * this.increase;

Kõrgematel tasemetel parameetrit increase suurendatakse.

Vaenlaste säästlik kasutamine

Kui vaenlase spriit on animatsioon, on otstarbekaks mälu kasutamiseks parem ka vaenlasi käsitleda nagu plahvatusi - mitte luua uusi, vaid taaskasutada.

Hakkame ka vaenlasi taaskasutama, s.t. kui vaenlane ei ole aktiivne, siis teda ei uuendata (liigutata), s.t. lisame vaenlaste konstruktorfunktsioonile parameetri state; vaenlase loomisel konstruktorfunktsioonis enemy

this.state = "hidden";

Vaenlasi uuendandatakse (update) ja joonistatakse vaid siis, kui nad on aktiivsed, s.t. lisame vastavatesse funktsioonidesse kontrollid

this.update = function() {
	  if (this.state =="active") {...
		

Vaenlasi luuakse mängu funktsioonis update vajaduse korral (muidu ilmuvad nad kõik peaaegu samas reas); massiivis activeEnemies on vaenlased, kes on parajasti ekraanil, ülejäänud on massiivis enemies; sellest massiivist otsitakse 'mittemängivaid', s.t. mis pole aktiivsed analoogiliselt sellega, kuidas otsitakse mittemängivaid plahvatusi:

activeEnemies = enemies.filter(function(e) {
		return e.state = "active";
	  });
	
	  if((activeEnemies.length < nEnemies/2) && (Math.random() < 0.1) ) {
		var x1 = w0/4 + Math.random()*w0/2;
		var y1 = 0;
		if (find_free(enemies, x1, y1) == -1); { //aktiveerime juhuslikult vaenlasi kuni < 20
		var bad = new enemy();
		enemies.push(bad);
		}
	  }

Kuulidega tabamist tuleb kontrollida vaid aktiivsete vaenlaste jaoks:

activeEnemies = enemies.filter(function(e) {
		return e.state=="active";
	  });
	  	 
	  bullets.forEach(function(bullet) {
		activeEnemies.forEach(function(enemy) {
		  if(collision(bullet, enemy)) {
			enemy.explode();
			player.kills++;
			bullet.active = false;
		  }
		});
	  });

Et nii plahvatuste kui ka vaenlaste massivist vaba (mittemängiva) leidmiseks saaks kasutada sama funktsiooni, on funktsioonile find_free lisatud veel üks parameeter objects - see on massiiv, millest peab otsima:

function find_free(objects,x,y) {
		var i = 0;
		while(i < objects.length && objects[i].state == "active"){
			i++;
			}
		if (i < objects.length) { //õnnestus!
			objects[i].x = x;
			objects[i].y = y;
			objects[i].state = "active";
			objects[i].draw();
			return 0;
			}
		else
			return -1; //ei leitud
		}

Kui vaenlast tabab kuul, vaenlane plahvatab ja muutub passiivseks (osa konstruktorfunktsioonist enemy):

this.explode = function() {
		explosion_snd.cloneNode(true).play();
		find_free(explosions,this.x,this.y);
		this.y = -this.sprite.height;  //peidame ylemise ääre taha
		this.state = "hidden";
	  };

Tasemete vahetamine

if (player.health > 0) {
			if (player.kills > nEnemies) {
				level++ ; 
				if (level <= maxLevels) {
					enemies.forEach(function(e) {
						e.sprite = enemySprite(); }); //uus spriit vastavalt uuele tasemele
					enemy.increase += 0.1;
					player.kills = 0; //igal tasemel peab tapma nEnemies vaenlast!
					}
				else {
				  announce("Victory!");
				  stopGame();
				  }
				}
			}
		else  {
			announce('You are dead...');//loose game
			stopGame();
			//clearInterval(gm);
			}
		}

Mängu lõpetamine

Mäng käivitatakse korduva taimeriga:

var gm = setInterval(gameLoop,FT); 

Funktsioon stopGame eemaldab sündmuste kuulajad ja vaenlased; plahvatused mängivad lõpuni:

function stopGame(){
		removeDown();
		removeUp();
		enemies.forEach(function(enemy) {
			enemy.explode();
			});
		enemies.length = 0;
		bullets.length = 0;
	}

Funktsioonid removeDown, removeUp on kirjeldatud veidi modifitseeritud utiliidis keys.js, kus varemkasutatud anonüümsetele funktsioonidele on antud nimed, muidu ei saa sündmuste kuulajaid eemaldada:

var keysDown = [];
window.addEventListener("keydown", downKey,false);
function downKey(e) {
	keysDown[e.keyCode] = true;
	}
function removeDown() {
	window.removeEventListener("keydown", downKey,false);
	keysDown.length = 0;
	}

window.addEventListener("keyup", upKey,false);
function upKey(e) {
	keysDown[e.keyCode] = false;
	}
function removeUp() {
	window.removeEventListener("keyup", upKey,false);
	keysDown.length = 0;
	}

Ja ongi kõik... Mängija on võitnud, kui ta tapab nEnemies vaenlast (rohkem pole isegi Talibanidel kusagilt võtta)


Ülesandeid.
1. Kangelasel on mõlemas käes relv - pane ta kahe käega ( vaheldumisi) tulistama; kuulid peaks välja lendama enam-vähem) käes oleva toru otsast selle suunas !
2.Lisa mängija edukuse (kui palju vaenlasi on tapetud) ja tervise arvestus - kui vaenlane põrkab mängijaga kokku, peab selle tervis vähenema 10% ja kui tervis on 0, on mängija kaotanud; tervis näidatakse punase kriipsuga mängu vasakus nurgas
3. Kui vaenlasi tapetakse, muutuvad nad tigedamaks ja hakkavad kiiremini allapoole liikuma - täienda ! Allalangemise kiirusega saab määrata ka mängu raskusastme.

© Jaak Henno

Küsimused, probleemid : e-mail