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.

Una griglia di caselle anche in C#

E facciamo la stessa cosa in C#: una griglia di caselle.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication14
{
    public partial class Form1 : Form
    {
        int NROWS = 5;
        int NCOLS = 3;

        public Form1()
        {
            InitializeComponent();
            for (int i = 0; i < NCOLS; i++)
            {
                for (int j = 0; j < NROWS; j++)
                {
                    Label miaLabel = new Label();
                    miaLabel.Text = "Ciccia";
                    miaLabel.BorderStyle = BorderStyle.FixedSingle;
                    miaLabel.Left = i * miaLabel.Width;
                    miaLabel.Top = j * miaLabel.Height;
                    Controls.Add(miaLabel);
                }
            }

        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }
    }
}
Ed ecco l'output, esattamente uguale a quello realizzato in VB.

domenica 4 gennaio 2015

Griglia di caselle in VB

E rifacciamo un codice come quello fatto con il Javascript per il canvas usando il VB.NET, per il quale avevo seguito una tecnica diversa...
Public Class Form1
    Dim NROWS As Integer = 5
    Dim NCOLS As Integer = 3

    Sub New()
        ' Chiamata richiesta dalla finestra di progettazione.
        InitializeComponent()
        For n = 0 To NCOLS - 1
            For m = 0 To NROWS - 1
                Dim casella As Label
                casella = New Label
                casella.Text = "ciccia"
                Me.Controls.Add(casella)
                casella.Left = n * casella.Width
                casella.Top = m * casella.Height
                casella.BorderStyle = BorderStyle.FixedSingle
                casella.Visible = True
            Next
        Next
    End Sub
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    End Sub
End Class

Output:



Ma c'era un altro metodo che avevo usato in precedenza, che sistemava una griglia di caselle dato il numero delle caselle e il numero di caselle per riga.
Public Class Form1
    Dim NLABELS As Integer = 15
    Dim NCOLS As Integer = 3

    Sub New()
        ' Chiamata richiesta dalla finestra di progettazione.
        InitializeComponent()
        For n = 0 To NLABELS - 1
            Dim casella As Label
            casella = New Label
            casella.Text = "ciccia"
            casella.Left = (n Mod NCOLS) * casella.Width
            casella.Top = (n \ NCOLS) * casella.Height

            Me.Controls.Add(casella)
            casella.BorderStyle = BorderStyle.FixedSingle
            casella.Visible = True
        Next

    End Sub
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    End Sub
End Class
...che dà un output simile, dato che il numero di caselle è divisibile per il numero di colonne...

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:

Costruzione del Breakout con il canvas.

Ecco, seguendo un tutorial ho realizzato l'abbozzo del videogiochino della pallina che deve rompere i mattoncini.
Per il momento ho realizzato solo la pallina e la racchetta.
Ora mi analizzo la struttura del giochino... Abbiamo alcune funzioni semplici che potrebbero essere anche messe su un file a parte.
Ho già linkato un file di codice Javascript per la sola funzione $ che evita di dover scrivere document.getElementById, e potrei linkare anche un altro file con queste funzioni.
Ma vediamo se sono "isolabili"...

Il codice intero è questo:
<!DOCTYPE html>
<html>
<head>
<script src="funzioni2.js"></script>

<script>
var canvas;
var ctx;
x=150;
y=150;
var dx=2;
var dy=4;
var WIDTH;
var HEIGHT;

var paddlex;
var paddlew;
var paddleh;


var rightDown=false;
var leftDown=false;

var intervallo;
function inizia(){
	
	//creiamo la base del contesto.
	canvas=$("canvas");
	ctx=canvas.getContext("2d");
	WIDTH=canvas.width;
	HEIGHT=canvas.height;
	
	
	paddlex=WIDTH/2-100;
	paddleh=10;
	paddlew=75;
	
	//funzione setInterval
	intervallo=setInterval(draw,15);
		
}

document.onkeydown=OnKeyDown;
document.onkeyup=OnKeyUp;

function OnKeyDown(evt){

	if (evt.keyCode==39) rightDown=true;
	else if(evt.keyCode==37) leftDown=true;	
}

