2012-10-02 19 views
20

Questa domanda è specifica per le simulazioni di banana reattiva e in tempo reale con una componente fisica e visiva (ad es. Giochi).Come implementare un loop di gioco in banana reattiva?

In base allo Fix Your Timestep! il modo ideale per impostare un loop di gioco (supponendo che la fisica debba essere riproducibile), è necessario un intervallo di tempo fisso tra i frame. Dopo aver considerato una serie di complicazioni reali, l'autore arriva a questo ciclo di gioco:

double t = 0.0; 
const double dt = 0.01; 

double currentTime = hires_time_in_seconds(); 
double accumulator = 0.0; 

State previous; 
State current; 

while (!quit) 
{ 
    double newTime = time(); 
    double frameTime = newTime - currentTime; 
    if (frameTime > 0.25) 
      frameTime = 0.25; // note: max frame time to avoid spiral of death 
    currentTime = newTime; 

    accumulator += frameTime; 

    while (accumulator >= dt) 
    { 
      previousState = currentState; 
      integrate(currentState, t, dt); 
      t += dt; 
      accumulator -= dt; 
    } 

    const double alpha = accumulator/dt; 

    State state = currentState*alpha + previousState * (1.0 - alpha); 

    render(state); 
} 

La sinossi è che la simulazione fisica è sempre alimentato lo stesso incremento di tempo (dt) per la stabilità numerica. Organizzare per questo deve considerare che fisica e immagini possono essere aggiornate a frequenze diverse e non si vuole essere troppo indietro.

Ad esempio, è possibile che si desiderino aggiornamenti a una frequenza di 20 hz, ma un aggiornamento visivo con un framerate di 60 hz. Questo ciclo interpola linearmente la fisica per compensare la differenza tra gli aggiornamenti di fisica e gli aggiornamenti grafici.

Inoltre, quando la differenza di tempo tra i frame è molto maggiore di dt, esiste un ciclo per gestire l'aggiornamento degli aggiornamenti in blocchi di dt. La nota sulla spirale della morte si riferisce solo a un caso in cui il calcolo della fisica semplicemente non riesce a tenere il passo con la frequenza desiderata di aggiornamenti, quindi gli consenti di saltare alcuni aggiornamenti.

Per questa discussione, la parte che mi interessa di più è organizzare in modo che la chiamata al motore fisico (la chiamata a integrate) venga sempre preceduta da dt. banana reattiva consente all'utente di scrivere questo ciclo di stile? Se é cosi, come? Forse un esempio di simulazione fisica in tempo reale è in ordine (o esiste già)?

risposta

19

Per questa discussione, la parte che mi interessa di più è organizzare in modo che la chiamata al motore fisico (la chiamata per l'integrazione) venga sempre eseguita da dt. La banana reattiva consente all'utente di scrivere questo ciclo di stile?

Sì, la banana reattiva può farlo.

L'idea è che si scrive un ciclo di eventi personalizzato e si aggancia la banana reattiva a quello. La biblioteca non fa alcuna ipotesi su dove ottenere i tuoi eventi, ma "solo" risolve il problema di descrivere ordinatamente i nuovi eventi in termini di quelli esistenti. In particolare, è possibile utilizzare la funzione newAddHandler per creare due funzioni di callback chiamate nelle posizioni appropriate nel ciclo degli eventi. Essenzialmente, la banana reattiva è solo un metodo sbalorditivo per scrivere le normali funzioni di callback che mantengono lo stato. Quando e come chiamate queste funzioni dipende da voi.

Ecco un quadro generale:

-- set up callback functions 
(renderEvent, render) <- newAddHandler 
(stateUpdateEvent, stateUpdate) <- newAddHandler 

-- make the callback functions do something interesting 
let networkDescription = do 
    eRender  <- fromAddHandler renderEvent 
    eStateUpdate <- fromAddHandler stateUpdateEvent 
    ... 
    -- functionality here 

actuate =<< compile networkDescription 

-- event loop 
while (! quit) 
{ 
    ... 
    while (accumulator >= dt) 
    { 
     stateUpdate (t,dt)  -- call one callback 
     t += dt 
     accumulator -= dt 
    } 
    ... 
    render()     -- call another callback 
} 

In realtà, ho scritto una game loop example in questo stile per una versione precedente di reattivo-banana, ma non hanno ottenuto intorno alla lucidatura e la sua pubblicazione su hackage. Le cose importanti che vorrei vedere completate sono:

  • Scegliere un motore grafico che sia facile da installare e funzioni in GHCi. Il concetto utilizza SDL, ma questo è davvero abbastanza imbarazzante in quanto non può essere utilizzato da GHCi. Qualcosa come OpenGL + GLFW sarebbe bello.
  • Offrire una piccola astrazione per semplificare la scrittura della fase di interpolazione. Probabilmente solo due cose: un evento eTimer :: Event t() che rappresenta gli aggiornamenti fisici regolari e un comportamento bSinceLastTimer :: Behavior t TimeDiff che misura il tempo trascorso dagli ultimi aggiornamenti fisici, che possono essere utilizzati per fare l'interpolazione. (Si tratta di un comportamento, invece di un evento, in modo che il interno "disegnare questo!" Aggiornamenti sono trasparenti.)

Andreas Bernstein blackout clone using reactive-banana può essere un ottimo esempio per implementare in questo stile.

+0

Non sono sicuro di SDL, ma con OpenGL e GLFW entrambi utilizzano la memorizzazione locale dei thread sul thread originale del processo (deve essere il thread originale, la limitazione del fornitore). GHCi esegue ciascun comando in un thread diverso per impostazione predefinita. Ciò significa che le librerie come OpenGL/GLFW (e molte altre librerie gui) non possono accedere correttamente alla loro memoria locale dei thread e andare in giro da GHCi. La soluzione è aggiungere -fno-ghci-sandbox all'avvio di GHCi. Potresti provare questo e vedere se corregge i tuoi guai SDL + GHCi: http://www.haskell.org/ghc/docs/7.0.1/html/users_guide/release-7-0-1.html –

+0

È un po ' più difficile di '-fno-ghci-sandbox', temo. Su Mac, SDL deve essere compilato perché ridefinisce 'main' come macro. Le versioni GLFW tendono a bloccarsi in GHCi a causa di altre incompatibilità che non capisco. –

+1

GLFW-b non dovrebbe bloccarsi da GHCi se si utilizza '-fno-ghci-sandbox'. Se lo fa, stai colpendo un nuovo bug quindi per favore fai un rapporto! :) –

Problemi correlati