Visualizzazione post con etichetta canvas. Mostra tutti i post
Visualizzazione post con etichetta canvas. Mostra tutti i post

sabato 21 febbraio 2015

Un bel problemone: ostacoli trasparenti!

Ed ecco un bellissimo bug! L'ostacolo "trasparente"! Quando viene incontrato un altro ostacolo più in basso, il "mobile" sceglie quest'ultimo come ostacolo, ignorando il primo.
L'inverso non accade.



Però se io nella matrice inverto la posizione dei due ostacoli, accade solo il contrario:
Ecco il codice:
function init(){
 initContext("canvas");
 
 //preparazione di figure varie
 sfondo=new rect(0,0,WIDTH,HEIGHT,"white");
 rettangolo=new rect(0,0,50,50,"red");
 ostacolo=new rect(100,200,300,80,"cyan");
 ostacolo2=new rect(100,400,300,80,"green");
 matrice.push(ostacolo);
 matrice.push(ostacolo2);
 setInterval(draw,1);
} 


Proviamo a invertire l'ordine nella matrice:
function init(){
 initContext("canvas");
 
 //preparazione di figure varie
 sfondo=new rect(0,0,WIDTH,HEIGHT,"white");
 rettangolo=new rect(0,0,50,50,"red");
 ostacolo=new rect(100,200,300,80,"cyan");
 ostacolo2=new rect(100,400,300,80,"green");
 matrice.push(ostacolo2);
 matrice.push(ostacolo);
 setInterval(draw,1);
} 
E rivediamo...



Sì, confermato!

Cerchiamo di spiegare il meccanismo.
Nello scorrere la matrice, il programma analizza dapprima il primo elemento, trova verificata la funzione scavalcaBasso e appone il mobile a ridosso di esso; quindi analizza il secondo elemento e trova verificata la stessa funzione anche in questo, e allora appone il mobile a ridosso di quest'altro, facendogli "magicamente" saltare al di là dell'ostacolo.
Se invece io inizio con l'ultimo elemento dell'array, dopo aver verificato la funzione scavalcaBasso per esso, non vi sono più altri elementi da analizzare, perché l'altro ostacolo è già stato analizzato in precedenza, e allora la cosa funziona.

Come rimediare?

venerdì 20 febbraio 2015

Collisioni con ostacoli multipli

Creiamo un altro ostacolo ad angolo retto col primo.

Fatto! Con la creazione di una matrice di oggetti, funziona tutto! Salvo il "bug" degli angoli che avevo già individuato e per il quale dovrò studiare una soluzione, ossia che se il mobile entra velocemente in esatta corrispondenza di un angolo non viene bloccato e attraversa l'ostacolo.
Ecco comunque il codice:
function OnMouseMove(evt){
 
 puntoX=evt.pageX-canvas.offsetLeft;
 puntoY=evt.pageY-canvas.offsetTop;
 for(var i=0;i<matrice.length;i++){
  matrice[i].direzione="";
 }
 for(var i=0;i<matrice.length;i++){
  if(scavalcaDestra(puntoX,rettangolo,matrice[i])) matrice[i].direzione="destra";
  if(scavalcaSinistra(puntoX,rettangolo,matrice[i])) matrice[i].direzione="sinistra";
  if(scavalcaAlto(puntoY,rettangolo,matrice[i]))matrice[i].direzione="alto";
  if(scavalcaBasso(puntoY,rettangolo,matrice[i]))matrice[i].direzione="basso";
 }
 
 rettangolo.x=puntoX;
 rettangolo.y=puntoY;
 for(var i=0;i<matrice.length;i++){
  if(matrice[i].direzione=="basso")rettangolo.y=alto(matrice[i])-rettangolo.h;
  if(matrice[i].direzione=="alto")rettangolo.y=basso(matrice[i]);
  if(matrice[i].direzione=="destra")rettangolo.x=sinistra(matrice[i])-rettangolo.w;
  if(matrice[i].direzione=="sinistra")rettangolo.x=destra(matrice[i]);
 }
 
}
Dopo aver creato un array e pushativi dentro i due rettangoli:
var puntoX;
var puntoY;
var rettangolo,ostacolo;
var ostacolo2;

var matrice=new Array;

function init(){
 initContext("canvas");
 
 //preparazione di figure varie
 sfondo=new rect(0,0,WIDTH,HEIGHT,"white");
 rettangolo=new rect(0,0,50,50,"red");
 ostacolo=new rect(100,300,300,80,"cyan");
 ostacolo2=new rect(400,100,80,200,"green");
 matrice.push(ostacolo);
 matrice.push(ostacolo2);
 setInterval(draw,1);
} 


Ecco il filmato, con il "bug degli angoli":



...bug che già avrei in mente come affrontare, speriamo che funzioni.

Iniziamo ad affrontare il problema di più ostacoli...

Adesso il problema è con più di un ostacolo.
Mentre finora ero riuscito a superare i problemi, adesso invece il problema è completamente nuovo e mi ci sto scervellando teoricamente da tempo.
Meglio mettere tutto in pratica e sperimentare, anziché fasciarsi la testa con le obiezioni a livello teorico.


L'idea di base è mettere la variabile direzione come proprietà di ogni oggetto ostacolo.
Proviamo, nella dichiarazione del costruttore di rect:
function rect(x,y,w,h,colore){
 this.direzione="";
 this.x=x;
 this.y=y;
 this.w=w;
 this.h=h;
 this.colore=colore
 this.disegna=function(){
  ctx.fillStyle=this.colore;
  ctx.beginPath();
  ctx.rect(this.x,this.y,this.w,this.h);
  ctx.closePath();
  ctx.fill(); 
 }  
}
E a questo punto il programma dovrà leggere la direzione da ogni singolo oggetto... Vediamo di costruire un codice plausibile...

