2012-03-28 5 views
7

Volevo scrivere un semplice gioco in Android usando opengl es, ma ho subito avuto problemi con il ciclo di gioco principale. Come ho letto qui: http://developer.android.com/resources/tutorials/opengl/opengl-es10.html l'app chiama public void onDrawFrame(GL10 gl) ogni volta che è necessario ridisegnare la superficie. O smth così. Il mio problema era: come creare un ciclo di aggiornamento indipendente dalle chiamate di estrazione. Voglio dire - non può funzionare così, non posso aggiornare la logica di gioco solo quando il dispositivo (app?) Vuole ridisegnare la superficie (o sbaglio?).Come creare un ciclo di aggiornamento/disegno in modo appropriato con Android opengl?

Dopo aver cercato su google, sono giunto alla conclusione che devo creare un altro thread per il ciclo di aggiornamento. Ma poi ho avuto un altro problema: con un thread che si occupava del disegno e un altro che si occupava di aggiornare la logica del gioco, non sapevo come farli cooperare. Innanzitutto, erano due classi separate (almeno nella mia implementazione) quindi non potevano usare le stesse variabili di gioco e oggetti (sprite, timer, variabili diverse, contatori e così via ... praticamente tutto ciò che queste due classi dovevano fare i loro lavori). Ora penso che potrei in qualche modo riunirli entrambi in una classe. Ma - in secondo luogo, avevo bisogno di sincronizzare i due fili in qualche modo.

Infine, sono uscito con questa idea generale:

  • 3 classi:
    1. public class MyRenderer implements GLSurfaceView.Renderer con onSurfaceCreated() metodo prendersi cura del disegno
    2. public class UpdateThread implements Runnable con run() e update() metodi. chiamava il metodo update() esattamente 60 volte al secondo (volevo un ciclo a passo fisso)
    3. public class SpritesHolder usato come contenitore per tutti gli oggetti/variabili/oggetti del gioco (come sprite, timer, variabili di stato e così via ...) con tutti i campi pubblici.
  • Quindi, in pratica la classe SpritesHolder era una scatola contenente tutti le variabili necessarie in un unico luogo, in modo da MyRenderer e UpdateThread classi potevano accedervi ed utilizzarlo.
  • Per quanto riguarda la sincronizzazione - ho appena fatto dovrebbe occupare in questo modo:

    public void update(float delta) 
    { 
        synchronized (spritesHolder) 
        { 
         // whole method code... 
        } 
    } 
    

    e:

    public void onDrawFrame(GL10 gl) 
    { 
        synchronized (spritesHolder) 
        { 
         // whole method code... 
        } 
    } 
    

    in modo che entrambi i fili non ha utilizzato lo spritesHolder allo stesso tempo. Quindi gli aggiornamenti sono stati eseguiti 60 volte al secondo e il disegno ha avuto luogo ogni volta che l'app (dispositivo?) Era necessaria.

Un sacco di parole, mi dispiace, ho quasi finito di scrivere questo post. ;) Ad ogni modo - questo (descritto sopra) funziona e ho persino scritto qualche gioco basato su questo 'modello' ma penso che le mie idee potrebbero essere pazzesche e si può definire tutto molto meglio. Sarei molto grato per tutti i commenti e i consigli.

+0

solo una parola: http://obviam.net/ :) – akonsu

risposta

1

La soluzione probabilmente funzionerà, anche se potrebbe non essere ottimale per le prestazioni. Se ci pensi, il thread di rendering e il thread di aggiornamento non hanno davvero bisogno di essere esclusivi al 100%.

Di recente ho trascorso un po 'pensando a questo per il mio gioco, e questo è quello che ho finito per venire con (non necessariamente migliore, ma semplicemente qualcosa a cui pensare):

Per ogni oggetto renderable, ho un oggetto di proprietà del thread di aggiornamento e un altro oggetto di proprietà del thread di rendering che è solo un semplice contenitore che contiene istruzioni su come disegnare un oggetto (matrice del modello, valori uniformi, meshID, ecc.). Corro attraverso il thread di aggiornamento calcolando tutte le posizioni e i valori finali per i miei oggetti renderizzabili, quindi eseguo la sincronizzazione per una piccola finestra in cui passo i valori modificati durante quel frame agli oggetti nel thread renderizzabile. Quindi, non appena le nuove informazioni sono passate, il rendering del frame inizia mentre il successivo frame di aggiornamento viene eseguito simultaneamente. Perché c'è solo una piccola finestra di esclusione consente l'aggiornamento e disegnare i thread per eseguire contemporaneamente la maggior parte del tempo.

1

Non l'ho ancora usato con OpenGL, ma UpdateThread dovrebbe essere un TimerTask. In te l'attività di lancio con

new Timer().schedule(new UpdateThread(view), 0, 15); //where view is your view, and 15 is the wait time 

nel vostro TimerTask chiamano la vista con un Handler. per esempio (nella classe di visualizzazione)

Handler refreshHandler = new Handler() { 
    public void handleMessage(Message msg) { 
       //Handle it; e.g. for canvas invalidate() 
      }}; 

Nel vostro metodo run UpdateThread fare questo:

view.refreshHandler.sendMessage(new Message()); 

Ora è indipendente dalla vostra gameloop. Inoltre il tuo gameloop non dovrebbe essere nel MainActivity ma in un Thread (OO unidirezionale).