Kokkupõrgete arvutamine pikseldatat kasutades
Selles loengus esitatakse täpsemaid meetodeid kokkupõrgete arvutamiseks :
Võrdle AS3 vastavate meetoditega!

Kõige täpsema (pixel-perfect) kokkupõrgete kontrollis kasutatakse (tavaliselt suurema) objekti piksleid.

Pikslid

Piksel(raster)kujutised salvestatakse formaadis:

Html5-s saab kanvaale joonistatud pildi pikslimassiivi käsuga getImageData(x,y,w,h), kus x,y on kanvaalt loetava ala vasak ülanurk, w,h - selle ala laius ja körgus. Käsu tulemusena luuakse andmestruktuur, millel on omadused .width (=w), .height (= h) ja .data - see on lineaarne massiiv imageData pikkusega 4 x w x h, kus pildi iga piksel p on salvestatud nelja arvuga: p.red, p.green, p.blue,p.alpha, seega (pildi koordinaatides!) pikslile p(x,y) vastavad arvud

imageDatap.red = imageData[4*(x + w * y)]
p.green = imageData[4*(x + w * y)+1] 
p.blue = imageData[4*(x + w * y)+2] 
p.alpha = imageData[4*(x + w * y)+3]
Kui kujutis (suurem pilt) on läbipaistva taustaga, on kõige lihtsam on kasutada viimast, läbipaistvuse (alpha) kanalit - kui see on null, siis seal pole midagi aga kui ekraani punktis p(x,y).alpha > 0, siis on selles punktis värv, s.t. kujutis.

Objekti keskpunkt lõikub teise objekti pikslitega

This browser does not understand canvas!
Keskpunkt pikslitega Piirdenelinurk pikslitega

Kui näiteks on tarvis kontrollida (väikese) linnu (s.t. seda võib lähendada piirdenelinurga või selle keskpunktiga) kokkupõrget puuga (suur mittekumer objekt), võib (virtuaalsele) kanvaale luua/joonistada suure objekti, siis lugeda käsuga getImageData selle piklite massiiv ja siis kontrollida väikese objekti keskpunkti lõikumist suure objekti pikslite massiiviga:

 
function collision3(obj1,obj2){
	//imageData with center
	var x = Math.floor(obj2.x-Obj1.x+(obj2.w)/2);
	var y = Math.floor(obj2.y-Obj1.y+(obj2.h)/2);
	var p = 4*(x+Obj1.w*y)+3;
	return Obj1.data[p]>0;	}

Esitatud koodis esimese (suure) objekti koordinaatide Obj1.x, Obj1.y lahutamene on vajalik obj2 (väike) nihutamiseks obj1-ga samasse (suhtelisse) punkti - sama lahutamine nihutaks obj1 koordinaatide algusesse!


Piirdenelinurga lõikumine pikslitega

Märksa varem teatab objektide lõikumisest meetod, mis kontrollib objektide piirdenelinurga lõikumist teise objekti pikslitega:

function collision4(obj1,obj2){
	//imageData with bounding Box!
	var x1 = obj2.x-Obj1.x;
	var x2 = obj2.x+obj2.w-Obj1.x;
	var y1 = obj2.y-Obj1.y;
	var y2 = obj2.y+obj2.h-Obj1.y;
	var p1 = 4*(x1+Obj1.w*y1)+3;
	var p2 = 4*(x1+Obj1.w*y2)+3;
	var p3 = 4*(x2+Obj1.w*y1)+3;
	var p4 = 4*(x2+Obj1.w*y2)+3;
	return Obj1.data[p1]>0 || Obj1.data[p2]>0 || Obj1.data[p3]>0 || Obj1.data[p4]>0; }

Liikumise peatamine

collisionPikseldata ja teiste kokkupõrke arvutamise meetodite (ruudustik) puhul kõige lihtsam liikumise kontrolli viis on kontrollida liikuva objekti uut asukohta, s.t. seda, kuhu ta järgmises kaadris jõuaks. Kui seal on kokkupõrge, objekti enam ei liigutata. Kui objekti kiirus on suurem kui 1 px/fr (pixel/frame - pikslit kaadris), võib liikuv objekt peatuda enne takistuseni jõudmist, kuid sageli (näiteks hüpelt maandumisel - Mario!) peab liikuv objekt peatuma täpselt vastu takistust. Selle saavutamiseks peab pärast takistuse avastamist liigutama objekti ühe piksli haaval kuni see on vastu takistust.

Takistuseni jõudmist võib (tavaliselt) kontrollida liikuva objekti piirdenelinurga abil. Loomulikult pole tarvis kontrollida piirdenelinurga kõiki tippe, vaid ainult neid, mis on liikumise suunas. Kui näiteks objekt liigub paremale-üles, s.t. selle kiiruse komponendid on vx > 0, vy < 0, tuleb kontrollida tippe (x,y), (x+w,y), (x+w,y+h) (vt. joonisel). Objekti asendit muudetakse pikslihaaval vertikaal- horisontaalsuunas (koordinaadid peavad olema täisarvud), muutes enne selles suunas, milles muutus on suurem, s.t. kui Math.abs(vx)>Math.abs(vy), siis tuleb enne muuta horisontaalsuunas. Järgnevas ongi sellise kaardi (map) põhjal täpse liikumise peatamise skript - liikuv objekt peatub täpselt takistusega ruuduni jõudmisel; vaid paari rea muutmisega saab sellest ka pikseldatal põhineva peatamise.

function check_collision1(vx,vy,obj1){

	var x = obj1.x;
	var y = obj1.y;
	var wb = obj1.width;		
	var hb = obj1.height;
	
	var points1;
	
	if (vx < 0) {
		if (vy == 0)
			points1 = [[x,y],[x,y+hb]]; 
		else if (vy > 0)
			points1 = [[x,y],[x,y+hb],[x+wb,y+hb]]; 
		else if (vy < 0)
			points1 = [[x+wb,y],[x,y],[x,y+hb]]; 
			};
	if (vx == 0) {
		if (vy > 0)
			points1 = [[x,y+hb],[x+wb,y+hb]]; 
		else if (vy < 0)
			points1 = [[x+wb,y],[x,y]];
			};
	if (vx > 0) {
		if (vy == 0)
			points1 = [[x+wb,y+hb],[x+wb,y]]; 
		else if (vy > 0)
			points1 = [[x,y+hb],[x+wb,y+hb],[x+wb,y]]; 
		else if (vy < 0)
			points1 = [[x+wb,y+hb],[x+wb,y],[x,y]]; 
			};
			 
	 var t = 0;
	 var j = Math.max(Math.abs(vx),Math.abs(vy)); //how many steps to check
	 var dvx = vx/j;
	 var dvy = vy/j;
	 found: do{
		t++;
		var i = 0;
	  do{
		 var x1 = points1[i][0]; //coordinate of the faremost corner
		 var y1 = points1[i][1];
		 var xx1 = (x1+t*dvx);
		 var yy1 = (y1+t*dvy);
		 var v = Math.floor(xx1/d0);
		 var r = Math.floor(yy1/d0);
		 
		 if (map[v][r]!=0 ) // not transparent!
		 { 
		 break found; }; 
		 i++; 
		 } while (i < points1.length); 
		} while (t < j) ;
	
	xb = Math.floor(xb + (t-1)*dvx); //move obj1 !
	yb = Math.floor(yb + (t-1)*dvy);
	obj1.setPosition(xb,yb);