function OnMouseMove(evt){
 
 puntoX=evt.pageX-canvas.offsetLeft;
 puntoY=evt.pageY-canvas.offsetTop;
 ostacolo.direzione="";
 if(scavalcaDestra(puntoX,rettangolo,ostacolo)) ostacolo.direzione="destra";
 if(scavalcaSinistra(puntoX,rettangolo,ostacolo)) ostacolo.direzione="sinistra";
 if(scavalcaAlto(puntoY,rettangolo,ostacolo))ostacolo.direzione="alto";
 if(scavalcaBasso(puntoY,rettangolo,ostacolo))ostacolo.direzione="basso";
 scrivi(ostacolo.direzione);
 rettangolo.x=puntoX;
 rettangolo.y=puntoY;
 if(ostacolo.direzione=="basso")rettangolo.y=alto(ostacolo)-rettangolo.h;
 if(ostacolo.direzione=="alto")rettangolo.y=basso(ostacolo);
 if(ostacolo.direzione=="destra")rettangolo.x=sinistra(ostacolo)-rettangolo.w;
 if(ostacolo.direzione=="sinistra")rettangolo.x=destra(ostacolo);
 
}
Funziona esattamente come prima.
Adesso il problema è attribuire a ogni ostacolo un valore diverso...

Collisioni con un ostacolo completate.

Il passo successivo è respingere nella direzione voluta il mobile a seconda della direzione di arrivo sull'ostacolo.

Con questo codice sembra funzionare tutto.
function OnMouseMove(evt){
 puntoX=evt.pageX-canvas.offsetLeft;
 puntoY=evt.pageY-canvas.offsetTop;
 direzione="";
 if(scavalcaDestra(puntoX,rettangolo,ostacolo))direzione="destra";
 if(scavalcaSinistra(puntoX,rettangolo,ostacolo)) direzione="sinistra";
 if(scavalcaAlto(puntoY,rettangolo,ostacolo))direzione="alto";
 if(scavalcaBasso(puntoY,rettangolo,ostacolo))direzione="basso";
 scrivi(direzione);
 rettangolo.x=puntoX;
 rettangolo.y=puntoY;
 if(direzione=="basso")rettangolo.y=alto(ostacolo)-rettangolo.h;
 if(direzione=="alto")rettangolo.y=basso(ostacolo);
 if(direzione=="destra")rettangolo.x=sinistra(ostacolo)-rettangolo.w;
 if(direzione=="sinistra")rettangolo.x=destra(ostacolo);
 
}
A ogni movimento del mouse azzero "direzione", che viene poi individuata con le funzioni che modificano la variabile direzione e quindi, quando il mobile acquisisce le coordinate del puntatore del mouse, verrà respinto nella direzione voluta a seconda del valore assunto dalla variabile direzione.
Ecco un filmato:

Collisioni col canvas: individuare la direzione di un oggetto verso un altro.

Una volta create le funzioni che stabiliscono le posizioni dell'ostacolo rispetto al mobile, cerco di cogliere il momento in cui il mobile, nel suo movimento, va a sovrapporsi all'ostacolo.
Ecco le funzioni:
function scavalcaBasso(puntatore, mobile, ostacolo){
 if(ostacoloInBasso(mobile, ostacolo) && puntatore+mobile.h>alto(ostacolo)) return true;
 return false;
}
function scavalcaAlto(puntatore,mobile,ostacolo){
 if(ostacoloInAlto(mobile,ostacolo) && puntatore<basso(ostacolo))return true;
 return false;
}
function scavalcaDestra(puntatore,mobile,ostacolo){
 if(ostacoloADestra(mobile,ostacolo) && puntatore+mobile.w>sinistra(ostacolo))return true;
 return false;
}
function scavalcaSinistra(puntatore,mobile,ostacolo){
 if(ostacoloASinistra(mobile,ostacolo) && puntatore<destra(ostacolo))return true;
 return false;
} 
Ed eccole applicate nell'evento OnMouseMove:
function OnMouseMove(evt){
 puntoX=evt.pageX-canvas.offsetLeft;
 puntoY=evt.pageY-canvas.offsetTop;
 
 if(scavalcaDestra(puntoX,rettangolo,ostacolo))direzione="destra";
 if(scavalcaSinistra(puntoX,rettangolo,ostacolo)) direzione="sinistra";
 if(scavalcaAlto(puntoY,rettangolo,ostacolo))direzione="alto";
 if(scavalcaBasso(puntoY,rettangolo,ostacolo))direzione="basso";
 scrivi(direzione);
 rettangolo.x=puntoX;
 rettangolo.y=puntoY;
} 
E adesso mi ci faccio anche un filmato:



Abbiamo dunque creato il modo di individuare la direzione nella quale arriva il mobile sull'ostacolo.

Collisioni fra rettangoli con blocco di un rettangolo mobile su un ostacolo, che continua a muoversi al movimento del mouse secondo le direzioni consentite.

Decidiamoci ad affrontare il problema (difficilissimo).

Per prima cosa, quando due rettangoli si sovrappongono, per respingere di nuovo il rettangolo "mobile" ai margini del rettangolo "ostacolo", bisogna sapere la direzione dalla quale il mobile viene, per poter respingerlo in quella direzione.
Se ho una situazione di questo genere...



Il mobile (rettangolo rosso) si sovrappone all'ostacolo (rettangolo blu), ma non è possibile stabilire se sia arrivato a sovrapporsi partendo dall'alto o da sinistra, e quindi non è possibile stabilire se lo si deve ricacciare in alto o a sinistra.


Quindi si deve individuare la direzione.
Ho trovato il modo di confrontare la posizione reale del mobile rispetto all'ostacolo.
Creiamo le funzioni.