function OnKeyUp(evt){
	if (evt.keyCode==39) rightDown=false;
	else if (evt.keyCode==37) leftDown=false;
}

function circle(x,y,r){
	ctx.fillStyle="red";
	ctx.beginPath();
	ctx.arc(x,y,r,0,Math.PI*2,true);
	ctx.closePath();
	ctx.fill();
}

function rect(x,y,w,h){
	ctx.beginPath();
	ctx.rect(x,y,w,h);
	ctx.closePath();
}

function clear(){
	ctx.clearRect(0,0,WIDTH,HEIGHT);
}

function draw() {
  	clear();
  	circle(x,y,10);
  	if(rightDown) paddlex+=5;
  	if(leftDown) paddlex-=5;
  	
  	ctx.fillStyle="blue";
  	rect(paddlex,HEIGHT-paddleh,paddlew,paddleh);
  	if(x+dx<0 || x+dx>WIDTH) 
  		dx=-dx;
  	if(y+dy<0)
  		dy=-dy;
  	else if(y+dy>HEIGHT){
	  	if(x>paddlex && x<(paddlex+paddlew))
	  		dy=-dy;
	  	else{
	  		clearInterval(intervallo);
	  		alert("Game over!");
	  		window.location.reload();
  		}
   }
	  	
  	
  		
  	ctx.fill();
  	x+=dx;
  	y+=dy;
}
	




window.addEventListener("load",inizia);
</script>
</head>
<body>

<canvas id="canvas" width="800" height="500" style="border:3px solid maroon"></canvas>

</body>
</html>


