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.
1. Università degli Studi di Trento
Facoltà di Scienze MM.FF.NN
Corso di Laurea Specialistica in Informatica
A.A. 20072008
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. 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
4. ● 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 “supersfera”, 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,
5. 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 powerup, ecc...
6. 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 Autogenerated 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 powerup 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
7. 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)
8. 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)
*/
9. 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.
10. // 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)
{
11. // 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 powerup ,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();
12. 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 powerup 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 powerup.
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.