Innanzitutto definiamo i lati del mobile, alto, basso, sinistra e destra.
function alto(figura){
 return figura.y;
}
function basso(figura){
 return figura.y+figura.h;
}
function sinistra(figura){
 return figura.x;
}
function destra(figura){
 return figura.x+figura.w;
}
Quindi definiamo la posizione relativa del mobile rispetto all'ostacolo.<
function ostacoloInBasso(mobile,ostacolo){
 if(basso(mobile)<=alto(ostacolo) && sinistra(mobile)<=destra(ostacolo) && destra(mobile)>=sinistra(ostacolo))return true;
 return false;
}
function ostacoloInAlto(mobile, ostacolo){
 if(alto(mobile)>=basso(ostacolo) && sinistra(mobile)<=destra(ostacolo) && destra(mobile)>=sinistra(ostacolo))return true;
 return false;
}
function ostacoloADestra(mobile,ostacolo){
 if(destra(mobile)<=sinistra(ostacolo) && alto(mobile)<=basso(ostacolo) && basso(mobile)>=alto(ostacolo))return true;
 return false;
}
function ostacoloASinistra(mobile,ostacolo){
 if(sinistra(mobile)>=destra(ostacolo) && alto(mobile)<=basso(ostacolo) && basso(mobile)>=alto(ostacolo))return true;
 return false;
}

Verifichiamole...
Ostacolo in basso:
function OnMouseMove(evt){
 puntoX=evt.pageX-canvas.offsetLeft;
 puntoY=evt.pageY-canvas.offsetTop;
 rettangolo.x=puntoX;
 rettangolo.y=puntoY;
 scrivi(ostacoloInBasso(rettangolo,ostacolo));
}
...e funziona.

Verifichiamo l'altra:
Ostacolo in alto:
function OnMouseMove(evt){
 puntoX=evt.pageX-canvas.offsetLeft;
 puntoY=evt.pageY-canvas.offsetTop;
 rettangolo.x=puntoX;
 rettangolo.y=puntoY;
 scrivi(ostacoloInAlto(rettangolo,ostacolo));
}
...okay!

Ostacolo a destra:
function OnMouseMove(evt){
 puntoX=evt.pageX-canvas.offsetLeft;
 puntoY=evt.pageY-canvas.offsetTop;
 rettangolo.x=puntoX;
 rettangolo.y=puntoY;
 scrivi(ostacoloADestra(rettangolo,ostacolo));
}
...okay!

Ostacolo a sinistra:
function OnMouseMove(evt){
 puntoX=evt.pageX-canvas.offsetLeft;
 puntoY=evt.pageY-canvas.offsetTop;
 rettangolo.x=puntoX;
 rettangolo.y=puntoY;
 scrivi(ostacoloASinistra(rettangolo,ostacolo));
}
...e funziona anche questo!
Ecco realizzato il primo passaggio.

martedì 17 febbraio 2015

Creazione dell'oggetto rect (rettangolo)

Ripassato il canvas, adesso ripasso come ho creato un oggetto rect, di cui la rappresentazione grafica è soltanto il metodo disegna():
function rect(x,y,w,h,colore){
 this.x=x;
 this.y=y;
 this.w=w;
 this.h=h;
 this.dx=this.x+this.w;
 this.dy=this.y+this.h;
 this.colore=colore
 this.controllo="";
 this.bloccoX=false;
 this.disegna=function(){
  ctx.fillStyle=this.colore;
  ctx.beginPath();
  ctx.rect(this.x,this.y,this.w,this.h);
  ctx.closePath();
  ctx.fill(); 
 }  
}
Fornendo come parametri le coordinate, l'ampiezza e l'altezza e il colore, poi queste vengono usate all'atto del disegnare.
Ci sono altre proprietà che però adesso mi suonano un po' come relitti perché non le ho mai usate, e forse sarebbe il caso di rivederle.
Intanto riscriviamole più chiare, ed eliminiamo quelle che non ci risultano utili (aggiunte in precedenza per certi tentativi poi non riusciti).
function rect(x,y,w,h,colore){
 this.x=x;
 this.y=y;
 this.w=w;
 this.h=h;
 this.destra=this.x+this.w;
 this.basso=this.y+this.h;
 this.colore=colore
 this.disegna=function(){
  ctx.fillStyle=this.colore;
  ctx.beginPath();
  ctx.rect(this.x,this.y,this.w,this.h);
  ctx.closePath();
  ctx.fill(); 
 }  
}

Così adesso provo un codice mediante la creazione di oggetti rettangolo...
}
function initContext(nome){
 canvas=$(nome);
 ctx=canvas.getContext("2d");
 var rettangolo=new rect(0,0,200,100,"red");
 var rettangolo2=new rect(100,100,200,300,"blue");
 rettangolo.disegna();
 rettangolo2.disegna();
}
Che funziona:

Ripassiamo le basi del canvas

In fondo creare un canvas non è poi così complicato.
A furia di fare infinite masturbazioni cerebrali sulle collisioni ottenute per mezzo dei movimenti del mouse stavo perdendo di vista la creazione di un canvas.
Ecco il codice, ripassiamolo...
var canvas;
var ctx;
var WIDTH;
var HEIGHT;
function initContext(nome){
 canvas=$(nome);
 ctx=canvas.getContext("2d");
 WIDTH=canvas.width;
 HEIGHT=canvas.height;
}
Dopo aver dichiarato le variabili necessarie creo il canvas e il contesto.
Più essenziale ancora:
var canvas;
var ctx;
function initContext(nome){
 canvas=$(nome);
 ctx=canvas.getContext("2d");
}
Dovrebbe funzionare: per verificarlo, disegniamoci un rettangolo...
var canvas;
var ctx;
function initContext(nome){
 canvas=$(nome);
 ctx=canvas.getContext("2d");
 ctx.fillStyle="dodgerblue";
 ctx.beginPath();
 ctx.rect(100,100,300,200);
 ctx.closePath();
 ctx.fill();
}




Ottimo! Almeno abbiamo ripassato le basi del canvas, che rischiavo di perdere di vista...

martedì 27 gennaio 2015

Creare delle funzioni che identifichino la posizione del mobile rispetto a un ostacolo.
Innanzitutto, identificare sinistra, destra, alto e basso.
function sinistra(oggetto){
 return oggetto.x;
}

