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...