Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Gestire le collisioni in Canvas con Processing.js

Gestire le collisioni tra oggetti grafici effettuando controlli sulle relative posizioni
Gestire le collisioni tra oggetti grafici effettuando controlli sulle relative posizioni
Link copiato negli appunti

Nelle scorse lezioni abbiamo visto come far muovere degli oggetti (che rappresentano i tweet) incolonnati a seconda dell'appartenenza ad alcuni slot (che rappresentano la lingua in cui i tweet sono scritti). Dobbiamo ancora risolvere il problema delle collisioni.

La tattica potrebbe incentrarsi sul controllare la posizione del tweet corrente rispetto a tutti i tweet precedenti sul suo slot di appartenenza cercando una eventuale sovrapposizione. In caso positivo potremmo allontanare tra loro i tweet risolvendo in questo modo la collisione.

Per prima cosa operiamo sulla classe Slot aggiungendo una funzione inc che accetta un tweet in ingresso e lo accoda ad un array, ecco come appare la classe dopo queste modifiche:

// la classe che rappresenta la lingua
class Slot{
	String language;
    int index;
    int pos_x;
    ArrayList tw; // array di tweet associati a questo slot
	Slot(lan){
    	language = lan;
        index = slots.size();
        prev_slot = slots.get(index-1);
        tw = new ArrayList(); //inizializzo l'array
        pos_x = ( prev_slot == null ? 0 : prev_slot.get_pos_x(prev_slot.last_col) ) + box_w + slot_gap; slots.add(this);
    }
	void inc(tweet){
    	tw.add(tweet); // aggiungo il tweet
    }
	int get_pos_x(){ return pos_x;}
}

Bene, adesso creiamo una variabile slot_index che memorizzi, per ogni tweet, la sua posizione nello slot di appartenenza, modifichiamo quindi la classe Tweet:

class Tweet{
	HashMap tweet;
	int index;
	Slot slot;
	float pos_y, vel_y;
	int slot_index // conterrà la posizione del tweet nello slot di appartenenza
	Tweet(HashMap data, i){
		tweet = data;
		pos_y = brick_start; vel_y = 0;
		slot = getSlot(data.iso_language_code) || new Slot(data.iso_language_code);
		index = i;
		slot.inc(this); // aggiungo il tweet allo slot
        slot_index = slot.tw.size() - 1; // memorizzo la posizione del tweet nello slot
	}
	// ... resto della classe ...

Ora riscriviamo il metodo draw della classe Tweet in modo che effettui il controllo di sovrapposizione di cui abbiamo precedentemente parlato e, in caso di collisione, aggiusti la posizione del tweet.

void draw(){
	boolean update = true; // true = incrementa la posizione; false = stai fermo 
    for(int k=0; k < slot_index; k++){ // per ogni tweet precedente nello stesso slot
		Tweet tw = (Tweet) slot.tw.get(k);
		if( abs( pos_y - tw.pos_y) < (box_h+2)){ // se esiste sovrapposizione
	        pos_y = tw.pos_y - (box_h+1); //allontana il tweet eliminando la collisione
    	    update = false; // non incrementare la posizione del tweet
			break; // esci dal ciclo
		}
	}
	rect(slot.get_pos_x(), pos_y, box_w, box_h); // disegna il tweet in ogni caso
    if(update && pos_y < brick_end) { // se update=true e se non ha raggiunto il fondo
		vel_y += gravita; //aggiorna la velocità
		pos_y += vel_y; // aggiorna la posizione
	}
}

Aggiorniamo il progetto all'interno del browser e osserviamo il risultato:

Figura 6. I mattoncini impilati grazie alla gestione delle collisioni

(clic per ingrandire)


I mattoncini impilati grazie alla gestione delle collisioni

Se lasciamo scorrere l'animazione per un sufficiente periodo di tempo notiamo però che non esiste nessun meccanismo che impedisca ad una colonna di crescere a dismisura, al punto di evadere dai confini del
Canvas.

Per risolvere questo problema è sufficiente implementare un ulteriore controllo sullo slot che spezzi la colonna, raggiunto un determinato numero di elementi. Iniziamo dalla classe Slot aggiungendo una variabile last_col per memorizzare il numero di colonne già utilizzate dallo slot:

class Slot{
	String language;
    int index;
    int pos_x, last_col;
    ArrayList tw;
    Slot(lan){
    	last_col = 0; // inizializziamo la variabile a 0
	// ... resto della classe ...

All'aggiunta di ogni tweet dobbiamo ora controllare se sia o meno necessario aggiungere una nuova colonna allo slot e, in caso affermativo, incrementare last_col e spostare la posizione di tutti gli slot successivi a questo per far spazio alla nuova colonna. Riscriviamo quindi il metodo inc facendo in modo che applichi questo algoritmo e ritorni al tweet un indice della colonna nel quale è stato posizionato:

int inc(tweet){
	// impostiamo il numero di tweet per colonna a 20
    if(floor(tw.size() / 20) > last_col) { // ci servono più colonne delle attuali?
	  for(int k = index+1; k < slots.size(); k++ ) // per ogni slot successivo a questo
            slots.get(k).pos_x += (box_w + col_gap); // incrementiamo la pos_x
	  last_col ++; // incrementiamo last_col
    }
	tw.add(tweet);
	return last_col; // ritorniamo al tweet la sua colonna di appartenenza
}

Avendo introdotto questo nuovo concetto di 'colonna di appartenenza' dobbiamo modificare la funzione
get_pos_x in modo che tenga conto della colonna nella quale è stato posizionato il tweet:

int get_pos_x(col){
	return pos_x + (box_w + col_gap) * col;
}

Ora non ci resta che modificare la classe Tweet in modo che tenga conto della 'colonna di appartenenza'. Creiamo quindi una variabile per lo scopo ('col_x') e valorizziamola all'interno del costruttore:

class Tweet{
	// ... tutte le precedenti dichiarazioni ...
	int col_x; // conterrà l'indice della colonna assegnata a questo tweet
	Tweet(HashMap data, i) {
		tweet = data;
        pos_y = brick_start;
        vel_y = 0;
        slot = getSlot(data.iso_language_code) || new Slot(data.iso_language_code);
        index = i;
        col_x = slot.inc(this); // memorizzo la colonna
        slot_index = slot.tw.size() -1;
	}
	// ... resto della classe ...

Infine dobbiamo tenere conto di 'col_x' all'interno del metodo draw della classe Tweet, in particolare è necessario modificare la chiamata a get_pos_x e aggiungere un controllo sulla collisione che verifichi che i due tweet si trovino sulla medesima colonna:

void draw(){
	boolean update = true;
    for(int k=0; k < slot_index; k++){
		Tweet tw = (Tweet) slot.tw.get(k); // aggiungiamo un check sulla colonna
		if( abs( pos_y - tw.pos_y) < (box_h+2) && col_x == tw.col_x){
        	pos_y = tw.pos_y - (box_h+1);
            update = false;
            break;
		}
	} // passiamo la colonna alla funzione 
	get_pos_x rect(slot.get_pos_x(col_x), pos_y, box_w, box_h);
    if(update && pos_y < brick_end){
        vel_y += gravita;
        pos_y += vel_y;
    }
}

Visualizziamo il progetto all'interno del browser per verificare il corretto funzionamento di questo ultimo
avanzamento:

Figura 7. Mattoncini su colonne multiple

(clic per ingrandire)


Mattoncini su colonne multiple

Con questo abbiamo concluso legata alla dinamica dell'infografica, ora possiamo passare alla
gestione del colore.

Ti consigliamo anche