function destra(oggetto){
 return oggetto.x+oggetto.w;
}

function alto(oggetto){
 return oggetto.y;
}

function basso(oggetto){
 return oggetto.y+oggetto.h;
}

Con queste funzioni, adesso definiamo la posizione relativa di un mobile rispetto a un ostacolo:
function ASinistra(mobile, ostacolo){
 if(destra(mobile)<sinistra(ostacolo) && alto(mobile)<basso(ostacolo) && basso(mobile)>alto(ostacolo)) return true;
 return false;
}

function ADestra(mobile ostacolo){
 if(sinistra(mobile)>destra(ostacolo) && alto(mobile)>basso(ostacolo) && basso(mobile)>alto(ostacolo)) return true;
 return false;
}

function InAlto(mobile, ostacolo){
 if(basso(mobile)<alto(ostacolo) && destra(mobile)>sinistra(ostacolo) && sinistra(mobile)<destra(ostacolo)) return true;
 return false;
}

function InBasso(mobile, ostacolo){
 if (alto(mobile)>basso(ostacolo) && destra(mobile)>sinistra(ostacolo) && sinistra(mobile)<destra(ostacolo)) return true;
 return false;
} 
E adesso facciamo la prova... modificando il codice di OnMouseMove:
function OnMouseMove(evt){
 rettangolo.x=evt.pageX;
 rettangolo.y=evt.pageY;

 scrivi(InBasso(rettangolo,ost));
}
provando via via con InAlto, ADestra, ASinistra, e cambiando ost con ost2 (due diversi ostacoli disegnati sul canvas) funziona tutto!


Alla fine ho fatto tutto, definendo sia le posizioni del mobile, sia le posizioni delm mobile virtuale dato dalla posizione del cursore...
function sinistra(oggetto){
 return oggetto.x;
}

function destra(oggetto){
 return oggetto.x+oggetto.w;
}

function alto(oggetto){
 return oggetto.y;
}

function basso(oggetto){
 return oggetto.y+oggetto.h;
}


//_________________________________________________________________________________________________

function ASinistra(mobile, ostacolo){
 if(destra(mobile)<sinistra(ostacolo) && alto(mobile)<basso(ostacolo) && basso(mobile)>alto(ostacolo)) return true;
 return false;
}

function ADestra(mobile, ostacolo){
 if(sinistra(mobile)>destra(ostacolo) && alto(mobile)<basso(ostacolo) && basso(mobile)>alto(ostacolo)) return true;
 return false;
}

function InAlto(mobile, ostacolo){
 if(basso(mobile)<alto(ostacolo) && destra(mobile)>sinistra(ostacolo) && sinistra(mobile)<destra(ostacolo)) return true;
 return false;
}

function InBasso(mobile, ostacolo){
 if (alto(mobile)>basso(ostacolo) && destra(mobile)>sinistra(ostacolo) && sinistra(mobile)<destra(ostacolo)) return true;
 return false;
}

//________________________________________________________________________________________________

function sinistraVirtuale(evt,mobile){
 return evt.pageX;
}

function destraVirtuale(evt,mobile){
 return evt.pageX+mobile.w;
}

function altoVirtuale(evt,mobile){
 return evt.pageY;
}

function bassoVirtuale(evt,mobile){
 return evt.pageY+mobile.h;
}

//________________________________________________________________________________________________

function ASinistraDelMargineDestro(evt,mobile, ostacolo){
 if(sinistraVirtuale(evt,mobile)<destra(ostacolo) && altoVirtuale(evt,mobile)<basso(ostacolo) && bassoVirtuale(evt,mobile)>alto(ostacolo)) return true;
 return false;
}
 
function ADestraDelMargineSinistro(evt,mobile, ostacolo){
 if(destraVirtuale(evt,mobile)>sinistra(ostacolo) && altoVirtuale(evt,mobile)<basso(ostacolo) && bassoVirtuale(evt,mobile)>alto(ostacolo)) return true;
 return false;
}
function InAltoDelMargineBasso(evt,mobile, ostacolo){
 if(altoVirtuale(evt,mobile)<basso(ostacolo) && destraVirtuale(evt,mobile)>sinistra(ostacolo) && sinistraVirtuale(evt,mobile)<destra(ostacolo)) return true;
 return false;
}

function InBassoDelMargineAlto(evt,mobile, ostacolo){
 if (bassoVirtuale(evt,mobile)>alto(ostacolo) && destraVirtuale(evt,mobile)>sinistra(ostacolo) && sinistraVirtuale(evt,mobile)<destra(ostacolo)) return true;
 return false;
}

sabato 17 gennaio 2015

Collisioni

Lo scopo è far rimanere il rettangolo entro la metà sinistra del canvas.

La logica è che il margine sinistro del rettangolo è pari al pageX del mouse se non supera un determinato valore di coordinata x.

function OnMouseMove(evt){
 if (evt.pageX<300) rettangolo.x=evt.pageX;
 rettangolo.y=evt.pageY;
}




mercoledì 14 gennaio 2015

Oggetti rettangolo sul canvas.

Trasformo un rettangolo in un oggetto.
function rect(x,y,w,h,colore){
 ctx.fillStyle=colore;
 ctx.beginPath();
 ctx.rect(x,y,w,h);
 ctx.closePath();
 ctx.fill();
 this.x=x;
 this.y=y;
 this.w=w;
 this.h=h;
}
Così quando voglio disegnare un rettangolo sul canvas:
 rettangolo=new rect(100,100,50,20,"dodgerblue"); 
 alert(rettangolo.x);
Che mi restituisce una messageBox con il valore 100.

Ancora:
function draw(){
  rect(0,0,WIDTH,HEIGHT,sfondo);
  
 rettangolo=new rect(100,100,50,20,"dodgerblue"); 
 alert(rettangolo.x);
 
 rettangolo2=new rect(50,150,200,10,"green");
 alert("Area = "+rettangolo2.x*rettangolo2.y);
}


Ed ecco i successivi sviluppi...

