View Full Version : [CICLO 4] Storia 1
Storia: Introduzione di una temporizzazione fissata per il movimento della gemma.
Occorre fare in modo che, alla rapida pressione delle frecce direzionali sinistra e destra, la gemma si sposti di una e una sola casella. Tenendo premuti i tasti in questione, la gemma continuerà a spostarsi nella direzione corrispondente di una casella per ogni decimo di secondo, a meno di collisioni con i bordi laterali.
Come in precedenza, alla pressione contemporanea dei tasti sinistra e destra, la gemma non dovrà spostarsi in nessuna delle due direzioni, e dovrà comunque continuare la sua caduta verticale (a meno di collisione con il bordo inferiore).
In secondo luogo, come sfondo della schermata, l'engine del gioco disegnerà una immagine fissa in jpg, di dimensione 800x600 e profondità di colore 24 bit.
Punti cardine da tenere a mente durante i lavori:
Mai fare a gara a chi finisce il task per primo, meglio procedere con calma, altrimenti perderemo molto più tempo in seguito
Evitiamo di complicarci la vita, esiste di certo una soluzione più semplice di quella che abbiamo pensato di implementare
MAI aggiungere elementi non richiesti esplicitamente dai task: se mai serviranno, se ne parlerà nelle prossime storie
Comunichiamo il più possibile, se qualcosa non è chiaro discutiamone tutti i dettagli fino ad eliminare ogni dubbio, anche il più insignificante
Task:
4.1.1: Disegnare l'immagine Jpg dello sfondo alle coordinate 0x0 di dimensione 800x600 pixel dietro a tutti gli altri elementi presenti nella finestra. (cover: completo)
4.1.2: Creare un timer molto preciso che sia in grado di misurare il trascorrere del tempo con una precisione pari ad almeno 5 millesimi di secondo. (BlueDragon: completo)
4.1.3: Sincronizzare il disegno di ogni frame in ad 1/50 di secondo aggiungendo un loop che attende il termine dei 20 millisecondi previsti per il disegno di un intero frame prima di lasciare il controllo. (71104: completato)
4.1.4: Input deve ignorare la pressione di tasti uguali e consecutivi se l'intevallo tra loro è inferiore ad 1/50 di secondo segnalando solo la pressione del primo. (^TiGeRShArK^: completato)
4.1.5: Refactoring. Aggiungere a drawable la capacita di specificare il layer su cui disegnare i vari oggetti. (VICIUS: completo)
4.1.6: Refactoing della classe Game. spostare tutta la creazione nel costruttore e creare un gameLoop come si deve.
ciao ;)
BlueDragon
01-11-2005, 15:47
Task:
4.1.3: Sincronizzare il disegno di ogni frame in ad 1/50 di secondo aggiungendo un loop che attende il termine dei 20 millisecondi previsti per il disegno di un intero frame prima di lasciare il controllo.
Scusa Vicius ma perché c'è questo task che impone un framerate? In quello che ha scritto Jocchan non c'è e non abbiamo bisogno di vincolare il disegno dei frame ad una certa frequenza per ottenere la temporizzazione del movimento della gemma.
Inoltre, nel 4.1.4, l'intervallo da ignorare non è un 1/10? Jocchan ha parlato di spostamenti di massimo 1 casella ogni decimo di secondo, non ogni 50esimo :)
Scusa se rompo ma meglio chiarire subito :D
La tua obiezione e' giusta.
La Storia non e' implementabile esattamente come scritta, quindi abbiamo deciso di cambiare alcuni dettagli per semplificarci la vita nei task. Uno di questi dettagli e' il tempo di spostamento da una casella e l'altra che invece di essere 1/10 di secondo esatto, sara' qualcosa di leggermente diverso. Alla fine del Ciclo 4 il Customer decidera' se il tempo sara' di suo gradimento oppure vorra' delle variazioni.
Dato questo, abbiamo deciso che sara' piu' semplice implementare quanto richiesto sincronizzando il gioco al cinquantesimo e abbiamo aggiunto il task relativo.
io vorrei suggerire un'altra maniera (secondo me migliore) di temporizzare il gioco, premesso che il gioco deve essere temporizzato perché non può girare a velocità diverse su computers diversi.
secondo me Input deve tenere un flag per ogni tasto che intercetta che indica se il tasto al momento è alzato o abbassato (e questo già succede) e poi bisogna introdurre un timer (non so come si possa fare in Java...) con una certa frequenza e reagire ad ogni scatto del timer reagendo alla pressione dei tasti attualmente abbassati e disegnando un frame. non ho capito se questo corrisponde a quello che si è progettato in questa storia, ma non mi sembra...
inoltre sarebbe anche bene implementare qualcosa di simile a quello che succede in Win32 coi messaggi WM_TIMER, i quali non vengono immessi nella coda se il timeout è scaduto ma l'ultimo messaggio della coda è un altro WM_TIMER che l'applicazione deve ancora processare.
secondo me la Sleep deve essere evitata come la peste, a meno che non si lavori in multithreading.
Scusa Vicius ma perché c'è questo task che impone un framerate? In quello che ha scritto Jocchan non c'è e non abbiamo bisogno di vincolare il disegno dei frame ad una certa frequenza per ottenere la temporizzazione del movimento della gemma.
Inoltre, nel 4.1.4, l'intervallo da ignorare non è un 1/10? Jocchan ha parlato di spostamenti di massimo 1 casella ogni decimo di secondo, non ogni 50esimo :)
Scusa se rompo ma meglio chiarire subito :D
In questo dettaglio sono stato volutamente vago, ho solo dato un tempo di riferimento, in modo che poi fosse possibile scegliere l'implementazione più adeguata per la temporizzazione.
In questo caso, anche se apparentemente un pò ci allontaniamo dalla storia, alla fine il risultato sarà molto simile a quello desiderato (a parte il tempo che - come ho detto poco fa - è solo indicativo).
Il customer non sa in che modo sono fisicamente implementate le varie feature, ed al termine del ciclo si limiterà a valutare se il risultato coincide o no con quello desiderato ;)
Task:
4.1.1: Disegnare l'immagine Jpg dello sfondo alle coordinate 0x0 di dimensione 800x600 pixel dietro a tutti gli altri elementi presenti nella finestra.
Ci provo.. ^^
Primo task, non mi picchiate se non va bene qualcosa :sofico:
Tempo previsto: 1 giorno
La jpg sarà il file back000.jpg, che in questo momento Antares88 sta committando.
@Vicius: per favore potresti aggiungere questo task, prima del 4.1.1?
Task 4.1.0: Creare uno sfondo 800x600 in jpg, con nome del file back000.jpg
Il task in questione è già completato da Antares ;)
BlueDragon
01-11-2005, 17:27
Task:
4.1.2: Creare un timer molto preciso che sia in grado di misurare il trascorrere del tempo con una precisione pari ad almeno 5 millesimi di secondo.
Mi prenoto per il 4.1.2.
La risposta è System.currentTimeMillis().
Va bene? Ho vinto qualche cosa? :sofico:
Dalla documentazione Java, classe System:
public static long currentTimeMillis()
Returns the current time in milliseconds. Note that while the unit of time of the return value is a millisecond, the granularity of the value depends on the underlying operating system and may be larger. For example, many operating systems measure time in units of tens of milliseconds.
See the description of the class Date for a discussion of slight discrepancies that may arise between "computer time" and coordinated universal time (UTC).
Returns:
the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
See Also:
Date
L'unico limite è il sistema operativo sottostante, ma non credo sia un limite aggirabile in altro modo.. :)
L'unico limite è il sistema operativo sottostante, ma non credo sia un limite aggirabile in altro modo.. :) in Windows esistono anche i timers ad alta risoluzione che arrivano ben al di sotto la decina di millisecondi; se alla Sun vedono la decina di ms come un limite sicuramente avranno usato i timers ad alta risoluzione per implementare la JVM per Windows.
Ci provo.. ^^
Primo task, non mi picchiate se non va bene qualcosa :sofico:
Tempo previsto: 1 giorno
Ok. è tutto tuo.
ciao ;)
La jpg sarà il file back000.jpg, che in questo momento Antares88 sta committando.
@Vicius: per favore potresti aggiungere questo task, prima del 4.1.1?
Task 4.1.0: Creare uno sfondo 800x600 in jpg, con nome del file back000.jpg
Il task in questione è già completato da Antares ;)
NO. fek a detto che non li devo scrivere i task di voi grafici. Siete troppo veloci :p
ciao ;)
BlueDragon
01-11-2005, 17:56
in Windows esistono anche i timers ad alta risoluzione che arrivano ben al di sotto la decina di millisecondi; se alla Sun vedono la decina di ms come un limite sicuramente avranno usato i timers ad alta risoluzione per implementare la JVM per Windows.
Sì, infatti usando currentTimeMillis su Windows mi segnala fino al singolo millisecondo :)
Mi prenoto per il 4.1.2.
La risposta è System.currentTimeMillis().
Va bene? Ho vinto qualche cosa? :sofico:
Dalla documentazione Java, classe System:
L'unico limite è il sistema operativo sottostante, ma non credo sia un limite aggirabile in altro modo.. :)
Vediamo che cosa scappa fuori.
ciao ;)
NO. fek a detto che non li devo scrivere i task di voi grafici. Siete troppo veloci :p
ciao ;)
Più che veloci, ci siamo portati avanti con il lavoro :stordita:
Mi prenoto per il 4.1.2.
La risposta è System.currentTimeMillis().
Va bene? Ho vinto qualche cosa? :sofico:
L'unico limite è il sistema operativo sottostante, ma non credo sia un limite aggirabile in altro modo.. :)
Chiaramente non e' un task testabile, pero' voglio vedere una classe Wrapper che ci permetta, se necessario, di cambiare l'implementazione del Timer. Overengineering? No, Facade Pattern dai Design Pattern :)
Il metodo getTime() della classe deve darmi il tempo in millisecondi passato dalla creazione della sua istanza.
io vorrei suggerire un'altra maniera (secondo me migliore) di temporizzare il gioco, premesso che il gioco deve essere temporizzato perché non può girare a velocità diverse su computers diversi.
Per ora andiamo avanti con la soluzione richiesta dai task per due motivi:
1) perche' l'ho gia' fatto almeno venti volte e so che funziona :)
2) perche' so che funziona perche' l'ho gia' fatto almeni venti volte :)
Ed infine perche' sono letteralmente tre righe di codice da implementare, e' semplice, e non richiede nessuna infrastruttura per registrare callback e scatenare eventi di sincronizzazione.
Task:
4.1.1: Disegnare l'immagine Jpg dello sfondo alle coordinate 0x0 di dimensione 800x600 pixel dietro a tutti gli altri elementi presenti nella finestra. (cover: 1 giorno)
Test ( :sperem: ):
public class TestBackground extends TestCase
{
private Background background;
public void setUp()
{
background = new Background("back000", ".jpg");
}
public void testBackgroundPosition()
{
assertEquals("X origin must be 0",
0F, background.getX());
assertEquals("Y origin must be 0",
0F, background.getY());
}
public void testBackgroundWidthAndHeight()
{
assertEquals("Width must be equal to the width of the window",
Game.getWindowWidth(), background.getWidth());
assertEquals("Height must be equal to the height of the window",
Game.getWindowHeight(), background.getHeight());
}
public void testBackgroundSpriteNotNull()
{
assertNotNull(background.getSprite());
}
public void testDraw()
{
MockEngine engine = new MockEngine();
background.draw(engine);
assertEquals(1, engine.getNumberOfQuadsDrawn());
}
}
A proposito, a chi devo chiedere l'user/pwd per il commit? fek? vicius? :rolleyes:
Bene! Hai anche aggiunto il background alla classe Game per vederlo in gioco?
Bene! Hai anche aggiunto il background alla classe Game per vederlo in gioco?
Sì, ho già finito tutto ^^
Ho anche spostato la griglia per farla stare dentro il riquadro dello sfondo di esempio. Inoltre c'era anche un problema nel disegnare la griglia in game.
Era usato: new Rectangle(TOP, LEFT, WIDTH+LEFT, HEIGTH+TOP)
quando invece dev'essere: new Rectangle(LEFT, TOP, WIDTH+LEFT, HEIGTH+TOP)
chi mi passa via pm l'autenticazione per il commit? :rolleyes:
fek? :Prrr:
Ti ho passato i dati in MSN.
scusa cover, ma da te in locale funziona? da me (su linux) esce il seguente errore:
Exception in thread "main" org.lwjgl.opengl.OpenGLException: Invalid value (1281)
at org.lwjgl.opengl.Util.checkGLError(Util.java:56)
at org.lwjgl.opengl.Display.update(Display.java:567)
at it.diamonds.engine.DisplayImpl.update(DisplayImpl.java:65)
at it.diamonds.engine.Engine.updateDisplay(Engine.java:61)
at it.diamonds.Game.update(Game.java:156)
at it.diamonds.Game.main(Game.java:55)
penso che sia dovuto sempre al fatto delle dimensioni della texture
Uhm...in locale funziona (sotto win xp), provo a vedere sotto suse se dà errore anche a me
http://img179.imageshack.us/img179/8548/x3wd.th.jpg (http://img179.imageshack.us/my.php?image=x3wd.jpg)
penso che sia dovuto sempre al fatto delle dimensioni della texture
Ti riferisci al controllo della texture se è una potenza di 2?
Anche se con una lentezza incredibile (eclipse mi prende tutta la memoria lasciandomi solo qualche mb disponibile su 512 :( ) ho provato sul notebook con suse 10 e funziona pure lì... :confused:
http://img386.imageshack.us/img386/3967/schermata18tq.th.jpg (http://img386.imageshack.us/my.php?image=schermata18tq.jpg)
4.1.3 plz, 1 giorno ^^
ps: gli screenshot sono stupendi!! :eek: però da me non funziona, da eccezione :(
BlueDragon
01-11-2005, 21:01
Anche a me da errore sotto Windows, ed il problema sono le potenze di due.
Ho cambiato le dimensioni dell'immagine facendola diventare 1024x1024 e tutto va bene.
L'errore è generico e non quello specifico per le potenze di due perché è stato volutamente skippato con un if... :P
if (otherType == null)
{
if (!isTwoPow((int)height) || !isTwoPow((int)width))
{
throw new TextureNotPowerOfTwoException();
}
}
(otherType è una variabile introdotta dal task 4.1.1)
cover, lo sistemi tu togliendo l'if? Così almeno quando esplode sappiamo perché :)
Domanda: come mai l'area di gioco a sinistra non è semitrasparente? Eppure la png lo era :confused:
BlueDragon
01-11-2005, 21:13
Classe Timer committata sul server.
Il metodo getTime() restituisce il numero di millisecondi passati da quando è stato istanziato l'oggetto Timer :)
package it.diamonds;
public class Timer
{
private long startTime;
public Timer()
{
startTime = System.currentTimeMillis();
}
public long getTime()
{
return System.currentTimeMillis()-startTime;
}
}
Anche a me da errore sotto Windows, ed il problema sono le potenze di due.
Ho cambiato le dimensioni dell'immagine facendola diventare 1024x1024 e tutto va bene.
L'errore è generico e non quello specifico per le potenze di due perché è stato volutamente skippato con un if... :P
if (otherType == null)
{
if (!isTwoPow((int)height) || !isTwoPow((int)width))
{
throw new TextureNotPowerOfTwoException();
}
}
(otherType è una variabile introdotta dal task 4.1.1)
cover, lo sistemi tu togliendo l'if? Così almeno quando esplode sappiamo perché :)
Avevo fatto quel controllo apposta per i file con estensione diversa dalla default (visto che il background è un jpg) per saltare il controllo della potenza e accettarlo comunque, provandolo in locale funziona e quindi pensavo che era tutto ok. Comunque ora l'ho tolto, ho modificato l'immagine a una grandezza 1024*1024 ed è tutto a posto.
Ho solo dovuto cambiare momentaneamente un test:
public void testBackgroundWidthAndHeight()
{
assertEquals("Width must be equal to the width of the window",
1024, background.getWidth());
//Game.getWindowWidth(), background.getWidth());
assertEquals("Height must be equal to the height of the window",
1024, background.getHeight());
//Game.getWindowHeight(), background.getHeight());
}
Momentaneamente messo a 1024 il controllo, anche se sarebbe più comodo riuscire a passare questo problema della potenza per poter utilizzare 800*600 e quindi solo l'area della finestra (ma anche come ho fatto ora potrebbe andare volendo.. ;) )
Hai fatto il commit della nuova immagine?
Appena fatto... sia dell'immagine, sia del test.. stavo facendo anche della texture.java ma hai già fatto tu ^^
BlueDragon
01-11-2005, 21:29
Domanda: come mai l'area di gioco a sinistra non è semitrasparente? Eppure la png lo era :confused:
Se vogliamo essere pignoli, anche il diamante è una png trasparente eppure almeno a me scende giù con un bel bordo nero. Mi sa che ci siamo persi l'alpha channel da qualche parte.. :D
E' un bug che mettera' a posto Raffaele.
4.1.3 plz, 1 giorno ^^
ps: gli screenshot sono stupendi!! :eek: però da me non funziona, da eccezione :(
Ok.
Cavolo siete perfino troppo veloci :D
ciao ;)
Classe Timer committata sul server.
Il metodo getTime() restituisce il numero di millisecondi passati da quando è stato istanziato l'oggetto Timer :)
package it.diamonds;
public class Timer
{
private long startTime;
public Timer()
{
startTime = System.currentTimeMillis();
}
public long getTime()
{
return System.currentTimeMillis()-startTime;
}
}
Posso suggerire l'uso della classe Sys di LWJGL? E' implementata nativamente e dovrebbe essere più affidabile e rapida della classe System di JAVA.
http://www.lwjgl.org/javadoc/org/lwjgl/Sys.html
Posso suggerire l'uso della classe Sys di LWJGL? E' implementata nativamente e dovrebbe essere più affidabile e rapida della classe System di JAVA.
http://www.lwjgl.org/javadoc/org/lwjgl/Sys.html in ogni caso io penso che userò la classe Timer di BlueDragon; poi casomai la possiamo riscrivere wrappando LWJGL.
BlueDragon
02-11-2005, 00:20
in ogni caso io penso che userò la classe Timer di BlueDragon; poi casomai la possiamo riscrivere wrappando LWJGL.
Se proprio vogliamo essere precisissimi, ho notato una novità della classe System 1.5:
nanoTime
public static long nanoTime()Returns the current value of the most precise available system timer, in nanoseconds.
This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time. The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees are made about how frequently values change. Differences in successive calls that span greater than approximately 292 years (263 nanoseconds) will not accurately compute elapsed time due to numerical overflow.
For example, to measure how long some code takes to execute:
long startTime = System.nanoTime();
// ... the code being measured ...
long estimatedTime = System.nanoTime() - startTime;
Returns:
The current value of the system timer, in nanoseconds.
Since:
1.5
E' implementato nativamente e visto che ritorna "the most precise available system timer", dovrebbe essere il top ottenibile :)
Ho fatto un po' di prove ed effettivamente currentTimeMillis non distingue bene i millesimi di secondo, anzi sembra mancare della precisione richiesta ma nanoTime dovrebbe andare bene.
Tanto per essere chiari, ho messo due semplici istruzioni nel loop infinito di Game:
System.out.println(System.nanoTime());
System.out.println(System.currentTimeMillis());
Mi sono preso i valori stampati sulla console ed ho fatto un po' di sottrazioni.
Secondo currentTimeMillis alcuni loop ci mettevano 0 millisecondi, altri 15 o 16 (avevo tolto lo sleep da 25 ms). Quindi currentTimeMillis direi che ha una precisione non migliore di 15.5 ms, almeno durante il test sul mio sistema.
Al contrario nanoTime dava valori diversi per ogni iterazione, di solito compresi tra 5.8 e 10ms, spesso attorno agli 8ms. Direi quindi che quest'ultima soluzione è più affidabile e dovrebbe garantire una precisione maggiore di quella richiesta (5ms).
^TiGeRShArK^
02-11-2005, 00:27
Task:
4.1.4: Input deve ignorare la pressione di tasti uguali e consecutivi se l'intevallo ùtra loro è inferiore ad 1/50 di secondo segnalando solo la pressione del primo.
Richiesta di prenotazione....
tempo stimato 2 giorni.
BlueDragon
02-11-2005, 00:39
Ho committato la nuova versione di Timer, che ora usa nanoTime().
Meno male che avevamo wrappato :D
Ho committato la nuova versione di Timer, che ora usa nanoTime().
Meno male che avevamo wrappato :D
Ottimo. Stavo giusto per proporti un refactoring :mano:
ciao ;)
Richiesta di prenotazione....
tempo stimato 2 giorni.
Tutto tuo.
ciao ;)
Ho risolto il problema relativo alla trasparenza anche se in una maniera veloce che dovrà essere rivista per bene.
Ci siamo finalmente scontrati con la più grande rogna che si possiede quando si realizza un'applicazione con trasparenze. In un'applicazione tridimensionale (e Diamonds non fa eccezione in quanto usa un solo piano di uno spazio tridimensionale) non si possono renderizzare oggetti opachi (cioè privi di trasparenza) misti ad oggetti semitrasparenti, ma per ottenere i risultati sperati è necessario renderizzare prima tutti gli oggetti opachi e, successivamente, tutti gli oggetti semitrasparenti. Inoltre, poiché noi renderizziamo il tutto su una stessa coordinata Z (uguale a 1) è molto importante che gli oggetti renderizzati per ultimi siano quelli che devono stare in primo piano. Ad esempio: il diamante deve essere renderizzato dopo la griglia in backgroung altrimenti accade che il diamante risulta essere coperto (in semitrasparenza) dalla griglia.
Ok.
Cavolo siete perfino troppo veloci :D
ciao ;)
Se continuano cosi' dobbiamo aggiungere una seconda storia a questo ciclo :)
Ho risolto il problema relativo alla trasparenza anche se in una maniera veloce che dovrà essere rivista per bene.
Ci siamo finalmente scontrati con la più grande rogna che si possiede quando si realizza un'applicazione con trasparenze. In un'applicazione tridimensionale (e Diamonds non fa eccezione in quanto usa un solo piano di uno spazio tridimensionale) non si possono renderizzare oggetti opachi (cioè privi di trasparenza) misti ad oggetti semitrasparenti, ma per ottenere i risultati sperati è necessario renderizzare prima tutti gli oggetti opachi e, successivamente, tutti gli oggetti semitrasparenti. Inoltre, poiché noi renderizziamo il tutto su una stessa coordinata Z (uguale a 1) è molto importante che gli oggetti renderizzati per ultimi siano quelli che devono stare in primo piano. Ad esempio: il diamante deve essere renderizzato dopo la griglia in backgroung altrimenti accade che il diamante risulta essere coperto (in semitrasparenza) dalla griglia.
Stavo solo aspettando che il problema si presentasse :)
Per risolvere il problema useremo un approccio a layer: quando aggiungiamo un oggetto Drawable alla lista di oggetti da disegnare, gli assegniamo anche un layer (da 0 a 10 ad esempio). Gli oggetti nel layer 10 vengono diesgnati per primi perche' sono piu' lontani, gli oggetti nel layer 0 vengono disegnati per ultimi e saranno quelli trasparenti. All'interno di ogni layer gli oggetti vengono disegnati nell'ordine di inserimento.
Tutto chiaro? Vic, puoi aggiungere il task per favore?
Stavo solo aspettando che il problema si presentasse :)
Per risolvere il problema useremo un approccio a layer: quando aggiungiamo un oggetto Drawable alla lista di oggetti da disegnare, gli assegniamo anche un layer (da 0 a 10 ad esempio). Gli oggetti nel layer 10 vengono diesgnati per primi perche' sono piu' lontani, gli oggetti nel layer 0 vengono disegnati per ultimi e saranno quelli trasparenti. All'interno di ogni layer gli oggetti vengono disegnati nell'ordine di inserimento.
Tutto chiaro? Vic, puoi aggiungere il task per favore?
Immagino che tutte le gemme stiano sullo stesso layer, giusto?
La domanda è apparentemente stupida, ma meglio precisare anche i dettagli più insignificanti :P
va bene questo test per il task 4.1.3?
public class TestGame extends TestCase
{
private MockEngine engine;
private Game game;
public void setUp()
{
engine = new MockEngine();
game = Game.createForTesting(engine);
}
public void testFrameRate()
{
Timer timer = new Timer();
game.mainLoop();
assertEquals(20, timer.getTime(), 5);
assertTrue(engine.getNumberOfQuadsDrawn() > 0);
}
}
ho assunto di dover cambiare alcune cose: la gestione del loop principale deve andare in un nuovo metodo "mainLoop" e tutta l'inizializzazione effettuata nella main dovrebbe invece andare nel costruttore.
PS: ho visto ora il videogioco con le trasparenze: sempre più bello!!! :eek:
Stavo solo aspettando che il problema si presentasse :)
Per risolvere il problema useremo un approccio a layer: quando aggiungiamo un oggetto Drawable alla lista di oggetti da disegnare, gli assegniamo anche un layer (da 0 a 10 ad esempio). Gli oggetti nel layer 10 vengono diesgnati per primi perche' sono piu' lontani, gli oggetti nel layer 0 vengono disegnati per ultimi e saranno quelli trasparenti. All'interno di ogni layer gli oggetti vengono disegnati nell'ordine di inserimento.
Tutto chiaro? Vic, puoi aggiungere il task per favore?
Ci sono ragioni particolari per invertire la scala? Preferirei usare lo stesso ordine che usano programmi come photoshop per i layer. Layer 0 è il piu basso per poi salire.
ciao ;)
Task:
4.1.5: Refactoring. Aggiungere a drawable la capacita di specificare il layer su cui disegnare i vari oggetti. (VICIUS: 2 giorni)
Bello questo task mi piace. Vic mi prenoto. 2 giorni. :asd:
ciao ;)
ARGHCOSAVEDOLACLASSETIMERNONE'TESTATA!!! :mad:
ecco i test, ho anche aggiunto un test per un nuovo metodo reset, che può servire:
public class TestTimer extends TestCase
{
public void testTimer() throws InterruptedException
{
Timer timer = new Timer();
Thread.sleep(500);
assertEquals(500, timer.getTime(), 10);
}
public void testReset() throws InterruptedException
{
Timer timer = new Timer();
Thread.sleep(123);
timer.reset();
assertEquals(0, timer.getTime(), 10);
}
}
EDIT: ho modificato i valori delle due sleep altrimenti ogni volta che eseguiamo i test dobbiamo aspettare altri 2 secondi, come se già Eclipse non ci mettesse una vita :asd:
ARGHCOSAVEDOLACLASSETIMERNONE'TESTATA!!! :mad:
ecco i test, ho anche aggiunto un test per un nuovo metodo reset, che può servire:
fek ha fatto proprio un buon lavoro con te :D
Fai pure il commit del Test.
ciao ;)
fek ha fatto proprio un buon lavoro con te :D
Fai pure il commit del Test. prima vorrei sapere una cosa: nelle assertEquals il terzo argomento cosa rappresenta, una tolleranza?
prima vorrei sapere una cosa: nelle assertEquals il terzo argomento cosa rappresenta, una tolleranza? non fa nullla, ormai ho committato :D
allora VICIUS, i miei test per il task 4.1.3 vanno bene?
Immagino che tutte le gemme stiano sullo stesso layer, giusto?
La domanda è apparentemente stupida, ma meglio precisare anche i dettagli più insignificanti :P
Direi che questa e' una tua decisione. Quando il sistema e' in piedi, lo usi come ti viene piu' comodo.
Ci sono ragioni particolari per invertire la scala? Preferirei usare lo stesso ordine che usano programmi come photoshop per i layer. Layer 0 è il piu basso per poi salire.
ciao ;)
Ho tirato la scala a caso al momento, usa quella che ti viene piu' naturale.
ARGHCOSAVEDOLACLASSETIMERNONE'TESTATA!!! :mad:
ecco i test, ho anche aggiunto un test per un nuovo metodo reset, che può servire:
Il metodo reset serve o puo' servire? E' una distinzione importante.
Ma come hai fatto a testare il game loop e il timer???? Ma ho creato davvero un mostro :D
fek ha fatto proprio un buon lavoro con te :D
Fai pure il commit del Test.
ciao ;)
Hey, ma lui era quello che i test non servono eh... :p
prima vorrei sapere una cosa: nelle assertEquals il terzo argomento cosa rappresenta, una tolleranza?
Si si tratta proprio della tolleranza. Abbassa il valore a 5 cosi rimaniamo fedeli alle specifiche.
Per il test del 4.1.3 io userei un assertEquals(timer.getTime(), 20, 5); Per essere sicuri aspetta una conferma da fek.
ciao ;)
Allora lo dico io... tutte le gemme vanno sullo stesso layer :P
Il metodo reset serve o puo' servire? E' una distinzione importante. è un compromesso: probabilmente servirà! :D
prima di eliminarlo però io aspetterei ad aver completato il mio task (che completerò stasera perché ora vado a lezione) se non addirittura la storia.
Ma come hai fatto a testare il game loop e il timer???? Ma ho creato davvero un mostro :D e infatti per testare il loop del gioco dovrò cambiare un po' di cose come ho già detto: nella main devo separare la parte di inizializzazione (che devo mettere nel costruttore) dalla parte del loop (che devo mettere in un metodo apposito).
Hey, ma lui era quello che i test non servono eh... :p no, aspetta un secondo, :D io come ho già detto non sono un semplice pignolo sono un perfezionsta (MHWUAHWUHUWA :Perfido: :Perfido: ) e il motivo percui mi sono impuntato sui test della classe Timer non è che io pensi che i test servono sempre, ma è piuttosto il fatto che voglio uniformare il metodo di lavoro di tutto il gruppo e l'aspetto finale del codice. se decidiamo che le classi vanno testate il più possibile e testiamo la prima classe, allora io per induzione (:p :p :p) LE TESTO TUTTE! :Perfido:
è un compromesso: probabilmente servirà! :D
prima di eliminarlo però io aspetterei ad aver completato il mio task (che completerò stasera perché ora vado a lezione) se non addirittura la storia.
Toglilo. You Aren't Gonna Need It. Niente codice che probabilmente servira'. Lo aggiungi quando ti serve.
e infatti per testare il loop del gioco dovrò cambiare un po' di cose come ho già detto: nella main devo separare la parte di inizializzazione (che devo mettere nel costruttore) dalla parte del loop (che devo mettere in un metodo apposito).
Ottimo, ma questo e' un refactoring corposo che pensavamo di fare in un suo task apposito. Vicius?
Vicius un consiglio (magari poi quando finisci lo rivediamo insieme). Fai in modo che vengano renderizzati prima tutti gli oggetti senza trasparenza (essenzialmente, nel nostro caso, le texture JPEG, cioè attualmente il solo sfondo). Dopo averli renderizzati bisogna cambiare uno stato di OpenGL riguardante la scrittura nello ZBUFFER e successivamente disegnare quelli semitrasparenti con l'ordine corretto secondo i layer definiti.
Bisogna quindi fare un sistema a layer a doppio fronte: quello che riguarda gli oggetti opachi e quello che riguarda quelli trasparenti.
Ancora meglio, disabilitiamo lo zbuffer totalmente e il problema e' risolto. Niente depth test e depth write.
Sono già prenotati tutti i task ? :(
Ho fatto il commit dei miei test e di "LayerManager". Il mio task lo considero finito .
ciao ;)
Sono già prenotati tutti i task ? :(
Stanno andando via come il pane, ma continuando cosi' avremo sicuramente una nuova Storia. Fai un task in Pair Programming con me?
Va bene, ma vediamo per bene quando ci possiamo trovare contemporaneamente online perchè scommetto che te sarai disponibile dopo cena, mentre io dopo cena quasi mai :D
cionci, c'è un refactoring da fare che va fatto prima del mio task; bisogna fare le seguenti cose:
1) spostare il codice di inizializzazione presente in Game.main nel costruttore di Game, che attualmente è vuoto
2) spostare il codice del loop principale in un metodo a parte, che potrai chiamare "mainLoop" (è come l'ho chiamato io nei miei test)
3) il contenuto della main alla fine deve essere costituito da una chiamata a mainLoop seguita direttamente dal codice di finalizzazione
ora però scrivere dei test per questo refactoring non mi sembra una buona idea perché ad esempio per testare l'avvenuta inizializzazione della classe Game dovremmo esporre tanti di quei metodi getters per prelevare tanti di quei campi privati che a un certo punto subentra anche un discorso di eleganza di design... senza contare che noi non abbiamo mai testato Game finora perché la sua implementazione è in gran parte provvisoria (pensate ad es. a come dovrà essere modificata quando aggiungeremo i menu del gioco...).
comunque chi si occupa di questo refactoring, io? fek? cionci?
Come volete voi, io stasera sono disponibile per un po' di Pair Refactoring ( :p ) se volete. Questo e' un bel refactoring corposo e sarebbe interessante affrontarlo sul forum.
Ed io stasera non ci sono :cry:
io invece si :Prrr:
quando iniziamo? per me va bene anche adesso; sono curioso di sapere come si affrontano situazioni dove la necessità di testare causa problemi di design.
Ed io, come sempre, vi osserverò in silenzio :p
Azz mi sono fatto scoprire...
Ed io, come sempre, vi osserverò in silenzio :p
Azz mi sono fatto scoprire...
Ti faccio compagnia ^^
^TiGeRShArK^
02-11-2005, 20:14
fatto il primo test ke fallisce... (ovviamente :D)
public class TestGemMove extends TestCase
{
private Gem gem;
private Input input;
private Timer timer;
public void setUp()
{
gem = Gem.createForTesting();
input = Input.createForTesting();
timer = new Timer();
input.setInputDelay(20);
gem.setPos(100f, 200f);
}
public void testKeyDown()
{
float y = gem.getY();
long startTime = timer.getTime();
while (timer.getTime() - startTime < input.getInputDelay())
{
input.generateKey(KeyCode.vk_Down);
gem.reactToInput(input);
}
assertEquals("gem react erroneously to multiple vk_Down", y + gem.getSpeedY(), gem.getY(), 1);
}
ho dei dubbi però....
1) devo fare altri due test identici x left e right o faccio un test generico TestKeyPulled???
2) nel caso di pressione di down e left x esempio ke deve succedere????
Il tasto down per ora non lo consideriamo, quindi puoi tralasciarlo ;)
^TiGeRShArK^
02-11-2005, 20:23
ehm..
qdi devo intervenire solo su left e right giusto??
ok... ora modifico il codice..
tnt il test lo passava ma dovevo fare un bel èò di refactoring ke nn m piaceva proprio :p
Per ora, consideriamo solo left e right (up dovrebbe essere già escluso), il tasto down inizieremo a considerarlo solo in una prossima storia ;)
1) faccio un test generico TestKeyPulled???
2) nel caso di pressione di down e left x esempio ke deve succedere????
Ti consiglio di creare un MockTimer che ritorni il tempo che decidi tu (magari nel costruttore) invece di usare il Timer vero e proprio. Estrai un'interfaccia dal Timer (c'e' il comando di Refactoring in Eclipse) e poi implementa l'interfaccia nel Mock.
^TiGeRShArK^
02-11-2005, 20:57
ehm... in effetti forse sarebbe + pulito..
xò così x ora funziona...
o meglio...
i miei test li passa..
devo capire xkè nn passa gli altri test ke si spostano a sinistra.. :muro:
ma è meglio se lo faccio ora il mockTimer o prima faccio passare tutti i test?
^TiGeRShArK^
02-11-2005, 21:03
capito...
mi ero perso ke il reactToInput era stato spostato in Grid anzikè essere in Gem... :muro:
sukoy27k
02-11-2005, 21:06
fatto il primo test ke fallisce... (ovviamente :D)
public class TestGemMove extends TestCase
{
private Gem gem;
private Input input;
private Timer timer;
public void setUp()
{
gem = Gem.createForTesting();
input = Input.createForTesting();
timer = new Timer();
input.setInputDelay(20);
gem.setPos(100f, 200f);
}
public void testKeyDown()
{
float y = gem.getY();
long startTime = timer.getTime();
while (timer.getTime() - startTime < input.getInputDelay())
{
input.generateKey(KeyCode.vk_Down);
gem.reactToInput(input);
}
assertEquals("gem react erroneously to multiple vk_Down", y + gem.getSpeedY(), gem.getY(), 1);
}
ho dei dubbi però....
1) devo fare altri due test identici x left e right o faccio un test generico TestKeyPulled???
2) nel caso di pressione di down e left x esempio ke deve succedere????
io stavo pensando di intervenire direttamente in KeyboradImpl,nel metodo update,basta inserire un ritardo lì conservando la chiave prima di mandarlo a dormire per 1/50 di sec,per questo caso stavo scrivendo i test per keyboardimPL.
Anche se effettivamente la tua soluzione è molto più bella della mia,tanto da farmi sbavare :cry:
Vabbè spero di capirci qualcosa di più nei prossimi giorni, :stordita:
ehm... in effetti forse sarebbe + pulito..
xò così x ora funziona...
o meglio...
i miei test li passa..
devo capire xkè nn passa gli altri test ke si spostano a sinistra.. :muro:
ma è meglio se lo faccio ora il mockTimer o prima faccio passare tutti i test?
Prima fallo funzionare e poi fai il refactoring.
Hey, questo forum e' k-free ;)
^TiGeRShArK^
02-11-2005, 21:47
mi sa che abbiamo di nuovo qualche casino con i teardown che sarà nato dopo aver aggiunto il setStartTime ad Input..
infatti da eclipse alcuni test mi passano, ma mi falliscono con ant...
vedo se riesco a capirci qualcosa....
:cry:
VICIUS, col permesso di fek ho completato il task 4.1.3 senza il test :D
Fate un ultimo commosso saluto alla classe GridBackground, che stasera e' salita' lassu' in cielo dove il refactoring non esiste e tutte le classi sono testate. RIP.
VICIUS, supponendo che tu abbia ideato quel task per risolvere il problema della gemma che si sposta a destra e a sinistra a una velocità incontrollabile (:D), secondo me quel task non risolverà il problema e anzi, secondo me non è nemmeno fattibile (infatti mi sembra che TigerShark stia avendo problemi).
leggi il codice di Input e quello di Game: stringendo molto il nostro gioco funziona banalmente con un loop che effettua periodicamente un poll sullo stato della tastiera, quindi secondo me non è possibile "ignorare la pressione di un tasto", il meccanismo del poll non tiene conto dell'evento della pressione e del rilascio del tasto e del tempo che separa due pressioni consecutive, semplicemente analizza lo stato del tasto in un momento arbitrario.
il problema per cui la gemma se ne schizza di qua e di la è che la frequenza di poll corrisponde precisamente a quella di refresh dello schermo, e questa è una cosa che dobbiamo assolutamente correggere! il frame rate non deve corrispondere alla frequenza di poll, deve essere maggiore!
il primo passo che si deve fare per effettuare questa correzione è spostare la gestione della gravità, che attualmente viene gestita da reactToInput, ed è concettualmente errato perché la gravità deve essere un fenomeno a parte indipendente dalla frequenza con cui viene chiamata reactToInput (che stringendo molto corrisponde praticamente alla frequenza con cui viene effettuato il poll sulla tastiera).
il secondo passo della correzione invece è il mega-refactoring di Game :)
secondo me questa classe va ridisegnata addirittura in multithreading: un thread fa cadere la gemma ed effettua il refresh del frame, mentre l'altro gestisce l'input. se non implementiamo in multithreading allora possiamo anche fare tutto in un thread solo con un loop unico dove, a sincronìe diverse, gestiamo i due fenomeni indipendenti della gravità/refresh e della gestione dell'input, con un codice del genere:
while (1)
{
// codice di gravità/refresh sincronizzato ai 20 ms
if(0 == (timer.getTime() % 20))
{
.
.
.
}
// codice di gestione input sincronizzato ai 75 ms
if(0 == (timer.getTime() % 75))
{
.
.
.
}
}
BlueDragon
02-11-2005, 23:55
ARGHCOSAVEDOLACLASSETIMERNONE'TESTATA!!! :mad:
Non è testata perché mi sono fidato di fek quando ha detto che non era testabile :D
Infatti anche se il tuo test è una buona idea, cosa succede se assieme alla build di Ant fai partire anche qualche programma "CPU Intensive" ?
Non mi intendo di sistemi operativi e multitasking ma penso che il processore potrebbe benissimo ignorare per un bel po' di tempo il thread java ed eseguire l'Assert molto più tardi del previsto, ritornando quindi dal timer un numero di millisecondi diverso da quello che ci si aspetta :)
secondo me questa classe va ridisegnata addirittura in multithreading: un thread fa cadere la gemma ed effettua il refresh del frame, mentre l'altro gestisce l'input. se non implementiamo in multithreading allora possiamo anche fare tutto in un thread solo con un loop unico dove, a sincronìe diverse, gestiamo i due fenomeni indipendenti della gravità/refresh e della gestione dell'input, con un codice del genere:
Multithreading? Certo che tu ami complicarti la vita eh? :D
^TiGeRShArK^
03-11-2005, 00:24
ho committato grazie ai consigli di fek (certo che se avesse capito subito quello che gli avevo kiesto mi sarei risparmiato un'ora di smadonnamenti :Prrr: )
qua di seguito posto i Test completi....
da notare che ora per il metodo per lo spostamento delle gemme nella griglia a causa di un input è diventato grid.reactToInput(Input input, Timer timer), in pratica necessita anche di un timer.
public void testKeyLeftLessThanDelay()
{
grid.insertGem(2, 4, gem);
grid.setGemUnderControl(gem);
input.generateKey(KeyCode.vk_Left);
grid.reactToInput(input, timer);
timer.setTime(timer.getTime() + Grid.INPUTDELAY - 1);
input.generateKey(KeyCode.vk_Left);
grid.reactToInput(input, timer);
assertTrue("gem reacts erroneously to multiple vk_Left in a timeframe shorter than Grid.getInputDelay()", grid.isGemAt(2, 3));
}
public void testKeyLeftMoreThanDelay()
{
grid.insertGem(2, 4, gem);
grid.setGemUnderControl(gem);
input.generateKey(KeyCode.vk_Left);
grid.reactToInput(input, timer);
timer.setTime(timer.getTime() + Grid.INPUTDELAY + 1);
input.generateKey(KeyCode.vk_Left);
grid.reactToInput(input, timer);
assertTrue("gem reacts erroneously to multiple vk_Left in a timeframe larger than Grid.getInputDelay()", grid.isGemAt(2, 2));
}
public void testKeyRightLessThanDelay()
{
grid.insertGem(2, 4, gem);
grid.setGemUnderControl(gem);
input.generateKey(KeyCode.vk_Right);
grid.reactToInput(input, timer);
timer.setTime(timer.getTime() + Grid.INPUTDELAY - 1);
input.generateKey(KeyCode.vk_Right);
grid.reactToInput(input, timer);
assertTrue("gem reacts erroneously to multiple vk_Right in a timeframe shorter than Grid.getInputDelay()", grid.isGemAt(2, 5));
}
public void testKeyRightMoreThanDelay()
{
grid.insertGem(2, 4, gem);
grid.setGemUnderControl(gem);
input.generateKey(KeyCode.vk_Right);
grid.reactToInput(input, timer);
timer.setTime(timer.getTime() + Grid.INPUTDELAY + 1);
input.generateKey(KeyCode.vk_Right);
grid.reactToInput(input, timer);
assertTrue("gem reacts erroneously to multiple vk_Right in a timeframe larger than Grid.getInputDelay()", grid.isGemAt(2, 6));
}
e dopo questo il letto non me lo caccia nessuno.... :mbe:
Bene. Tutti i task sono stati completati prima del previsto quindi ora potete procedere al "poderoso" refactoring di Game.
Ancora una volta. Complimenti a tutti. :mano:
ciao ;)
Multithreading? Certo che tu ami complicarti la vita eh? :D
Beh dicono che bisogna iniziare a sfruttare i sistemi dual core :D Che Diamonds possa essere un precursore dei tempi?
Beh dicono che bisogna iniziare a sfruttare i sistemi dual core :D Che Diamonds possa essere un precursore dei tempi?
Facciamo precorre i tempi a qualcun altro :D
^TiGeRShArK^
03-11-2005, 10:27
Facciamo precorre i tempi a qualcun altro :D
pienamente d'accordo! :D
Quindi e' gia' tempo di un'altra Storia?
Jocchan, a te...
No...controllate la build... I test vecchi su grid devono essere aggiornati... Probabilmente Tiger non ha uploadato i test corretti...
Inoltre non capisco perchè si applica la gravità alla pressione di un tasto... Non dovrebbe applicarla solo al momento in cui viene chiamato il draw ?
No...controllate la build... I test vecchi su grid devono essere aggiornati... Probabilmente Tiger non ha uploadato i test corretti...
No niente...qui ho sbagliato io :fagiano:
^TiGeRShArK^
03-11-2005, 11:47
ah ecco.. mi ero già preoccupato di essermi incasinato con i merge ieri sera..
dato ke un certo berto ha committato e avevamo cambiato delle cose negli stessi file :D
;)
Quindi e' gia' tempo di un'altra Storia?
Jocchan, a te...
La scrivo adesso, intanto assicuriamoci che sia tutto a posto ;)
Per i task della prossima Storia, avra' la precedenza chi non ha eseguito un task in questa storia.
Non fate i soliti arraffoni :D
fek, ma applicare la gravita in reactToInput è una cosa voluta ?
Io la applicherei in draw, mi sembra più logico... In questo modo il movimente progredisce ritmato dalla frequenza di disegno, invece che dalla frequenza con cui sono proposti gli input...
fek, ma applicare la gravita in reactToInput è una cosa voluta ?
Io la applicherei in draw, mi sembra più logico... In questo modo il movimente progredisce ritmato dalla frequenza di disegno, invece che dalla frequenza con cui sono proposti gli input...
Si' e no. Sicuramente la gravita' in reactToInput() "puzza" un po', ma anche in draw() non la vedrei a suo agio. Non e' un terreno semplice e ci sono varie soluzioni. Ti dico quella complessa che preferisco io, ma non tutti sono d'accordo con me.
Io preferisco sempre che i metodi draw (render) siano costanti e non modifichino l'aspetto osservabile dell'oggetto (non tutti sono d'accordo con me qui) e affiderei l'aggiornamento dell'oggetto ad un metodo update, dove vedrei naturale l'applicazione della gravita'.
Ed in genere chiamerei l'update con la frequenza di aggiornamento della logica del gioco e non con la frequenza di aggiornamento dal render. 71104 ha allungato troppo la gamba con il multithreading ma l'idea di base di separate frequenza di aggiornamento del gioco e del rendering non e' sbagliata.
A questo punto si pone pero' una complicazione: il rendering puo' prendere la posizione corrente dell'oggetto e usarla per il disegno, oppure puo' prendere la posizione degl'ultimi due turni e interpolare fra le due. La prima soluzione e' piu' semplice, ma il risultato a schermo non e' ideale, e gia' si vede il diamante zompettare felice e saltare frame. La seconda soluzione e' piu' complessa ma il risultato e' garantito essere ottimo.
Ora, capiamoci bene non sto richiedendo nessuna delle due soluzioni e non voglio dare uno sguardo troppo in avanti. Preferisco vedere come si evolve il codice e le richieste del Coach e poi potremmo evolverci verso uno di questi due "Design Pattern" (hey! bella idea! magari un giorno ci scrivo un libro sui Design Patter nei VG) se fosse necessario.
Sicuramente la gravita' dentro reactToInput() chiede un refactoring.
Ok...ottimo...
Altra cosa: ho visto che in reactToInput si cerca di non far muovere la gemma se vengono premuti due tasti insieme effettuando sia un movimento a destra che un movimento a sinistra...ma la cosa non funziona perchè il timer viene aggiornato, quindi non si possono fare altri movimenti oltre al primo...
^TiGeRShArK^
03-11-2005, 14:02
Per i task della prossima Storia, avra' la precedenza chi non ha eseguito un task in questa storia.
Non fate i soliti arraffoni :D
naturalmente!
io mi prendo sempre un pò di riposo dopo aver completato un task x fare lavorare anke gli altri :D
( e in + nn mi distruggo io:Prrr: )
fek, ma applicare la gravita in reactToInput è una cosa voluta ?
Io la applicherei in draw, mi sembra più logico... In questo modo il movimente progredisce ritmato dalla frequenza di disegno, invece che dalla frequenza con cui sono proposti gli input... la mia opinione: la gravità applicata in reactToInput è errata, in draw è peggio; senza contare che il tuo discorso della frequenza non vale perché puoi metterla come ti pare, ma attualmente noi abbiamo una sola frequenza nel loop principale, ovvero quella su cui si sincronizza il metodo Game.synchronize().
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.