JOGLnoid
Upcoming SlideShare
Loading in...5
×
 

JOGLnoid

on

  • 416 views

The project described in this report, called affectionately "JOGLnoid", corresponds ...

The project described in this report, called affectionately "JOGLnoid", corresponds 
a clone of the famous arcade game "Arkanoid". The game was developed through the assistance of the OpenGL graphics library.

Statistics

Views

Total Views
416
Views on SlideShare
413
Embed Views
3

Actions

Likes
0
Downloads
1
Comments
0

1 Embed 3

http://www.graphitech.it 3

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

JOGLnoid JOGLnoid Document Transcript

  • Università degli Studi di Trento Facoltà di Scienze MM.FF.NN Corso di Laurea Specialistica in Informatica A.A. 2007­2008  Relazione del progetto del corso di “Principi di Computer Grafica”,  Prof. Raffaele De Amicis, Prof. Giuseppe Conti Massimo Santini  matr.130531 1 Introduzione 1.1 JOGLnoid L'applicazione descritta in questa relazione, denominata simpaticamente “JOGLnoid”, corrisponde  ad un clone del celebre videogioco arcade “Arkanoid”. JOGLnoid, infatti, è un semplice gioco in cui  l'obiettivo principale dell'utente è quello di abbattere un determinato numero di mattoncini, posti  all'interno di una scena bidimensionale, tramite l'uso di una sfera e di una piccola barra di gioco. La  barra serve infatti a far rimbalzare la sfera di gioco contro i mattoncini, evitando che quest'ultima  cada nella porzione di schermo sottostante alla barra stessa, causando così la perdita della partita. Il gioco è stato sviluppato tramite l'ausilio della libreria grafica OpenGL, e premette all'utente di  giocare partite composte da un massimo di 10 livelli, interagendo con l'applicazione tramite l'uso  della tastiera del computer. 2 Componenti principali della finestra principale dell'applicazione 2.1 La schermata di gioco  La finestra di gioco principale, di dimensione 1200x900 pixels,  si compone sostazialmente di due  parti principali: sul pannello di sinistra troviamo la scena di gioco renderizzata tramite OpenGL,  mentre sul pannello di destra è disponibile un'interfaccia utente necessaria per gestire e controllare il  gioco. Figura 1: La schermata principale di JOGLnoid
  • 2.1.1 L'interfaccia di controllo del gioco L'interfaccia utente sulla sinistra della schermata di gioco, permette sostazialmente di effettuare  tutte   quelle   operazioni   necessarie   all'avvio   di   una   parti.   Infatti,   tramite   una   serie   di   pulsanti  etichettati e di menu a tendina, è possibile avviare una nuova partita, selezionandone il livello dal  quale si vuole iniziare, oppure chiuderne a sua volta una già iniziata od, infine, chiudere l'intera  applicazione. Figura 2: L'interfaccia di controllo del gioco Sono inoltre presenti degli indicatori, necessari per visualizzare il punteggio corrente della partita, il  numero di vite rimaste ed ovviamente il livello corrente gioco durante la partita. 2.1.2 La scena di gioco La scena di gioco è visualizzata sulla sinistra della finestra principale, all'interno di un pannello di  dimensioni   600x400 pixels ed è renderizzata tramite l'utilizzo della liberia  Jogl  (binding Java di  OpenGL). Nella porzione superiore della scena vengono rappresentati i mattoncini da abbattere,  colorati in maniera casuale, mentre nella parte inferiore sono presenti la sfera e la barra di gioco,  entrambe colorate con tonalità di grigio. Inoltre il colore di queste componenti presenta  sfumature  (shading) del colore sui vertici. 3 Giocare a JOGLnoid 3.1 Gestione di una partita a JOGLnoid Una   partita   a   JOGLnoid   consiste   consiste   di   10   livelli   di   gioco   ognuno   dei   quali   presenta  caratteristiche differenti. Tuttavia, l'utente non è obbligato ad iniziare necessariamente dall livello 1.  Vi è infatti la possibilità di scegliere il livello iniziale, selezionando la voce corretta nel menù a  tendina relativo ai livelli di gioco. Se tale scelta non viene effettuata si avvia di “default” una partita  dal   livello   1.   Lo   scopo   del   gioco,   come   detto   precedentemente,   è   quello   di   abbattere   tutti   i  mattoncini presenti nella scena, in modo da superare il livello e passare al successivo. Ogni   livello   presenta   un   numero   di   mattoncini   differenti.   In   particolare,   il   numero   totale   dei  mattoncini corrisponde a 160, e ad ogni livello ne vengono disegnati esclusivamente un numero pari  alla percentuale asseganta al livello in questione. Ciò significa che, ad esempio, se si vuole superare 
  • il primo livello sarà necessario abbattere un numero di mattoncini pari al 10% del totale (ovvero 16);  il superamento del secondo invece necessiterà l'abbattimento del 20% del totale e cosi' via, fino al  livello 10, il quale conterrà il 100% dei mattoncini, ovvero in numero pari al totale possibile (160). L'abbattimento di un mattoncino comporta un bonus sul punteggio totale di 10 punti.  Di seguito è mostrato il metodo init_grid(int n_lines, int level)appartenente alla  classe GLRenderer, ovvero il metodo che si occupa di riempire la scena di gioco con il numero  adeguato di mattoncini. /***  * Inizializza la griglia da abbattere  * @param n_lines numero delle linee di mattoncini che compongono la scena   * da abbattere  */ private void init_grid(int n_lines, int level) { box Box; int num_mattoncini; int num_mattoncini_left; double index; //il numero totale dei mattoncini da abbattere per livello num_mattoncini = ((20*8)*(level*10)/100); // numero box per linea int numBxL = (int)(4.0f / 0.50f); STACK_BOX.clear(); for (int i=1; i<=n_lines; i++) { for (int j=1; j<=numBxL;j++) { Box = new box((0.50f*j)­2.25f,((float)­i*(0.10f))+2.05f); STACK_BOX.addElement(Box); num_boxes++; } } //calcolo il num di mattoncini da levare num_mattoncini_left = 160 ­ num_mattoncini; for (int k=0; k < num_mattoncini_left; k++) { index =(double) Math.random() * (STACK_BOX.size()­1); index = Math.round(index); STACK_BOX.removeElementAt((int)index); num_boxes = num_boxes ­ 1; } } È inoltre possibile che un mattoncino abbattuto contenga una specie di sorpresa, ovvero ciò che nel  gioco   originale   è   chiamato   “power­up”.   Questi  power­up,  non   sono   altro   che   degli   oggetti   che  precipitano verso il settore inferiore dell'area di gioco e che vengono eliminati non appaena escono  da quest'ultima. Se uno di questi oggetto viene intercettato tramite la sbarra di gioco, allora il gioco  subisce   alcune   variazioni   che   possono   rivelarsi   in   alcuni   casi   favorevoli   oppure   sfavorevoli  all'utente. I power­up possono essere di cinque tipi diversi, a seconda del colore assegnatogli: ● Rosso: Allarga le dimensioni della barra di gioco, facilitando l'intercettazione della sfera; ● Verde: Restringe le dimensioni della barra di gioco, rendendo l'intercettazione della sfera  più difficile. Rallenta la velocità della sfera di gioco;
  • ● Blu: Aumenta la velocità della sfera; ● Nero: Riempie nuovamente la scena di gioco con un numero di mattoncini pari a quelli   assegnati al livello corrente; ● Viola: Trasforma la sfera di gioco in una “super­sfera”, in grado di abbattere i mattoncini  senza provocare collisioni; Infine, l'abbattimento di tutti i mattoncini, consente il passaggio al livello successivo. Ad ogni partita vengono assegnate un numero di vite pari a tre. Ovviamente, quando la sfera di  gioco finisce nella zona sottostante la barra di gioco, la partita viene fermata e ciò comporta la  perdita di una vita. L'esaurimento di tutte le vite disponibili comporta la fine della partita corrente. 3.2 L'interazione tramite la tastiera Lo   strumento   principale   di   interazione   con   JOGLnoid   è   ovviamente   la   tastiera   del   computer.  Sostanzialmente i tasti necessari al gioco possono essere divisi in due categorie: quelli relativi al  controllo della barra di gioco e quelli relativi all'orientamento della telecamera virtuale. 3.2.1 Tasti per il controllo del gioco I tasti utilizzati per muovere la barra di gioco sono esclusivamente due efanno parte del tastierino  direzionale: ● RIGHT_KEY (  ) : Sposta la barra verso destra; ● LEFT_KEY   (  ) : Sposta la barra verso sinsitra; A questi due tasti va inoltre aggiunta la barra spaziatrice, la quale permette di avviare la partita,  ogni qualvolta questa viene fermata. ● SPACEBAR  : Lancia la pallina; 3.2.2 Tasti per il controllo della telecamera virtuale I tasti in questione permettono di muovere la telecamera virtuale in maniera da poter vedere il gioco  da  punti di  vista differenti. Oltre a ciò, è possibile ruotare la scena di gioco in senso orario  e  antioratio tramite l'utilizzo del mouse, ovvero tenendo premuto il tasto sinistro e trascinando verso  destra o verso sinistra. Tra i tasti di controllo della telecamera virtuale vi sono: ● D_KEY  : Ruota la telecamera verso destra; ● A_KEY  : Ruota la telecamera verso sinistra; ● S_KEY  : Sposta la telecamera indietro; ● W_KEY : Sposta la telecamera in avanti; ● Q_KEY  : Sposta la telecamera in alto; ● E_KEY  : Sposta la telecamera in basso; ● PAG_UP  : Ruota la telecamera verso l'alto; ● PAG_DOWN : Ruota la telecamera verso il basso. 4 Le componenti principali di JOGLnoid La struttua di JOGLnoid è sostanzialmente organizzata in packages (pacchetti). Ad ognuno di questi  è assegnato un compito ben preciso ed ogni package contiene le classi necessarie per il suo scopo. I package di maggiore interesse sono senz'altro gt.events, physics, math, game.components, texture, 
  • texture.loader, custom.events e render. Di seguito verrà proposta una descrizione degli aspetti principali di tali pacchetti. 4.1 Il package gt.events Il   package   gt.events   contiene   le   classi   statiche  GameStatus,  KeyNavigator  e  MovementStatus.  Le ultime due classi citate si occupano esclusivamente di gestire la mappatura dei controlli della  tastiera.   Il   loro   scopo   è   quindi   unicamente   quello   di   interfacciare   la   tastiera   del   computer   con  l'applicazione JOGLnoid. La classe   GameStatus  riveste, invece, un ruolo maggiormente interessante, in quanto le sue  variabli   statiche   si   occupano   di   memorizzare   lo   stato   corrente   del   gioco.   Tutto   ciò   è   utile   dal  momento che, gestendo i vari eventi che accadono durante la partita, questa classe permette sempre  di sapere qual'è lo stato corrente della partita che si sta giocando. Tra queste caratteristiche figurano,  ad esempio, il numero di vite rimaste, il livello corrente, il punteggio corrente ecc...  Di seguito viene mostrata l'implementazione di tale classe. package gt.events; /***  * Classe GameStatus, costituisce un listener che permette ad ApplicationStarter  * di conoscere lo stato del gioco in GLRenderer  * @author Massimo Santini 130531  *  */ public class GameStatus { public static int num_lives = 3; public static int current_level = 1; public static int game_score = 0; public static boolean new_game = false; public static boolean reset = false; public static boolean game_is_working = true; public static boolean setLevelBoxes = false; public static boolean collision_wall_sound = false; public static boolean collision_box_sound = false; public static boolean collision_bar_sound = false; } 4.2 I package texture e texture.loder Questi due package contengono classi dedite esclusivamente alla gestione ed al caricamento delle  texture del gioco. Per un maggiore approfondimento si rimanda al codice sorgente dell'applicazione. 4.3 Il package custom.events JOGLnoid implementa un meccanismo di “design pattern” che permette alla classe  GLRenderer  di generare un evento personalizzato ogni qual'volta accade qualcosa nella scena di gioco. Tale  evento   è   implementato   dalla   classe  RenderEvent,   appartenente   a   questo   package,  e   viene  intercettato   dalla   classe   che   gestisce   l'interfaccia   utente.   Tutto   ciò   permette   all'interfaccia   di  controllo di essere sempre aggiornata sullo stato corrente del gioco, in maniera da poter poi settare i  valori corretti nei suoi indicatori, come ad esempio il punteggio corrente oppure il numero delle vite  rimaste. L'evento personalizzato   RenderEvent contiene alcune proprietà  quali, in particolare,  punteggio  e  lives,   che   vengono   istanziate   al   momento   della   creazione   dell'oggetto   evento.   In  particolare oggetti di questo tipo vengono creati ogni qualvolta accade nella scena di gioco qualcosa  tipo l'abbattimento di un mattoncino, una collisione, un'intercettazione di un power­up, ecc...
  • Di seguito viene presentata l'implementazione della classe  RenderEvent. package custom.events; import java.util.EventObject; public class RenderEvent extends EventObject { int punteggio; int lives; boolean wall_sound = false; boolean box_sound = false; boolean bar_sound = false; public RenderEvent(Object source, int points, int lives_left, boolean  w_sound, boolean b_sound, boolean br_sound) { super(source); // TODO Auto­generated constructor stub punteggio = points; lives = lives_left; wall_sound = w_sound; box_sound = b_sound; bar_sound = br_sound;  } public int getPoints() { return punteggio; } public int getLivesLeft() { return lives; } public boolean getWall_sound() { return wall_sound; } public boolean getBox_sound() { return box_sound; } public boolean getBar_sound() { return bar_sound; } } 4.4 Il package game.components Il   package   game.components   include   tutte   quelle   classi   che   permettono   di   creare   le   varie  componenti   grafiche   del   gioco,   quali   la   sfera,   la   barra   di   gioco,   i   power­up   e   i   mattoncini   da  abbattere. Ognuna delle precedenti componenti è implementata rispettivamente dalle classi Ngon,  bar, box e surprise. In   particolare   ognuno   di   queste   classi   implementa   il   metodo  draw(GLAutoDrawable  gLDrawable),il   quale   permette   di   disegnare   l'oggetto   in   questione   all'interno   della   scena   di  gioco. Di particolare interesse è la classe che implementa la sfera di gioco, descritta nel paragrafo  4.4.1. Infine è necessario sottolineare che ognuna delle componenti descritte fino ad ora presenta  alcune   proprietà   particolari,   chiamate  bound_box[]:   queste   proprietà   permettono   di   definire  quella regione relativa all'oggetto da disegnare, detta bounding box, la quale risulta necessaria per la  gestione delle collisioni tra gli oggetti stessi.  4.4.1 La classe Ngon La   classe  Ngon  implementa   la   sfera   di   gioco.   Tale   sfera   viene   implementata   disegnando   una  particolare primitiva geometrica, ovvero un poligono di dimensione n, che viene poi fatto ruotare di 
  • 360°     lungo   l'asse  y.    In   questa   maniera   è   possibile   ottenere   un'approssimazione   di   una   sfera  abbastanza precisa. Tale precisione dipende ovviamente dal numero di lati che possiede il poligono  ed in questa applicazione si è scelto di utilizzare un decagono (poligono di 10 dimensioni). Nelle  prossime righe è mostrata la procedura si occupa della creazione della sfera di gioco. for (int j=0; j<=360.0f; j++) { gl.glRotatef(j, 0.0f, 1.0f, 0.0f); gl.glBegin(GL.GL_POLYGON); for (int i=0; i<=size; i++) { if (red) { gl.glColor3f(1.0f, 0.0f, 0.0f); } else { gl.glColor3f(0.6f,0.6f,0.6f); } gl.glVertex2d(radius*(Math.cos(angle_inc*i)),radius*(Math.sin(angle_inc*i))); } gl.glEnd(); j++; }   4.5 Il package physics Il package  physics  contiene un'unica classe, nominata  collision. Tale classe si occupa della  gestione delle collisioni fra gli oggetti della scena.  Una collisione viene rilevata sostanzialmente quando la bounding box   della sfera di gioco “entra”  letteralmente nella bounding box dell'oggetto con cui sta per collidere. In particolare, la classe collision implementa otto diversi metodi, ognuno dei quali è preposto  ad individuare un particolare tipo di collisione. Questi metodi sono: ● isCollidingWithSXWall(Ngon ball) Questo metodo si occupa di rilevare eventuali collisioni della sfera con la delimitazione  sinistra dell'area di gioco; ● isCollidingWithDXWall(Ngon ball) Questo metodo si occupa di rilevare eventuali collisioni della sfera con la delimitazione  destra dell'area di gioco; ● isCollidingWithUpperWall(Ngon ball) Questo metodo si occupa di rilevare eventuali collisioni della sfera con la delimitazione  superiore dell'area di gioco; ● isCollidingWithGameBar(Ngon ball, bar gameBar) Questo metodo si occupa di rilevare le collisioni della sfera con la barra di gioco; ● isCollidingWithBoxRight(Ngon ball, box mattoncino) Questo metodo si occupa di rilevare le collisioni della sfera con un mattoncino, nel caso in  cui la sfera lo colpisca sul suo lato destro; ● isCollidingWithBoxLeft(Ngon ball, box mattoncino) Questo metodo si occupa di rilevare le collisioni della sfera con un mattoncino, nel caso in  cui la sfera lo colpisca sul suo lato sinistro; ● isCollidingWithBoxUp(Ngon ball, box mattoncino)
  • Questo metodo si occupa di rilevare le collisioni della sfera con un mattoncino, nel caso in  cui la sfera lo colpisca sul suo lato superiore; ● isCollidingWithBoxDown(Ngon ball, box mattoncino) Questo metodo si occupa di rilevare le collisioni della sfera con un mattoncino, nel caso in  cui la sfera lo colpisca sul suo lato inferiore; Ognuno   dei   metodi   visti   sopra   ritorna   un   valore   di   tipo   booleano   il   quale   determina,   in   caso  affermativo, l'avvenuta collisione con l'oggetto in questione.  Per quanto riguarda le collisioni della sfera con le delimitazioni dell'area di gioco, la condizione che  determina l'avvenuta collisione è determinata da un controllo sulle coordinate della   bounding box  della   sfera;   di   fatto,   non   appena   tali   coordinate   oltrepassano   i   valori   delle   coordinate   delle  delimitazioni, viene rilevato un urto con una di quest'ultime e si procede ad inserire sullo stack la  matrice di riflessione relativa al tipo di urto, in maniera da modificare adeguatamente la traiettoria  della sfera durante il gioco. Le seguenti righe di codice, ad esempio, mostrano come viene gestita  un'ipotetica collisione con la delimitazione superiore della scena di gioco. /***  * Controlla se avviene una collisione con la delimitazione superiore  * dell'area di gioco  * @param ball pallina del gioco  * @return true or false valore di ritorno (vero o falso)  */ public static boolean isCollidingWithUpperWall(Ngon ball) { // La sfera di gioco collide con la delimitazione superiore  dell'area di gioco if (ball.bound_box[3] >= 2.0f) { //System.out.println("collisione soffitto"); return(true); } else { return(false); } } Le   collisioni   con   i   mattoncini   invece,   vengono   trattate   in   modo   simile,   anche   se   sono   stati  introddotti alcuni controlli necessari, soprattutto per identificare con quale lato del mattoncino si sta  collidendo. Sostanzialmente l'idea fondamentale consiste nel verificare quale dei lati della bounding  box della sfera sta “letteralmente” entrando all'interno dell'area del mattoncino in questione. Una  volta determinato quale lato della bounding box della sfera sta per collidere, si procede ad inserire  sullo   stack   la   matrice   di   riflessione   associata   a   quel   particolare   tipo   di   urto,   modificando  opportunamente la traiettoria della sfera. Inoltre, viene introdotto un'ulteriore controllo che assicuri che la sfera stia per collidere con un  particolare mattoncino e non con altri, posti nella scena di gioco, le cui coordinate delle bounding  boxes potrebbero potenzialmente rendere valida la condizione di controllo sulla collisione. Questo  impedisce che, ad esempio, nel caso di una collisione sul lato sinistro di un mattoncino, vengano  eliminati tutti quei mattoncini che si trovano più a sinistra del mattoncino in questione. Di seguito è  riportato un'esempio di gestione di un urto con il lato sinistro di un mattoncino del gioco. /***  * Controlla se avviene una collisione con il lato sinistro di un   * mattoncino  * @param ball pallina del gioco  * @param mattoncino mattoncino su cui viene valutata la collisione  * @return true or false valore di ritorno (vero o falso)  */
  • public static boolean isCollidingWithBoxLeft(Ngon ball, box mattoncino) { if ( (ball.bound_box[1]>=mattoncino.bound_box[0])& (ball.bound_box[1]<mattoncino.bound_box[1])& ((ball.getY()<mattoncino.bound_box[3])&(ball.getY()>mattoncino.bound_box[2])) ) { System.out.println("isCollidingWithBoxLeft"); return(true); } else  { return(false); } } 4.6 Il package math Il package  math  contiene due  classi che permettono l'utilizzo, dal punto di vista matematico di  matrici numeriche. In particolare queste ultime servono per la gestione di tutte quelle matrici che  permettono a JOGLnoid di muovere tutte le sue componenti grafiche. Fanno parte di questo package  le classi matrix e matrixOperations. La prima classe implementa la struttura di una matrice  numerica di dimensione 4x4, con in aggiunta alcuni metodi di servizio che, ad esempio, permettono  di modificare valori in determinate posizioni della matrice oppure di trasformare quest'ultima in una  matrice identità. La classe  matrixOperations, invece, si occupa esclusivamente, di calcolare il prodotto fra due  matrici. 4.7 Il package render Quest'ultimo package costituisce il motore grafico vero e proprio di JOGLnoid. Infatti contiene la  classe GLRenderer, il cui unico scopo è quello di disegnare gli oggetti nella scena di gioco. La scena di gioco è disegnata all'interno di un frustum ottenuto da un volume di vista creato con  un'angolo   di   apertura   di   45°,     dove   il  front   clipping   plane  e   il  far   clipping   plane  posti  rispettivamente a distanza di 1 unità e 800 unità dall'origine del volume di vista. Questa   classe   presenta   molteplici   aspetti   interessanti;   tra   questi   è   tuttavia   utile   soffermarsi   sul  metodo display(GLAutoDrawable gLDrawable), nel quale vi risiedono tutte le istruzioni  per il disegno degli oggetti nella scena.  Per quanto riguarda il movimento della sfera, questo avviene scaricando utilizzando uno stack sul  quale vengono caricate in maniera opportuna le matrici di traslazione o di riflessione. Il movimento  è quindi dato da una serie di trasformazioni affini che permettono di disegnare la sfera nelle sue  coordinate corrette. Infatti, ogni qual volta è necessario traslare la sfera, si procede all'inserimento  sullo stack di una matrice di traslazione, con i relativi valori settati opportunamente. Analogamente  un   urto   comporta   la   riflessione   della   traiettoria   della   sfera,   e   ciò   viene   effettuato   inserendovi  un'apposita  matrice di riflessione. Ovviamente questi inserimenti sono condizionati dai risultati  delle operazioni di rilevamento delle collisioni, le quali determinano la traiettoria che la sfera di  gioco deve seguire. Le righe di codice che seguono mostrano ad esempio, la fase di controllo di una possibile collisione  della sfera con la delimitazione superiore di un mattoncino. In caso affermativo, la corrsipondente  matrice di riflessione viene caricata sullo stack.
  • // Check delle collisioni con i mattoncini for (int j=0;j<STACK_BOX.size();j++) { tmp_box = STACK_BOX.get(j); xsorp = tmp_box.getX(); ysorp = tmp_box.getY(); if (collision.isCollidingWithBoxDown(ball, tmp_box)) { if (tmp_box.surprise == 1.0f) { creasorpresa(xsorp, ysorp); } YReflectionMatrix.setIdentity(); YReflectionMatrix.setIndex(1, 1, ­1.0f); if (collision_srp) { STACK_MATRIX.addElement(YReflectionMatrix); } GameStatus.collision_box_sound = true; GameStatus.game_score = GameStatus.game_score + 10; fireRenderEvent(); STACK_BOX.remove(j); } Al momento del disegno vero e proprio, tale stack viene scaricato e moltiplicando la matrice di  trasformazione corrente con quella in cima allo stack, siamo in grado di ottenere la matrice che  determinerà le coordinate in cui la sfera dovrà essere disegnata. Di seguito è mostrata la procedura  che si occupa di tale attività. //svuoto lo stack e disegno la pallina for (int k=0; k<STACK_MATRIX.size(); k++) { tmp = STACK_MATRIX.get(k); currentMatrix =  matrixOperations.multiplyMatrix(currentMatrix,tmp); ball.setX(currentMatrix.getValue(0, 2)); ball.setY(currentMatrix.getValue(1, 2)); ball.draw(gLDrawable); tmp.setIdentity(); } if (game_started) { STACK_MATRIX.clear(); } Inoltre una serie di  flag  booleane permette di controllare il flusso attraverso il codice durante il  disegno dei frames, a seconda che ci si trovi in una situazione in cui il gioco è fermo o viceversa. Un'altro aspetto interessante di questa classe è rappresentato dalle righe che seguono: /***  * Permette il firing di eventi dalla classe GLRenderer  *  */ protected synchronized void fireRenderEvent() { RenderEvent evt = new RenderEvent(this, GameStatus.game_score,  GameStatus.num_lives, GameStatus.collision_wall_sound,  GameStatus.collision_box_sound, GameStatus.collision_bar_sound); Object[] listeners = RenderEventListeners.getListenerList(); // loop through each listener and pass on the event if needed int numListeners = listeners.length; for (int i = 0; i<numListeners; i+=2)  { if (listeners[i] == RenderEventListener.class)  {
  • // pass the event to the listeners event dispatch method ((RenderEventListener)listeners[i +1]).RenderEventReceived(evt); }             } } Le  righe  precedenti implementano il metodo  fireRenderEvent(), ovvero quella possibilità  descritta   precedentemente,   che   permette   alla   classe  GLRenderer  di   creare   un   evento  personalizzato ogni qualvolta qualcosa accade nella scena di gioco. Un'altro   aspetto   interessante   è   rappresentato   dalla   gestione   dei   cosiddetti  power­up   ,descritti  precedentemente. La loro intercettazione è ovviamente un'altro tipo di collisione che deve essere  gestito. Di seguito è mostrata la parte di codice dedita a tale operazione. //   controllo   eventuali   collisioni   con   la   barra   o   la   necessita'   di   eliminare  sorprese if (((tmp_sorpresa.bound_box[2] <=  ­1.80f)&(tmp_sorpresa.bound_box[3] >= ­1.90f))  && ((tmp_sorpresa.getX() <= gameBar.bound_box[1]) &(tmp_sorpresa.getX() >=  gameBar.bound_box[0]))) { System.out.println(tmp_sorpresa.surprise_type); switch ((int)tmp_sorpresa.surprise_type) { case 0: //ricreo tutti i mattoncini da abbattere init_grid(num_lines, GameStatus.current_level); collision_srp = true; gameBar.setDefaultWidth(); ball_speed_Y =  0.07f; ball.setColorGray(); GameStatus.game_score = GameStatus.game_score +  20; break; case 1: //allargo la barra collision_srp = true; gameBar.setWidth(0.40f); ball_speed_Y = 0.07f; ball.setColorGray(); GameStatus.game_score = GameStatus.game_score + 1; break; case 2: //aumento la velocita' collision_srp = true; gameBar.setDefaultWidth(); ball_speed_Y =  0.09f; ball.setColorGray(); GameStatus.game_score = GameStatus.game_score + 3; break; case 3: //restringo la barra collision_srp = true; gameBar.setWidth(0.15f); ball_speed_Y = 0.05f; ball.setColorGray(); GameStatus.game_score = GameStatus.game_score + 10; break; case 4: collision_srp = false; gameBar.setDefaultWidth(); ball_speed_Y = 0.07f; ball.setColorRed();
  • GameStatus.game_score = GameStatus.game_score + 5; break; } fireRenderEvent(); STACK_SURPRISE.removeElementAt(d); } tmp_sorpresa.draw(gLDrawable); } La condizione descritta nel primo if  determina se la sorpresa in questione debba essere disegnata  oppure no, mentre il membro alla destra dell'operatore condizionale && rileva un'eventuale  collisione del power­up con la barra di gioco . In caso affermativo di procede alla valutazione del  tipo di sorpresa che è stato intercettato, modificando di conseguenza i relativi parametri con i valori  assegnati al  power­up. 5 Note tecniche sullo sviluppo dell'applicazione JOGLnoid JOGLnoid  è stato sviluppando in  JAVA, utilizzando l'ambiente di sviluppo Eclipse, ed il binding  OpenGL per JAVA, denominato Jogl. L'intera applicazione inoltre è stata creata su piattaforma i386,  in ambiente GNU/Linux.