<!DOCTYPE html>
<html>
<script src="funzioniCanvas.js"></script>
<script>
var sfondo;
var rettangolo;
var rettangolo2;

function init(){
  initContext("canvas");
  
  
  //istanziazione di rettangoli
  
  sfondo=new rect(0,0,WIDTH,HEIGHT,"black");
 rettangolo=new rect(100,100,50,20,"dodgerblue"); 
 rettangolo2=new rect(50,150,200,10,"green");

 
 
  intervallo=setInterval(draw,1);
}

function draw(){
  
  sfondo.disegna();
 rettangolo.disegna();
 rettangolo2.disegna();
 rettangolo2.y+=.1
 rettangolo.x+=.1;

}

window.addEventListener("load",init);
</script>
<body>
<canvas id="canvas" width=500 height=500></canvas>
<textarea id="testo" style="float:right;height:500px""></textarea>
<div id="delta"></div>
</body>
</html> 


Così ottengo anche il movimento dei rettangoli.
Essi vengono istanziati prima della funzione draw(), e vengono solo disegnati a ogni ripetizione della funzione.

sabato 10 gennaio 2015

Collisioni perfezionate (in attesa del prossimo bug)

Ecco le collisioni su tutti i lati, ancora perfezionato, in attesa che vengano fuori degli altri bugghetti a farmi imprecare un po'...
<!DOCTYPE html>
<html>
<script src="funzioniCanvas.js"></script>
<script>
var x=200;
var dx=4;
var y=200;
var dy=3;
var w=30;
var h=30;
var sfondo="black";
var colore="green";

var immagine;

function init(){
 initContext("canvas");
 intervallo=setInterval(draw,1);
}

function draw(){
 rect(0,0,WIDTH,HEIGHT,sfondo);
 rect(x,y,w,h,colore);
 
 rect(350,100,1,250,"#FFFF00");
 rect(50,100,1,250,"#FFFF00");
 //rect(100,50,300,1,"#FF0000");
 rect(100,400,300,1,"#FF0000");
 immagine=ctx.getImageData(x+w,y-1,dx,h+2);
 immagine2=ctx.getImageData(x-1,y-1,dx,h+2);
 immagineGiu=ctx.getImageData(x-1,y+h,w+2,dy)
 immagineSu=ctx.getImageData(x-1,y-1,w+2,dy)
 
 
 
 XDetect(immagine,0,255);
 XDetect(immagine2,0,255);

 YDetect(immagineGiu,0,255);
 YDetect(immagineSu,0,255);
 
 if(x+w>WIDTH || x<0) dx=-dx;
 if(y+h>HEIGHT || y<0) dy=-dy;
 
 x+=dx;
 y+=dy;
}

function XDetect(img,colByte, colore){
  for(var n=0;n<img.data.length;n+=4){ 
  if(img.data[n+colByte]==colore) {
   dx=-dx; 
   break;
  } 
 }
}

function YDetect(img,colByte, colore){
  for(var n=0;n<img.data.length;n+=4){ 
  if(img.data[n+colByte]==colore) {
   dy=-dy; 
   break;
  } 
 }
}

window.addEventListener("load",init);
</script>
<body>
<canvas id="canvas" width=500 height=500></canvas>
<textarea id="testo" style="float:right;height:500px""></textarea>
<div id="delta"></div>
</body>
</html>
Ecco il risultato:



Il movimento a scatti è una questione di browser. Non solo per questo, Firefox mi rallenta dopo un po' di uso, e per ripristinare la velocità devo riavviarlo... Boh?

Detezione delle collisioni su tutto il perimetro del rettangolo con il canvas.

Fatto! Realizzata la detezione delle collisioni su tutto il perimetro del rettangolo!
Ecco il codice:
<!DOCTYPE html>
<html>
<script src="funzioniCanvas.js"></script>
<script>
var x=200;
var dx=1;
var y=200;
var dy=1;
var w=50;
var h=50;
var sfondo="black";
var colore="green";

var immagine;

function init(){
 initContext("canvas");
 intervallo=setInterval(draw,1);
}

function draw(){
 rect(0,0,WIDTH,HEIGHT,sfondo);
 rect(x,y,w,h,colore);
 
 rect(350,100,1,250,"#FFFF00");
 rect(50,100,1,250,"#FFFF00");
 rect(100,50,300,1,"#FF0000");
 rect(100,400,300,1,"#FF0000");
 immagine=ctx.getImageData(x+w,y,1,h);
 immagine2=ctx.getImageData(x-1,y,1,h);
 immagineGiu=ctx.getImageData(x,y+h,w,1)
 immagineSu=ctx.getImageData(x,y-1,w,1)
 
 
 
 XDetect(immagine,0,255);
 XDetect(immagine2,0,255);

 YDetect(immagineGiu,0,255);
 YDetect(immagineSu,0,255);
 
 if(x+w>WIDTH || x<0) dx=-dx;

 x+=dx;
 y+=dy;
}

function XDetect(img,colByte, colore){
  for(var n=0;n<img.data.length;n+=4){ 
  if(img.data[n+colByte]==colore) {
   dx=-dx; 
   break;
  } 
 }
}

function YDetect(img,colByte, colore){
  for(var n=0;n<img.data.length;n+=4){ 
  if(img.data[n+colByte]==colore) {
   dy=-dy; 
   break;
  } 
 }
}

window.addEventListener("load",init);
</script>
<body>
<canvas id="canvas" width=500 height=500></canvas>
<textarea id="testo" style="float:right;height:500px""></textarea>
<div id="delta"></div>
</body>
</html>
Le due funzioni chiave sono XDetect e YDetect.

Ecco il risultato:

Detezione delle collisioni sull'asse X bilateralmente col canvas.