Abbiamo:
Funzione circle che traccia un cerchio:
function circle(x,y,r){
	ctx.fillStyle="red";
	ctx.beginPath();
	ctx.arc(x,y,r,0,Math.PI*2,true);
	ctx.closePath();
	ctx.fill();
}
Ecco, qui c'è un elemento che imposta il fillStyle e che va eliminato dalla funzione, oppure va compreso mettendo il colore come parametro.
Scegliamo la seconda e riscriviamo:
function circle(x,y,r,colore){
	ctx.fillStyle=colore;
	ctx.beginPath();
	ctx.arc(x,y,r,0,Math.PI*2,true);
	ctx.closePath();
	ctx.fill();
}
Provandola, con l'accortezza di aggiungere il colore fra i parametri quando si chiama la funzione,
function draw() {
  	clear();
  	circle(x,y,10,"green");
  	if(rightDown) paddlex+=5;
  	if(leftDown) paddlex-=5;
....
....
(ho cambiato colore per verificare bene il funzionamento) funziona!
La possiamo togliere dal codice e immettere in un file a parte, che va linkato al codice della pagina.
Lo chiamo "funzioniCanvas.js" e lo linko:
<!DOCTYPE html>
<html>
<head>
<script src="funzioni2.js"></script>
<script src="funzioniCanvas.js"></script>

Stessa cosa posso fare con la funzione rect che traccia un rettangolo:

function rect(x,y,w,h){
	ctx.beginPath();
	ctx.rect(x,y,w,h);
	ctx.closePath();
}
In questo caso il codice per il colore non è incluso nella funzione, ma è scritto a parte prima di chiamare la funzione.
Potrei fare la stessa scelta che per la funzione che traccia un cerchio, e includere il colore come parametro...

function rect(x,y,w,h,colore){
	ctx.fillStyle=colore;
	ctx.beginPath();
	ctx.rect(x,y,w,h);
	ctx.closePath();
	ctx.fill();
}
e funziona.
Tolgo questa funzione e la metto nel file di codice a parte.
E funziona!
La funzione clear che ripulisce il canvas consentendo l'effetto di animazione, posso portarla nel file di codice.
Questa però usa due variabili, che porto anch'esse nel file a parte:
function clear(){
	ctx.clearRect(0,0,WIDTH,HEIGHT);
}
E funziona ancora tutto.
Ora ci sono le funzioni che stabiliscono se sia stato premuto il tasto destra o sinistra. Queste mi sembrano sufficientemente generalizzabili da poterle portare nel file a parte: settano delle variabili booleane a seconda che sia stato premuto il tasto freccia destra o freccia sinistra, e affidano queste funzioni agli eventi onkeydown del documento HTML.
Fanno uso di due variabili booleane, che porto anch'esse sul file di codice.
var rightDown=false;
var leftDown=false;

document.onkeydown=OnKeyDown;
document.onkeyup=OnKeyUp;

function OnKeyDown(evt){

	if (evt.keyCode==39) rightDown=true;
	else if(evt.keyCode==37) leftDown=true;	
}

function OnKeyUp(evt){
	if (evt.keyCode==39) rightDown=false;
	else if (evt.keyCode==37) leftDown=false;
}


Ho ridotto quasi all'osso la cosa.
Ma potrei anche portare nel codice le procedure che "prendono possesso" del canvas e del contesto, tanto più che anch'esse usano variabili che vi ho già portato:
function inizia(){
	
	//creiamo la base del contesto.
	canvas=$("canvas");
	ctx=canvas.getContext("2d");
	WIDTH=canvas.width;
	HEIGHT=canvas.height;
....
La riscrivo come InitContext...
function initContext(nome){
	canvas=$(nome);
	ctx=canvas.getContext("2d");
	WIDTH=canvas.width;
	HEIGHT=canvas.height;
}
richiamandola dalla funzione iniziale:
function inizia(){
	
	initContext("canvas");
	
	
	paddlex=WIDTH/2-100;
	paddleh=10;
	paddlew=75;
	
	//funzione setInterval
	intervallo=setInterval(draw,15);
		
}
E quindi la porto sul file di codice insieme alle variabili che usa:
var canvas;
var ctx;

function initContext(nome){
	canvas=$(nome);
	ctx=canvas.getContext("2d");
	WIDTH=canvas.width;
	HEIGHT=canvas.height;
}
e funziona!
Io metterei anche una routine che inizializza tutte le variabili.
Prima dichiaro tutte le variabili, quindi le inizializzo in una funzione apposita:
//coordinate della palla
var x;
var y;
var dx;
var dy;

//coordinate della racchetta
var paddlex;
var paddlew;
var paddleh;

var intervallo;
function initVariables(){
	x=100;
	y=100;
	dx=2;
	dy=4;
	
	paddlew=75;
	paddleh=10;
	paddlex=WIDTH/2-120;
}

sabato 3 gennaio 2015

I codici di colore con lo stroke non combinano...

Prendiamo un ImageData di dimensioni 5 x 5.
Come facciamo?

function inizia(){
 canvas=document.getElementById("myCanvas");
 ctx=canvas.getContext("2d");
 ctx.fillStyle="white";
 ctx.rect(0,0,10,10);
 ctx.fill();

 sfondo=ctx.getImageData(0,0,5,5);
}
Ecco.
Ora stampiamo 20 valori, corrispondenti ai valori dei primi 5 pixel:
function inizia(){

 canvas=document.getElementById("myCanvas");
 ctx=canvas.getContext("2d");
 ctx.fillStyle="white";
 ctx.rect(0,0,10,10);
 ctx.fill();

 sfondo=ctx.getImageData(0,0,5,5);
 for(var i=0;i<20;i++){
  document.write(sfondo.data[i]+" ");
 }
}
Ecco i primi 5 pixel (4 valori per pixel) con un rettangolo delle stesse dimensioni del canvas con uno sfondo bianco:
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 
Ora cambiamo colore al rettangolo: rosso:
function inizia(){

 canvas=document.getElementById("myCanvas");
 ctx=canvas.getContext("2d");
 ctx.fillStyle="red";
 ctx.rect(0,0,10,10);
 ctx.fill();

 sfondo=ctx.getImageData(0,0,5,5);
 for(var i=0;i<20;i++){
  document.write(sfondo.data[i]+" ");
 }
}

255 0 0 255 255 0 0 255 255 0 0 255 255 0 0 255 255 0 0 255 
Verde:
function inizia(){

 canvas=document.getElementById("myCanvas");
 ctx=canvas.getContext("2d");
 ctx.fillStyle="green";
 ctx.rect(0,0,10,10);
 ctx.fill();

 sfondo=ctx.getImageData(0,0,5,5);
 for(var i=0;i<20;i++){
  document.write(sfondo.data[i]+" ");
 }
}
0 128 0 255 0 128 0 255 0 128 0 255 0 128 0 255 0 128 0 255 
Blu:
function inizia(){

 canvas=document.getElementById("myCanvas");
 ctx=canvas.getContext("2d");
 ctx.fillStyle="blue";
 ctx.rect(0,0,10,10);
 ctx.fill();

 sfondo=ctx.getImageData(0,0,5,5);
 for(var i=0;i<20;i++){
  document.write(sfondo.data[i]+" ");
 }
}
0 0 255 255 0 0 255 255 0 0 255 255 0 0 255 255 0 0 255 255 
Nero:
function inizia(){

 canvas=document.getElementById("myCanvas");
 ctx=canvas.getContext("2d");
 ctx.fillStyle="black";
 ctx.rect(0,0,10,10);
 ctx.fill();

 sfondo=ctx.getImageData(0,0,5,5);
 for(var i=0;i<20;i++){
  document.write(sfondo.data[i]+" ");
 }
}
0 0 0 255 0 0 0 255 0 0 0 255 0 0 0 255 0 0 0 255 
Sì, tutto corrisponde.
Ora proviamo con lo stroke piuttosto che col fill.

Sfondo bianco, stroke nero:
function inizia(){

 canvas=document.getElementById("myCanvas");
 ctx=canvas.getContext("2d");
 ctx.fillStyle="white";
 ctx.rect(0,0,10,10);
 ctx.fill();
 
 ctx.strokeStyle="black";
 ctx.beginPath();
 ctx.moveTo(0,0)
 ctx.lineTo(5,0)
 ctx.closePath();
 ctx.stroke();
 sfondo=ctx.getImageData(0,0,5,5);
 for(var i=0;i<20;i++){
  document.write(sfondo.data[i]+" ");
 }
}
64 64 64 255 64 64 64 255 64 64 64 255 64 64 64 255 128 128 128 255 


Ora da sfondo bianco stroke nero cambiamo lo sfondo in rosso:
function inizia(){

 canvas=document.getElementById("myCanvas");
 ctx=canvas.getContext("2d");
 ctx.fillStyle="red";
 ctx.rect(0,0,10,10);
 ctx.fill();
 
 ctx.strokeStyle="black";
 ctx.beginPath();
 ctx.moveTo(0,0)
 ctx.lineTo(5,0)
 ctx.closePath();
 ctx.stroke();
 sfondo=ctx.getImageData(0,0,5,5);
 for(var i=0;i<20;i++){
  document.write(sfondo.data[i]+" ");
 }
}
64 0 0 255 64 0 0 255 64 0 0 255 64 0 0 255 128 0 0 255 


Nero su verde:
function inizia(){

 canvas=document.getElementById("myCanvas");
 ctx=canvas.getContext("2d");
 ctx.fillStyle="green";
 ctx.rect(0,0,10,10);
 ctx.fill();
 
 ctx.strokeStyle="black";
 ctx.beginPath();
 ctx.moveTo(0,0)
 ctx.lineTo(5,0)
 ctx.closePath();
 ctx.stroke();
 sfondo=ctx.getImageData(0,0,5,5);
 for(var i=0;i<20;i++){
  document.write(sfondo.data[i]+" ");
 }
}
0 32 0 255 0 32 0 255 0 32 0 255 0 32 0 255 0 64 0 255 


Nero su blu:
function inizia(){

 canvas=document.getElementById("myCanvas");
 ctx=canvas.getContext("2d");
 ctx.fillStyle="blue";
 ctx.rect(0,0,10,10);
 ctx.fill();
 
 ctx.strokeStyle="black";
 ctx.beginPath();
 ctx.moveTo(0,0)
 ctx.lineTo(5,0)
 ctx.closePath();
 ctx.stroke();
 sfondo=ctx.getImageData(0,0,5,5);
 for(var i=0;i<20;i++){
  document.write(sfondo.data[i]+" ");
 }
}
0 0 64 255 0 0 64 255 0 0 64 255 0 0 64 255 0 0 128 255 
Cerchiamo di trarre delle conclusioni...
  • Nero su bianco: 64 64 64 255
  • Nero su rosso: 64 0 0 255
  • Nero su verde: 0 32 0 255
  • Nero su blu: 0 0 64 255
Buio pesto...