View Full Version : [CICLO 5] Storia 1
Storia 1: Storia di servizio, dedicata al perfezionamento della risposta agli input del giocatore. La risposta ai comandi deve essere istantanea e precisa, senza che alcun comando venga mai saltato, anche nelle situazioni più concitate. Nuovamente, occorrerà assicurarsi che la gemma non si sposti in maniera indesiderata dopo una pressione leggermente più lunga del solito, ed occorrerà correggere in maniera definitiva il bug di posizionamento della stessa alla pressione contemporanea dei tasti sx e dx.
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:
5.1.1: 71104
Implementare l'algoritmo di gestione degli Input di BlueDragon presente nel branch trunk/ (no copia/incolla. solo pair-programming)
ciao ;)
Task:
5.1.1:
Implementare l'algoritmo di gestione degli Input di BlueDragon presente nel branch trunk/ (no copia/incolla. solo pair-programming)
ciao ;) embe'?? :confused: un solo task? :confused:
comunque vorrei prenotarmi anche per questo, ma non so se a voi va bene perché già ho fatto il pair programming dell'altra volta (forse volete cambiare un po'...)
embe'?? :confused: un solo task? :confused:
comunque vorrei prenotarmi anche per questo, ma non so se a voi va bene perché già ho fatto il pair programming dell'altra volta (forse volete cambiare un po'...)
E' la fine della storia che abbiamo iniziato al ciclo precedente, quindi un solo task :)
Vediamo se altri si prenotano, per te ci sono un po' di task nella Storia 2 freschi freschi e le cose cominciano a movimentarsi.
Chi si prenota insieme a 71104?
Venghino, siori, venghino, non fate i timidi :Prrr:
nessuno si vuole prenotare con me perché faccio paura!!! :eekk:
ragazzi, qua non si prenota nessuno, siamo bloccati... Customer, dica una data: se entro la data nessuno si è ancora prenotato faccio o da solo o con fek :Prrr:
che ne pensa? :D
Bhe visto che l'idea l'ha avuta Blue Dragon sarebbe il caso che ci fosse anche lui nella coppia. Blue Dragon che fine hai fatto ? :D
ciao ;)
Bhe visto che l'idea l'ha avuta Blue Dragon sarebbe il caso che ci fosse anche lui nella coppia. Blue Dragon che fine hai fatto ? :D
ciao ;)
BD l'ho contattato io ieri, e ora non ha molto tempo: dice che per il weekend (cioè da venerdì) sarà disponibile (e quindi direi che avremo un "volontario" per il task).
Quindi consiglierei a 71104 di fare prima il task della seconda storia, e poi nel weekend di svolgere questo (per fortuna sono dei task scollegati) ;)
Ancora nessuno? Su forza che 71104 scalpita :D
ciao ;)
BlueDragon
19-11-2005, 14:33
A quanto pare sono "volontario d'ufficio" per questo pair programming :D
Ho parlato con 71104 e questo weekend non abbiamo orari compatibili, proveremo nelle sere della prossima settimana.
Comunque ecco cosa bisogna fare:
1) Popolare la coda di Input tramite KeyboardImpl
E' una cosa "banale" se non fosse per un unico problema...KeyboardImpl conosce il numero intero corrispondente al tasto premuto, ma non conosce l'enum KeyCode da inserire nella coda. Ho guardato la documentazione dell'enum ma non ho trovato metodi per risalire dall'int al KeyCode in maniera semplice. Io nel mio spike avevo fatto una mappa inversa da int a KeyCode, ma non era una cosa carina a vedersi.
Ho parlato con 71104 su MSN ed abbiamo pensato di modificare la coda affinché i KeyEvent contengano gli int e non i KeyCode. La differenza rispetto al codice che c'è adesso sarà semplicemente che anziché scrivere nel codice KeyCode.vk_Left, useremo KeyCode.vk_Left.value() per riferirci al valore int.
Il vantaggio di questa soluzione è che così KeyboardImpl non ha bisogno di una mappa per capire i nostri enum, e noi umani che non ci ricordiamo tutti gli int della tastiera a memoria possiamo ancora appoggiarci ad i nomi dell'enum KeyCode.
Se avete in mente soluzioni migliori....ben vengano!! :)
2) Migliorare le reazioni agli Input:
Tramite l'utilizzo delle informazioni aggiuntive forniteci dalla coda, possiamo quindi passare a migliorare il comportamento del reactToInput affinché vengano superati i seguenti test:
public void testKeyLeftLessThanDelay()
{
grid.insertGem(2, 5, gem);
grid.setGemUnderControl(gem);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Pressed);
grid.reactToInput(input, timer);
timer.setTime(timer.getTime() + grid.getInputDelay() - 1);
grid.reactToInput(input, timer);
assertTrue( "Gem has moved more than once with Left being pressed for less than Delay",
grid.isGemAt(2, 4));
}
public void testKeyLeftMoreThanDelay()
{
grid.insertGem(2, 5, gem);
grid.setGemUnderControl(gem);
input.generateKey(KeyCode.vk_Left);
grid.reactToInput(input, timer);
timer.setTime(timer.getTime() + grid.getInputDelay() + 1);
grid.reactToInput(input, timer);
assertTrue( "Gem isn't moving according to the correct delay with Left Key being pressed",
grid.isGemAt(2, 3));
}
public void testKeyRightLessThanDelay()
{
grid.insertGem(2, 5, gem);
grid.setGemUnderControl(gem);
input.generateKeyEvent(KeyCode.vk_Right, KeyState.Pressed);
grid.reactToInput(input, timer);
timer.setTime(timer.getTime() + grid.getInputDelay() - 1);
grid.reactToInput(input, timer);
assertTrue( "Gem has moved more than once with Left being pressed for less than Delay",
grid.isGemAt(2, 6));
}
public void testKeyRightMoreThanDelay()
{
grid.insertGem(2, 5, gem);
grid.setGemUnderControl(gem);
input.generateKey(KeyCode.vk_Right);
grid.reactToInput(input, timer);
timer.setTime(timer.getTime() + grid.getInputDelay() + 1);
grid.reactToInput(input, timer);
assertTrue( "Gem isn't moving according to the correct delay with Right Key being pressed",
grid.isGemAt(2, 7));
}
public void testKeyLeftReleased()
{
grid.insertGem(2, 5, gem);
grid.setGemUnderControl(gem);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Pressed);
grid.reactToInput(input, timer);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Released);
timer.setTime(timer.getTime() + grid.getInputDelay()+1);
grid.reactToInput(input, timer);
assertTrue( "Gem didn't stop moving after Left Key being released",
grid.isGemAt(2, 4));
}
public void testKeyRightReleased()
{
grid.insertGem(2, 5, gem);
grid.setGemUnderControl(gem);
input.generateKeyEvent(KeyCode.vk_Right, KeyState.Pressed);
grid.reactToInput(input, timer);
input.generateKeyEvent(KeyCode.vk_Right, KeyState.Released);
timer.setTime(timer.getTime() + grid.getInputDelay()+1);
grid.reactToInput(input, timer);
assertTrue( "Gem didn't stop moving after Right Key being released",
grid.isGemAt(2, 6));
}
public void testMultipleLeftKeyPressed()
{
grid.insertGem(2, 5, gem);
grid.setGemUnderControl(gem);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Pressed);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Released);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Pressed);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Released);
grid.reactToInput(input, timer);
assertTrue( "Gem didn't move twice with Left Key pressed twice by user.",
grid.isGemAt(2, 3));
}
public void testMultipleRightKeyPressed()
{
grid.insertGem(2, 5, gem);
grid.setGemUnderControl(gem);
input.generateKeyEvent(KeyCode.vk_Right, KeyState.Pressed);
input.generateKeyEvent(KeyCode.vk_Right, KeyState.Released);
input.generateKeyEvent(KeyCode.vk_Right, KeyState.Pressed);
input.generateKeyEvent(KeyCode.vk_Right, KeyState.Released);
grid.reactToInput(input, timer);
assertTrue( "Gem didn't move twice with Right Key pressed twice by user.",
grid.isGemAt(2, 7));
}
public void testRapidSequence()
{
grid.insertGem(2, 5, gem);
grid.setGemUnderControl(gem);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Pressed);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Released);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Pressed);
input.generateKeyEvent(KeyCode.vk_Left, KeyState.Released);
input.generateKeyEvent(KeyCode.vk_Right, KeyState.Pressed);
input.generateKeyEvent(KeyCode.vk_Right, KeyState.Released);
grid.reactToInput(input, timer);
assertTrue( "Grid didn't react correctly to fast sequence.",
grid.isGemAt(2, 4));
}
Di questi 9 test, 4 sostituiscono dei test già esistenti con lo stesso nome.
A parole ecco cosa testano:
testKeyLeftLessThanDelay()
Tenendo premuto il tasto, la gemma non si sposta una seconda volta prima che sia passato un certo delay.
testKeyLeftMoreThanDelay
Tenendo premuto il tasto, la gemma si sposta nuovamente una volta passato un certo delay.
testKeyLeftReleased
Rilasciando il tasto, la gemma smette di spostarsi.
testMultipleLeftKeyPressed
Se si preme diverse volte lo stesso tasto, la gemma si muove di un numero equivalente di celle senza che sia necessario attendere il delay.
testRapidSequence
La gemma è capace di reagire correttamente ad una catena di più eventi destra-sinistra in rapida successione senza attendere alcun delay.
1) Popolare la coda di Input tramite KeyboardImpl
E' una cosa "banale" se non fosse per un unico problema...KeyboardImpl conosce il numero intero corrispondente al tasto premuto, ma non conosce l'enum KeyCode da inserire nella coda. Ho guardato la documentazione dell'enum ma non ho trovato metodi per risalire dall'int al KeyCode in maniera semplice. Io nel mio spike avevo fatto una mappa inversa da int a KeyCode, ma non era una cosa carina a vedersi.
Ho parlato con 71104 su MSN ed abbiamo pensato di modificare la coda affinché i KeyEvent contengano gli int e non i KeyCode. La differenza rispetto al codice che c'è adesso sarà semplicemente che anziché scrivere nel codice KeyCode.vk_Left, useremo KeyCode.vk_Left.value() per riferirci al valore int.
Il vantaggio di questa soluzione è che così KeyboardImpl non ha bisogno di una mappa per capire i nostri enum, e noi umani che non ci ricordiamo tutti gli int della tastiera a memoria possiamo ancora appoggiarci ad i nomi dell'enum KeyCode.
Se avete in mente soluzioni migliori....ben vengano!! :)
L'idea che il cliente della classe debba scrivere KeyCode.vk_Left.value() non mi piace affatto, preferisco una mappa inversa piuttosto.
Personalmente nasconderei anche il KeyCode dietro ad un metodo esplicito (left() magari), ma questo e' un discorso che si fara' piu' avanti.
BlueDragon
19-11-2005, 18:11
Beh, possiamo anche mettere la mappa inversa, però a quel punto mi sfugge una cosa...a che serve che il valore KeyCode.vk_Left sia pari a org.lwjgl.input.Keyboard.KEY_LEFT? Se tanto nessuno ne usa mai il value... :D
Potevamo a questo punto fare una enum associata a valori tipo "pippo","pluto","paperino"....
C'è qualcosa che mi sfugge nell'uso della enum...mi sa che andrò a fare un giro su java.sun.com.... :)
Cmq il codice che avevo usato nello spike è questo:
Riempimento della coda in KeyBoardImpl:
public void update(Input input)
{
Keyboard.poll();
while(Keyboard.next())
{
Input.KeyCode key = (KeyCode)keyMap.get(Keyboard.getEventKey());
if (key != null)
{
boolean state = Keyboard.getEventKeyState();
if (state)
{
input.generateKeyEvent(key , Input.KeyState.Pressed);
} else
{
input.generateKeyEvent(key , Input.KeyState.Released);
}
}
}
}
Supportato dalla creazione della mappa inversa nel costruttore di KeyboardImpl:
private HashMap keyMap = new HashMap();
public KeyboardImplementation()
{
try
{
Keyboard.create();
keyMap.put(org.lwjgl.input.Keyboard.KEY_ESCAPE,KeyCode.vk_Escape);
keyMap.put(org.lwjgl.input.Keyboard.KEY_UP,KeyCode.vk_Up);
keyMap.put(org.lwjgl.input.Keyboard.KEY_DOWN,KeyCode.vk_Down);
keyMap.put(org.lwjgl.input.Keyboard.KEY_LEFT,KeyCode.vk_Left);
keyMap.put(org.lwjgl.input.Keyboard.KEY_RIGHT,KeyCode.vk_Right);
}
catch(Exception e)
{
throw new KeyboardException(
"Something appened while i was initializing the input " + e);
}
}
A che punto siamo con questa storia? Alla fine della giornata i task devono essere riassegnati, altrimenti rischiamo di non finirla.
Fatemi sapere qualcosa.
A che punto siamo con questa storia? Alla fine della giornata i task devono essere riassegnati, altrimenti rischiamo di non finirla.
Fatemi sapere qualcosa.
Merita un UP. :D
ciao ;)
Ok, il task e' libero. Prenotatevi pure.
dovrei aver quasi finito... l'input sembra rispondere bene: quando si fanno pressioni consecutive molto rapide la gemma si sposta tante volte quante sono le pressioni (non tiene conto del delay), mentre quando il tasto rimane premuto a lungo viene tenuto conto del delay e la gemma si sposta a tratti.
i test passano tutti ma ancora non faccio il commit perché reactToInput è arrivato a complessità ciclomatica 14 :mbe:
lo dividerò in una ventina di funzioni e poi committo, ora però devo uscire, ci penserò domani.
ah, una cosa che ho notato è che c'è una notevole duplicazione tra la gestione del tasto left e il tasto right, è questo che provoca una complessità così elevata; bisognerà fare qualcosa, anche perché non si può ulteriormente replicare quel codice nel caso al gioco venga aggiunta la gestione di un altro tasto :mc:
inoltre c'è un bel po' di monnezza da buttare sia in Input che in Grid: il bitset ormai non si usa più... ;)
dovrei aver quasi finito... l'input sembra rispondere bene: quando si fanno pressioni consecutive molto rapide la gemma si sposta tante volte quante sono le pressioni (non tiene conto del delay), mentre quando il tasto rimane premuto a lungo viene tenuto conto del delay e la gemma si sposta a tratti. [...]
Ottimo lavoro. Procedi con il refactoring. :)
caio ;)
BlueDragon
26-11-2005, 18:41
inoltre c'è un bel po' di monnezza da buttare sia in Input che in Grid: il bitset ormai non si usa più... ;)
Sono stato impegnato anche questa settimana ed alla fine è toccato tutto a te :)
Una curiosità, buttando via il bitset, come fai a sapere che un tasto è ancora premuto e la gemma va mossa ancora?
Io all'inizio avevo messo delle booleane in Grid che mi dicevano se era in corso un movimento a dx o sx (diventavano true quando c'era keypressed e false quando arrivava il keyreleased) poi però le avevo tolte ed avevo deciso di usare semplicemente input.isRightKeyPressed(), che interroga il bitset.
ho fatto il commit, ma come ho scritto nel log c'è un bug da correggere (la gemma si comporta in modo strano se le frecce dx e sx vengono premute contemporaneamente) e poi c'è da fare un discorso più complesso per quanto riguarda la duplicazione di codice per la gestione dei due tasti freccia.
BD chiede giustamente come faccio a sapere se i tasti sono attualmente premuti se non uso il BitSet; in effetti ho fatto come ha detto lui, cioè ho usato dei flag, i quali devono essere distinti per ciascuno dei due tasti; tuttavia non è possibile (secondo me) sostituire l'uso dei due flag con l'uso del BitSet perché oltre ai flag bisogna associare ai due tasti anche l'informazione del timestamp dell'ultima pressione, da usare poi assieme al parametro di delay per verificare poi ad ogni iterazione se la gemma deve essere spostata in risposta ad una pressione prolungata del tasto.
di conseguenza dopo la correzione del bug secondo me andrebbe creata una nuova classe Grid.KeyData per mantenere queste due informazioni, e Grid la dovrebbe istanziare due volte (una per ogni freccia). la gestione del tasto freccia giù invece non necessita dell'uso di KeyData perché è molto più semplice: quando arriva un evento vk_Down Pressed la gravità viene aumentata, quando arriva vk_Down Released viene rimessa al valore normale; la gestione della freccia giù insomma non richiede l'uso di parametri di delay ecc.
ho analizzato la situazione; per correggere l'errore mi serve di capire una cosa importante: reactToInput deve processare un solo evento della queue oppure la deve svuotare processando tutti quelli attualmente presenti??
Svuota tutta la coda e vediamo come esce.
ah ok, svuotando tutta la coda non c'è problema perché il bug scompare :)
allora faccio queste modifiche, casomai aggiungo un test apposito, e ricommitto.
EDIT: argh le mie manine stanno scrivendo da sole... O.o'
manine - fek, non dargli retta, *prima* scriveremo il test e *poi* facciamo le modifiche!!!
Altrimenti ti spezzo tutti i ditini :D
assurdo!! O.o'
il bug *non* scompare, però il relativo test passa!! O.o'
BlueDragon
27-11-2005, 12:24
assurdo!! O.o'
il bug *non* scompare, però il relativo test passa!! O.o'
Quale test? Quale bug?
Cmq io ho usato il BitSet ed 1 solo timestamp (tanto o stai andando a destra o stai andando a sinistra).
Oppure effettivamente stai andando sia a sinistra che a destra (cioè praticamente sei fermo) e l'uso di un solo timestamp ti aiuta a muovere contemporaneamente le due gemme nello stesso istante, quindi in pratica a tenerle ferme ;)
BlueDragon
27-11-2005, 13:31
Ho lanciato il gioco, presumo che il bug sia il fatto che tenendo premuto sx e dx contemporaneamente, la gemma si muove destra<->sinistra rapidamente.
Mi sembra che l'unico test che imponga alla gema di stare ferma con destra-sinistra è testNoMovementOnRightAndLeft().
public void testNoMovementOnRightAndLeft()
{
grid.insertGem(2, 4, gem);
grid.setGemUnderControl(gem);
input.generateKey(KeyCode.vk_Right);
input.generateKey(KeyCode.vk_Left);
grid.reactToInput(input, timer);
assertTrue(
"gem reacts erroneously to vk_Right and vk_Left pressed at the same time",
grid.isGemAt(2, 4));
}
Ovviamente questo test passa perché dice semplicemente che prememndo nello stesso istante destra e sinistra, la gemma non si sposta. Questo è vero (infatti il test passa) e cmq è una situazione che nella realtà non capita mai, perché si finisce sempre per premere uno dei due tasti qualche millisecondo prima dell'altro.
Il problema è che non c'è nessun test che dica: continuando a premere dx e sx, la gemma continua a rimanere ferma.
Eccoti un test che fallisce:
public void testNoMovementOnContinuousRightAndLeft()
{
grid.insertGem(2, 4, gem);
grid.setGemUnderControl(gem);
input.generateKey(KeyCode.vk_Right);
grid.reactToInput(input, timer);
timer.setTime(timer.getTime()+10);
input.generateKey(KeyCode.vk_Left);
grid.reactToInput(input, timer);
timer.setTime(timer.getTime() + grid.getInputDelay() - 5);
grid.reactToInput(input, timer);
assertTrue(
"Gem should not move when Left & Right key are both pressed",
grid.isGemAt(2, 4));
}
allora, vi riassumo la situazione: io BlueDragon e Jocchan ci siamo appena visti su MSN per chiarire la questione delle due frecce premute assieme.
la situazione che il giocatore prema due frecce precisamente nello stesso istante non è realistica, ma anche ammettendo che ciò accada ricordo che noi riceviamo gli input da una coda e quindi uno dei due eventi dovrà per forza di cose stare prima dell'altro :p
fatta questa premessa, attualmente accade che alla pressione contemporanea (o quasi) delle due frecce il programma reagisce spostando la gemma di una sola casella e poi lasciandola ferma; se arriva prima l'evento della freccia sinistra, la gemma verrà spostata a sinistra di una sola casella, altrimenti a destra.
secondo me non è possibile evitare questo comportamento perché reactToInput chiama i due handlers delle frecce destra e sinistra al termine di ogni iterazione del ciclo che svuota la coda; se così non facesse, cioè se chiamasse gli handlers al di fuori del ciclo, il problema delle pressioni contemporanee si risolverebbe, ma non avrei risposto al task perché la risposta alla sequenza di input potrebbe risultare imprecisa: gli handlers verrebbero chiamati dopo aver estratto tutti gli elementi dalla coda, e quindi alcuni eventi potrebbero non essere mai visti; gli eventi devono essere processati uno per volta, senza che un evento Released occulti un evento Pressed.
detto questo adesso devo fare un refactoring di fine storia per semplificare tutto il codice ed eliminare moltissime duplicazioni tra la gestione della freccia sinistra e della freccia destra.
Ok, serve molto refactoring. Tutto cio' che gestisce l'input dev'essere spostato in una classe InputReactor che per ora reagisce agli eventi Left, Right, Space richiamando delle call back registrate dal cliente (in questo caso la classe Grid).
Unico problema: come si fanno le callback in Java. In C# si usano delegate, quale paradigma i usa in in Java?
Ovviamente servono i test per questa nuova classe. Mi posso occupare io del refactoring questa sera, meglio ancora se qualcuno vuole farlo con me.
Ok, serve molto refactoring. Tutto cio' che gestisce l'input dev'essere spostato in una classe InputReactor che per ora reagisce agli eventi Left, Right, Space richiamando delle call back registrate dal cliente (in questo caso la classe Grid).
Unico problema: come si fanno le callback in Java. In C# si usano delegate, quale paradigma i usa in in Java?
Ovviamente servono i test per questa nuova classe. Mi posso occupare io del refactoring questa sera, meglio ancora se qualcuno vuole farlo con me.
Potremmo uasre una specie di Observer.
Una interfaccia InputReactorObserver che definisce il nome della callback. Grid dovrebbe implementare questa interfaccia. Durante la creazione di inputReactor si dovrebbe registrare l'istanza di grid tramite una attach.
ciao ;)
Potremmo uasre una specie di Observer.
Una interfaccia InputReactorObserver che definisce il nome della callback. Grid dovrebbe implementare questa interfaccia. Durante la creazione di inputReactor si dovrebbe registrare l'istanza di grid tramite una attach.
ciao ;)
Si', ci possiamo dirigere verso un Observer.
In C# farei qualcosa tipo questa:
delegate InputHandlerDelegate(KeyDaya key);
void RegisterHandler(InputHandlerDelegate handler)
{
...
}
void ReactToInput()
{
handler(key);
}
Ancora piu' sinteticamente userei gli eventi che si occupano della registrazione e rimozione dei delagati. In pratica implementano l'Observer pattern come costrutti del linguaggio (ancora meglio, sono costrutti del framework .NET).
C'e' niente di analogo in Java?
io un po' di refactoring già l'avevo fatto: ho tolto 115 linee di codice e ne ho aggiunte 72, risparmiandone quindi 43; così facendo ho eliminato le numerose duplicazioni di codice per la gestione dei tasti freccia destro e sinistro: ho fatto una copia singola di quel codice che ho messo in una classe interna a Grid.
ancora non ho committato, dimmi tu se vuoi che committo o se vuoi refattorizzare daccapo; non so se la classe che ho fatto, spostandola e facendola diventare pubblica può esserti utile per sviluppare la InputReactor di cui parli...
per le callback temo che non ci sia altra scelta se non quella di usare funzioni virtuali...
io comunque stasera non penso che farò questo refactoring, piuttosto inizierò il parser :p
io un po' di refactoring già l'avevo fatto: ho tolto 115 linee di codice e ne ho aggiunte 72, risparmiandone quindi 43; così facendo ho eliminato le numerose duplicazioni di codice per la gestione dei tasti freccia destro e sinistro: ho fatto una copia singola di quel codice che ho messo in una classe interna a Grid.
ancora non ho committato, dimmi tu se vuoi che committo o se vuoi refattorizzare daccapo; non so se la classe che ho fatto, spostandola e facendola diventare pubblica può esserti utile per sviluppare la InputReactor di cui parli...
per le callback temo che non ci sia altra scelta se non quella di usare funzioni virtuali...
Fai pure il commit, ci penso io con chi si vuole unire.
ho committato; scusa il ritardo ma prima sono uscito e poi ho cenato :p
se il codice del refactoring non ti serve fai l'update alla penultima revision anziché all'ultima.
a proposito, il codice che ho eliminato non l'ho cancellato, l'ho commentato (sono un paio di pezzi in Grid e anche un paio di metodi in Gem; quelli però mi sa che li ho proprio cancellati).
i test sono sempre gli stessi.
Ho risolto il problema del test sopra, ma ho dovuto modificare profondamente la classe... Grid ed in piccola parte quella di input (ho aggiunto un parametro timer per la generazione dei tasti)...
Faccio il commit oppure creo un nuovo branch ?
L'unico problema che rimane è un flickering che si ha quando si premono due tasti insieme (la gemma passa da una cella e ritorna a quella iniziale in poco tempo)...
Ho risolto il problema del test sopra, ma ho dovuto modificare profondamente la classe... Grid ed in piccola parte quella di input (ho aggiunto un parametro timer per la generazione dei tasti)...
Faccio il commit oppure creo un nuovo branch ?
Se ci sono tutti i test e il codice e' piu' semplice fai pure il commit.
Se ci sono tutti i test e il codice e' piu' semplice fai pure il commit.
Sicuramente come semplicità è paragonabile all'altro... I test sono gli stessi della versioen attuale e sono partito da quella per fare il refactoring... Ho tirato fuori un po' di metodi da ArrowKeyData...
Sicuramente come semplicità è paragonabile all'altro...
Allora e' ok, magari dai solo un'occhiata all'ultimo problema che hai segnalato prima.
E' il solito problema della gemma che si sposta velocemente a destra e a sinistra quando vengono premuti due tasti... Se premo prima il tasto destro e subito dopo il sx, la gemma si sposterà prima a destra e poi a sinistra... Se le pressioni capitano in due polling diversi (la frequenza di polling della tastiera è molto alta) si vede la gemma che per pochissimo tempo si sposta destra e poi di nuovo a sinistra... Se la pressione avviene nello stesso polling allora la gemma non si sposta...
Al contrario del codice attualmente nel repository, puoi premere mille volte i tasti destra e sinistra contemporaneamente e la gemma comunque si troverà sempre nella stessa posizione...
E' il solito problema della gemma che si sposta velocemente a destra e a sinistra quando vengono premuti due tasti... Se premo prima il tasto destro e subito dopo il sx, la gemma si sposterà prima a destra e poi a sinistra... Se le pressioni capitano in due polling diversi (la frequenza di polling della tastiera è molto alta) si vede la gemma che per pochissimo tempo si sposta destra e poi di nuovo a sinistra... Se la pressione avviene nello stesso polling allora la gemma non si sposta...
Hmmm, sta diventando tutto troppo complicato per un problema cosi' semplice da risolvere.
Si potrebbe stabilire un tempo minimo sotto il quale la pressione di due tasti opposti genera nessun evento, invece di generarne due.
Si potrebbe stabilire un tempo minimo sotto il quale la pressione di due tasti opposti genera nessun evento, invece di generarne due.
Sì, ma dovremmo fare il prelievo degli eventi almeno con un intervallo doppio di quel tempo minimo...e non solo, non basterebbe comunque...
Dai un'occhiata adesso al gioco...
in effetti, visto che gie venti vengono registrati, possiamo richiamare reactToInput ogni tot millisecondi...ora faccio una prova...
Già facendo uan cosa del genere le cose migliorano notevolamente:
private static void processInput()
{
if(input.isKeyEscape())
{
finished = true;
}
long timeStamp = timer.getTime();
if(timeStamp - lastInputProcessed >= 150)
{
lastInputProcessed = timeStamp;
grid.reactToInput(input, timer);
}
}
Ok, questo impone una frequenza di campionamento dell'input fissa, che e' una cosa positiva. Ma Jocchan deve darci la sua sensazione a riguardo dopo averlo provato.
A me la soluzione piace, e' semplice ed elegante.
La sensazione nel movimento normale non cambia... In ogni caso se arrivassero 2 pressioni all'interno dello stesso intervallo di tempo (cosa molto difficile in 150 ms) la gemma verrebbe spostata di due caselle...
Allora faccio un commit di questa modifica ?
La sensazione nel movimento normale non cambia... In ogni caso se arrivassero 2 pressioni all'interno dello stesso intervallo di tempo (cosa molto difficile in 150 ms) la gemma verrebbe spostata di due caselle...
Se poi vogliamo risolvere anche questo problema possiamo fare un'interpolazione della gemma al momento del disegno, in modo da creare un movimento fluido...
Con 120 secondo me si ottiene il rate migliore... Premendo due tasti qualche volta la gemma si vede spostare a destra e poi a sinistra...succede in circa il 10-15% dei casi...ed il movimento è più lento rispetto a prima...
Ho fatto il commit ed ho modificato il file GameConfig.xml: ho aggiunto/modificato RepeatDelay (la ripetizione di un tasto avviene dopo questo intervallo di tempo) e InputRate, che è il valore sopra impsotato a 120...
Ottimo. Vediamo che dice Jocchan ora.
Ho testato l'ultima build (la 285) e avevo l'impressione che la reattività agli input non fosse un granchè. Abbassando a 60 InputRate le cose migliorano notevolmente, ed il leggero flickering che avviene quando si premono sx e dx con una differenza di tempi bassissima è del tutto trascurabile (avviene insomma solo quando si VUOLE vedere cosa accade). Quindi, direi di settare 60 come default.
L'unica cosa che mi ha lasciato un pò perplesso è questa: generalmente, lasciando cadere le gemme fino a riempire la schermata, ci sono solo 6 caselle in cui queste non possono mai cadere (ovviamente non è un difetto di design, dato che le pietre che cadono dall'alto in ogni modalità possono sempre riempirle, e che con tutta probabilità quando avremo gemme che cadono a coppie il loro numero scenderà ulteriormente), ma in tutte le altre è tranquillamente possibile posizionare delle gemme.
Le caselle a cui mi riferisco sono queste in figura:
http://lnx.rc6.it/diamonds/varie/forbidden.jpg
Nella versione che ho testato oggi, a volte capita di non riuscire a piazzare le gemme anche in alcune caselle dove prima arrivavano tranquillamente. Ogni tanto ci arrivano, ma in maniera del tutto casuale. Ancora non posso dire se si tratta di una imprecisione rilevante o no, potrò farlo solo fra 2-3 cicli, ma alla fine dubito che potremo permetterci di ignorare questo fattore.
Se non è troppo complicato, e riusciamo a capirne subito la causa, direi di fixare subito il tutto... se invece la situazione fosse un pò troppo complessa, allora vedremo in seguito.
Scusa, ma modificando InputRate a 60 non influisci sulla reattività in quanto i tasti premuti vengono registrati... Comunque ora provo...
Ma le caselle tenti di raggiungerle tenendo premuto il tasto freccia o premendo velocemente il tasto ?
Hai provato a vedere il comportamento con entrambi i tasti premuti ? Rispetto a prima sembra notevolmente migliorato...
Ok...la reattività ai tasti è vero che sembra più immediata settandolo a 60...questo perchè in media passa meno tempo fra un aggiornamento e l'altro...
Prova anche la reazione ai due tasti premuti contemporaneamente...
Il comportamento con entrambi i tasti premuti è infinitamente migliorato... non siamo alla perfezione, ma ci siamo vicini... e data la marginalità della cosa il risultato va più che bene :)
L'unica cosa ancora da verificare è l'incidenza del problema che ho esposto nel mio ultimo post, e che credo derivi dalla reattività della gemma ai comandi.
Scusa, ma modificando InputRate a 60 non influisci sulla reattività in quanto i tasti premuti vengono registrati... Comunque ora provo...
Ma le caselle tenti di raggiungerle tenendo premuto il tasto freccia o premendo velocemente il tasto ?
Ovviamente in entrambi i modi :)
La reattività a due tasti premuti insieme secondo me difficilmente potrà essere perfetta... Aumenti la qualità della reazione ai due tasti aumentando inputRate
Intendi il fatto di non poter raggiungere sempre alcune celle ? Ma lo facevi tenendo premuto il pulsante freccia ? In tal caso basta diminuire RepeatDelay ;)
Abbassando RepeatDelay diminuisci la distanza fra due ripetizioni del tasto premuto...
Premendo velocemente i tasti io riesco a raggiungere tutti i punti...anche quello che hai contrassegnato con una X sulla seconda riga...
Il fatto che non riesca sempre a raggiungerli premendo velocemente dipende dal fatto che non premiamo mai con la stessa frequenza i tasti... Se alcune celle le raggiungiamo una volta a fortuna, non è detto che le possiamo raggiungere sempre...
La reattività a due tasti premuti insieme secondo me difficilmente potrà essere perfetta... Aumenti la qualità della reazione ai due tasti aumentando inputRate
Intendi il fatto di non poter raggiungere sempre alcune celle ? Ma lo facevi tenendo premuto il pulsante freccia ? In tal caso basta diminuire RepeatDelay ;)
Lo facevo sia tenendo premuto (cosa che il giocatore farà quasi sempre) che premendo a ripetizione... senza nessun risultato.
Aumentando inputRate però si ha l'impressione di avere una gemma più lenta e che risponde meno ai comandi...
Infatti...devi trovare il valore più giusto per entrambi...attento che se abbassi troppo il RepeatDelay una semplice pressione un poì più lunga viene ripetuta...
Premendo velocemente i tasti io riesco a raggiungere tutti i punti...anche quello che hai contrassegnato con una X sulla seconda riga...
Hai ragione, ci sono riuscito anche io, cosa che prima non accadeva mai :D
Il fatto che non riesca sempre a raggiungerli premendo velocemente dipende dal fatto che non premiamo mai con la stessa frequenza i tasti... Se alcune celle le raggiungiamo una volta a fortuna, non è detto che le possiamo raggiungere sempre...
Appunto per questo dicevo che bisogna valutare l'incidenza della cosa ^_^ La mia paura è che questa imprecisione potesse portare a una reattività imprevedibile nelle situazioni concitate.
Comunque è anche vero che non bisogna fasciarsi la testa prima di averla rotta, quindi attendiamo di avere le coppie di gemme prima di vedere se occorre intervenire ulteriormente :)
Prova con 70 per InputDelay e con 170 per RepeatDelay...
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.