Ecco il codice per le collisioni bilaterali sull'asse X.
Ho preferito creare due rettangolini-spia, l'uno sul lato destro e l'altro sul lato sinistro, in quanto la creazione di un rettangolo-spia delle dimensioni del rettangolo più qualche pixel accessorio che costituisse la zona-spia si è rivelata molto più lenta, verosimilmente perché deve trascrivere nella variabile immagine tutti i dati dei pixel del rettangolo, molti di più rispetto a quelli dei rettangolini.

La funzione di "detezione" (esisterà questa parola in italiano?) l'ho isolata in una funzione a parte:
function XDetect(img,colByte, colore){
		for(var n=0;n&>lt;img.data.length;n+=4){	
		if(img.data[n+colByte]==colore) {
			dx=-dx;	
			break;
		}	
	}
}
...che accetta come parametri il rettangolino (img), il numero di byte del quale si vuole eseguire la detezione dopo quello iniziale del colore rosso (se è 0 si farà la detezione dal byte del rosso, se 1 da quello del verde, se 2 da quello del blu e se 3 da quello della trasparenza), e come terzo parametro il colore da "detectare".
<!DOCTYPE html>
<html>
<script src="funzioniCanvas.js"></script>
<script>
var x=200;
var dx=1;
var y=21;
var dy=.1;
var w=80;
var h=10;
var sfondo="black";
var colore="green";
var osx=100;
var osy=10;
var osw=1;
var osh=200;
var oscolore="#FFFF00";
var immagine;

function init(){
	initContext("canvas");
	intervallo=setInterval(draw,1);
}

function draw(){
	rect(0,0,WIDTH,HEIGHT,sfondo);
	rect(x,y,w,h,colore);
	rect(osx,osy,osw,osh, oscolore);
	rect(300,0,1,50,"#FF0000");
	immagine=ctx.getImageData(x+w,y,1,h);
	immagine2=ctx.getImageData(x-1,y,1,h);
	XDetect(immagine,0,255);
	XDetect(immagine2,0,255);

	if(x+w>WIDTH || x<0) dx=-dx;

	x+=dx;
	y+=dy;
}

function XDetect(img,colByte, colore){
		for(var n=0;n<img.data.length;n+=4){	
		if(img.data[n+colByte]==colore) {
			dx=-dx;	
			break;
		}	
	}
}
window.addEventListener("load",init);
</script>
<body>
<canvas id="canvas" width=500 height=500></canvas>
<textarea id="testo" style="float:right;height:500px""></textarea>
<div id="delta"></div>
</body>
</html> 
Ed ecco il risultato:

venerdì 9 gennaio 2015

Collisioni con spostamenti superiori a un pixel

Se però il rettangolo si sposta con una velocità maggiore di 1 pixel alla volta, può accadere che la collisione non venga individuata.



Con un rettangolino-spia della larghezza di un solo pixel, lo spostamento di due pixel alla volta può far "saltare" un ostacolo della larghezza di un solo pixel.
Così conviene fare un rettangolino-spia di due pixel di larghezza.
Faccio in modo che il rettangolo si muova di due pixel alla volta, per fare una prova:
<!DOCTYPE html>
<html>
<script src="funzioniCanvas.js"></script>
<script>
var x=145;
var dx=2;

var y=50;
var w=100;
var h=10;
var sfondo="black";
var colore="green";
var osx=350;
var osy=50;
var osw=1;
var osh=100;
var oscolore="#FFFF00";
var immagine;

function init(){
 initContext("canvas");
 intervallo=setInterval(draw,10);
}

function draw(){
 rect(0,0,WIDTH,HEIGHT,sfondo);
 rect(x,y,w,h,colore);
 rect(osx,osy,osw,osh, oscolore);
 
 immagine=ctx.getImageData(x+w,y,1,h);
 
 $("testo").value="";
 for(var n=0;n<immagine.data.length;n+=4){
  $("testo").value+=immagine.data[n]+" "+immagine.data[n+1]+" "+immagine.data[n+2]+" "+immagine.data[n+3]+"\n";
  if(immagine.data[n]==255) {
   dx=-dx;
   
   break;
  }
  
  
 }
 x+=dx;
}
window.addEventListener("load",init);
</script>
<body>
<canvas id="canvas" width=500 height=500></canvas>
<textarea id="testo" style="float:right;height:500px""></textarea>
<div id="delta"></div>
</body>
</html> 
Ecco, lo spostamento istantaneo del rettangolo è di 2 pixel, mentre il rettangolino-spia ha ancora una larghezza di un pixel.
Se la teoria è giusta, cambiando la posizione di partenza del rettangolo, si alternano posizioni in cui la collisione viene vista e posizioni in cui non viene vista, alternate.
E infatti così succede:
Con questa posizione (x = 145),...
var x=145;
var dx=2;

.....

 immagine=ctx.getImageData(x+w,y,1,h);
...la collisione non viene individuata e l'ostacolo viene attraversato tranquillamente dal rettangolo.

Aumentiamo di 1 la posizione del rettangolo:
var x=146;
var dx=2;
(il rettangolino-spia è sempre di 1)
... la collisione c'è, e il rettangolo torna indietro non appena "toccato" l'ostacolo.

Aumentiamo ancora di 1
var x=147;
var dx=2;
e la collisione adesso non c'è: l'ostacolo viene attraversato.

Ancora:
var x=148;
var dx=2;
...e la collisione c'è!
Questo conferma in pieno la mia teoria illustrata con l'immagine sopra!

Adesso proviamo ad aumentare di 1 la larghezza del rettangolino-spia:
var x=145;
var dx=2;

....

 immagine=ctx.getImageData(x+w,y,2,h);
...e stiamo a vedere, variando di nuovo la posizione del rettangolo...
E la collisione c'è!

Aumentiamo:
var x=146;
var dx=2;
...e la collisione c'è!

Ancora:
var x=147;
var dx=2;
e la collisione c'è ancora!

Ancora:
var x=148;
var dx=2;
E la collisione c'è ancora!

La teoria è esatta!

Studio dettagliato della collisione.

Ecco un modello per studiare attentamente come avviene l'individuazione della collisione e come cambia la direzione del rettangolo una volta "colliso" con l'ostacolo.
La frequenza di ripetizione della funzione di ridisegno del canvas è impostata molto lenta per avere agio di vedere le modifiche dei valori, nella textarea i bytes visti dal rettangolino-spia, nel DIV sottostante il valore del byte di colore che si vuole esplorare e il numero che stabilisce la direzione del movimento.
<!DOCTYPE html>
<html>
<script src="funzioniCanvas.js"></script>
<script>
var x=245;
var dx=1;
var delta=1;
var y=50;
var w=100;
var h=10;
var sfondo="black";
var colore="green";
var osx=350;
var osy=50;
var osw=1;
var osh=100;
var oscolore="#FFFF00";
var immagine;

function init(){
 initContext("canvas");
 intervallo=setInterval(draw,1000);
}

function draw(){
 rect(0,0,WIDTH,HEIGHT,sfondo);
 rect(x,y,w,h,colore);
 rect(osx,osy,osw,osh, oscolore);
 
 immagine=ctx.getImageData(x+w,y,1,h);
 
 $("testo").value="";
 for(var n=0;n<immagine.data.length;n+=4){
  $("testo").value+=immagine.data[n]+" "+immagine.data[n+1]+" "+immagine.data[n+2]+" "+immagine.data[n+3]+"\n";
  if(immagine.data[n]==255) delta=-1;
  $("delta").innerHTML=immagine.data[n]+ " " +delta;
  
 }
 x+=delta;
}
window.addEventListener("load",init);
</script>
<body>
<canvas id="canvas" width=500 height=500></canvas>
<textarea id="testo" style="float:right;height:500px""></textarea>
<div id="delta"></div>
</body>
</html> 

Il rettangolino-spia nell'individuazione delle collisioni col canvas.

Dunque...
La tecnica che sto mettendo a punto è quella di creare un'area in forma di rettangolino sul bordo destro di un rettangolo, nella quale individuare il colore che va considerato come ostacolo.
Ecco il codice di un rettangolo verde che si muove fino a incontrare una linea verticale gialla ferma (un rettangolo di larghezza 1).
Al momento non ci sono collisioni.
<!DOCTYPE html>
<html>
<script src="funzioniCanvas.js"></script>
<script>
var x=100;
var y=50;
var w=100;
var h=50;
var sfondo="black";
var colore="green";
var osx=350;
var osy=50;
var osw=1;
var osh=100;
var oscolore="#FFFF00";
function init(){
 initContext("canvas");
 intervallo=setInterval(draw,10);
}

function draw(){
 rect(0,0,WIDTH,HEIGHT,sfondo);
 rect(x,y,w,h,colore);
 rect(osx,osy,osw,osh, oscolore);
 x+=1;
}
window.addEventListener("load",init);
</script>
<body>
<canvas id="canvas" width=500 height=500></canvas>
</body>
</html> 
In questo, il rettangolo verde si muove di 1 pixel alla volta.
Considerando che la funzione si ripete ogni 10 millisecondi, la velocità raggiunta è di 100 pixel al secondo.
Se aumento la velocità riducendo l'intervallo a 1 millisecondo, la velocità sarà di 1000 pixel al secondo.

Ora creiamo il rettangolino, che avrà una posizione rappresentata dalle coordinate x+w, y e dimensioni pari a 1 x h.
 immagine=ctx.getImageData(x+w,y,1,h);
E una textarea per vedere cosa "vede" il rettangolino.
<textarea id="testo" style="float:right;height:500px""></textarea>
Ora il codice che scansiona tutto il rettangolino:
<!DOCTYPE html>
<html>
<script src="funzioniCanvas.js"></script>
<script>
var x=240;
var y=50;
var w=100;
var h=10;
var sfondo="black";
var colore="green";
var osx=350;
var osy=50;
var osw=1;
var osh=100;
var oscolore="#FFFF00";
var immagine;

function init(){
 initContext("canvas");
 intervallo=setInterval(draw,500);
}

function draw(){
 rect(0,0,WIDTH,HEIGHT,sfondo);
 rect(x,y,w,h,colore);
 rect(osx,osy,osw,osh, oscolore);
 
 immagine=ctx.getImageData(x+w,y,1,h);
 
 $("testo").value="";
 for(var n=0;n<immagine.data.length;n+=4){
  $("testo").value+=immagine.data[n]+" "+immagine.data[n+1]+" "+immagine.data[n+2]+" "+immagine.data[n+3]+"\n";
 }
 x+=1;
}
window.addEventListener("load",init);
</script>
<body>
<canvas id="canvas" width=500 height=500></canvas>
<textarea id="testo" style="float:right;height:500px""></textarea>
</body>
</html> 
Ho fatto qualche adattamento, rimpicciolendo il rettangolo mobile e avvicinandolo all'ostacolo, e rallentando la frequenza di ripetizione del disegno del canvas in modo da poter vedere con calma i risultati nella textarea.
Comunque il risultato è indubbio: non appena il rettangolo contatta l'ostacolo, nel rettangolino-spia che ho creato viene "visto" l'ostacolo di colore giallo (codice 255 255 0 255).
Questo rende piuttosto facile individuare ostacoli, sia pure con alcune limitazioni che verranno fuori dopo...

giovedì 8 gennaio 2015

Collisioni con il canvas, prove con il colore.

Ecco il codice di un rettangolo rosso che viene respinto da due triangoli aventi in comune il byte del colore verde di valore 0xFF (255):
<!DOCTYPE html>
<html>
<script src="funzioniCanvas.js"></script>
<script>

var x=150;
var y=30;
var dx=0;
var delta=5
var differenza=delta;
var dy=0;
var w=50;
var h=30;
var COLORE="#FF0000"
var immagine;
var scarto=5;
function init(){
 //chiamata obbligatoria.
 initContext("canvas");
  
 intervallo=setInterval(draw,10); 
}

function draw(){
 rect(0,0,WIDTH,HEIGHT,"black");
 rect(x,y,w,h,COLORE);
 rect(10,20,10,30,"#00FF00");
 rect(250,20,10,30,"#00FFAA");
 var areaWidth=w+scarto*2;
 
 immagine=ctx.getImageData(x-scarto,y,areaWidth,h);
 
 for(var n=0;n<immagine.data.length;n+=4){
  if(immagine.data[n+1]==255){
  
  
   if(n % areaWidth<20){
    differenza=delta;
      }
   else if (n % areaWidth >(areaWidth-scarto)){
    differenza=-delta;
   }
  }
 }
 x+=differenza;
}

window.addEventListener("load",init);
</script>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
Non ho la screencam e non lo posso documentare...

lunedì 5 gennaio 2015

Codice per il disegno dei mattoni nel giochino del Breakout, con codice per eliminare i mattoni colpiti.

Abbiamo detto che per disegnare i mattoncini si può scrivere un codice di questo genere:
 for(var i=0;i<NCOLS;i++){
  for(var j=0;j<NROWS;j++){
   rect((i*(BRICKWIDTH+PADDING))+PADDING,
   (j*(BRICKHEIGHT+PADDING))+PADDING,
   BRICKWIDTH,BRICKHEIGHT,"maroon");
  }
 }
Per ogni riga, da 0 fino a numero di colonne meno 1, la coordinata sinistra del mattone è pari al numero per la larghezza del mattone stesso (calcolando anche il padding, ossia le distanze fra mattoni).
La coordinata alto, invece, si calcola per ogni numero di coordinata sinistra incrementando un altro numero da zero fino al numero di righe meno 1.
Uffa, non mi sono spiegato chiaramente ma il concetto comunque è questo, mi è sufficientemente chiaro.

Però nel programma del tutorial c'è anche il codice per stabilire se un determinato mattone di coordinate i e j va disegnato o no.
Per questo si istituisce un array di dimensioni i e j, in cui ogni mattone è identificato da due numeri.
 for(i=0;i<NROWS;i++){
  bricks[i]=new Array(NCOLS);
  for(j=0;j<NCOLS;j++){
   bricks[i][j]=1;
  }
 }
Inizialmente tutti gli elementi dell'array hanno valore 1.
Se poi il mattone sarà colpito, gli verrà dato valore zero e non sarà ridisegnato al successivo ridisegno del canvas.
Ed il codice che stabilisce quale mattone è stato colpito è questo:
  rowheight = BRICKHEIGHT + PADDING;
  colwidth = BRICKWIDTH + PADDING;
  row = Math.floor(y/rowheight);
  col = Math.floor(x/colwidth);

  if (y < NROWS * rowheight  && bricks[row][col] == 1) {
    dy = -dy;
    bricks[row][col] = 0;
  }
Conoscendo y, ossia la coordinata y della palla, si divide y per l'altezza di ogni singolo mattone per trovare il numero di righe al quale ci si trova (approssimando tutto con il Math.floor, ossia togliendo i decimali).
Stessa cosa conoscendo x, dividendolo per la larghezza di ogni singolo mattone, in modo da conoscere il numero di colonne, per poi approssimare avendo il numero esatto.
Ovviamente questo deve valere se la palla si trova fra i mattoni. Se non specifichiamo questo, un valore di y troppo grande fa sì che si possano ottenere numeri di riga oltre quelli presenti nella matrice di mattoni. Infatti ho provato e mi dà errore.

Se quindi il mattone al cui livello si trova la palla è stato tracciato, ossia è pari a 1, lo si imposta pari a 0 e si rimanda indietro la palla.
Essendo pari a 0, al successivo ridisegno del canvas non sarà disegnato perché...
for (i=0; i < NROWS; i++) {
     for (j=0; j < NCOLS; j++) {
        if (bricks[i][j] == 1) {
          rect((j * (BRICKWIDTH + PADDING)) + PADDING, 
              (i * (BRICKHEIGHT + PADDING)) + PADDING,
              BRICKWIDTH, BRICKHEIGHT,"saddlebrown");
        }
     }
...perché i mattoni sono ridisegnati solo se valgono 1.

domenica 4 gennaio 2015

Disegno di "mattoni" di un muro (una griglia di rettangoli) sul canvas

Per cominciare, creiamo un array di rettangoli da mettere in colonna o in riga...

Ecco la formuletta:
<!DOCTYPE html>
<html>
<script src="funzioniCanvas.js"></script>

<script>
var bricks;
var NCOLS=10;
var NROWS=5;
var BRICKWIDTH=45;
var BRICKHEIGHT=10;
var PADDING=1;
function init(){
 //questa deve esserci sempre all'inizio: inizializza la variabile ctx, contesto del canvas.
 initContext("canvas");
 
 
 //seguono tutte le altre funzioni.
 
 for(var i=0;i<NCOLS;i++){
  for(var j=0;j<NROWS;j++){
   rect((i*(BRICKWIDTH+PADDING))+PADDING,
   (j*(BRICKHEIGHT+PADDING))+PADDING,
   BRICKWIDTH,BRICKHEIGHT,"maroon");
  }
 }
 
}
window.addEventListener("load",init);
</script>
<body>
<canvas id="canvas" width=500 height=500></canvas>
</body>
</html> 
Il codice Javascript inizia con l'aggiunta dell'EventListener per l'evento load all'oggetto window.
L'evento load viene associato a una funzione iniziale dal nome qualunque (in questo caso l'ho chiamata Init ma avrei potuto pure chiamarla Pasquale...), e deve però obbligatoriamente chiamare la funzione initContext che è contenuta nella libreria funzioniCanvas.js e che inizializza il context attribuendovi la variabile ctx.
Quindi c'è il codice per disegnare i "mattoni" del muro.
Per ogni colonna, per ogni riga, disegna il mattone con le coordinate ottenute moltiplicando il numero rispettivamente di colonna e di riga per la coordinata x e la coordinata y.
Ecco il risultato: