2012-10-02 9 views
20

To pytanie dotyczy reaktywnych symulacji bananów i czasu rzeczywistego ze składnikiem fizycznym i wizualnym (np. Grami).Jak zaimplementować pętlę gry w reaktywnym bananie?

Zgodnie z idealnym sposobem na ustawienie pętli gry (przy założeniu, że fizyka musi być odtwarzalna), potrzebny jest ustalony czas między klatkami. Po uwzględnieniu szeregu rzeczywistych powikłań, autor przybywa na tej pętli gra:

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); 
} 

konspekt jest to, że symulacja fizyki jest zawsze podawany ten sam przyrost czasu (dt) dla stabilności numerycznej. Aranżując to, należy wziąć pod uwagę, że fizyka i wizualizacje mogą być aktualizowane na różnych częstotliwościach, a Ty nie chcesz być zbyt daleko w tyle.

Na przykład możesz chcieć aktualizacji z częstotliwością 20 Hz, ale aktualizację wizualną z liczbą klatek na sekundę 60 Hz. Ta pętla wykonuje liniową interpolację fizyki, aby uzupełnić różnice między aktualizacjami fizyki a aktualizacjami graficznymi.

Dodatkowo, gdy różnica w czasie pomiędzy klatkami jest znacznie większy niż dt jest pętla obsłużyć intensywniejszej aktualizacje w kawałki dt. Notatka o spirali śmierci odnosi się tylko do przypadku, w którym obliczenia fizyczne nie są w stanie nadążyć za pożądaną częstotliwością aktualizacji, więc pozwala się pominąć niektóre aktualizacje.

W tej dyskusji najbardziej interesuje mnie układ, w którym wywołanie silnika fizyki (wywołanie integrate) jest zawsze przekazywane przez dt. Czy reaktywny banan pozwala użytkownikowi napisać tę pętlę stylu? Jeśli tak to jak? Być może przykład symulacji fizyki w czasie rzeczywistym jest w porządku (lub już istnieje)?

Odpowiedz

19

W tej dyskusji najbardziej interesuje mnie układ, w którym wywołanie silnika fizyki (wezwanie do integracji) jest zawsze przekazywane przez dt. Czy reaktywny banan pozwala użytkownikowi pisać tę pętlę stylu?

Tak, może to zrobić reaktywny banan.

Chodzi o to, że piszesz niestandardową pętlę zdarzeń i podpinasz do niej reaktywny banan. Biblioteka nie przyjmuje żadnych założeń co do tego, skąd bierze się twoje wydarzenia, "tylko" rozwiązuje problem starannego opisywania nowych zdarzeń w kategoriach już istniejących. W szczególności można użyć funkcji newAddHandler, aby utworzyć dwie funkcje wywołania zwrotnego, które są wywoływane w odpowiednich miejscach w pętli zdarzeń. Zasadniczo, reaktywny banan jest po prostu zadziwiającą metodą pisania zwykłych funkcji zwrotnych, które utrzymują stan. Kiedy i jak nazywasz te funkcje, zależy od Ciebie.

Oto ogólny zarys:

-- 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 
} 

W rzeczywistości napisałem game loop example w tym stylu dla starszej wersji biernej-banana, ale nie dostał około do polerowania i opublikowanie go w hackage. Ważne rzeczy, które chciałbym zobaczyć, to:

  • Wybierz silnik graficzny, który jest łatwy w instalacji i działa w GHCi. Koncepcja wykorzystuje SDL, ale jest to naprawdę niezręczne, ponieważ nie można go użyć z GHCi. Coś jak OpenGL + GLFW byłoby miłe.
  • Zaoferuj małą abstrakcję, aby ułatwić zapisanie fazy interpolacji. Prawdopodobnie tylko dwie rzeczy: zdarzenie eTimer :: Event t() która reprezentuje regularne aktualizacje fizyki i zachowanie bSinceLastTimer :: Behavior t TimeDiff który mierzy czas od ostatnich aktualizacjach fizyki, które mogą być wykorzystywane do wykonywania interpolacji. (Jest to zachowanie, a nie zdarzenia, a więc wewnętrzny „wyciągnąć to!” Aktualizacje są przejrzyste.)

Andreas Bernsteina blackout clone using reactive-banana może być doskonałym przykładem do realizacji w tym stylu.

+0

nie jestem pewien o SDL, ale z OpenGL i GLFW oboje użycie pamięć lokalna wątku na oryginalnym przewlec procesu (to musi być to oryginalny wątek, ograniczenia sprzedawca). GHCi domyślnie uruchamia każde polecenie w innym wątku. Oznacza to, że biblioteki takie jak OpenGL/GLFW (i kilka innych bibliotek GUI) nie mogą poprawnie uzyskać dostępu do lokalnej pamięci wątków i przejść z GHCi. Rozwiązaniem jest dodanie -fno-ghci-sandbox przy uruchamianiu GHCi. Można spróbować i zobaczyć, czy to rozwiązuje swoje nieszczęścia SDL + GHCi: http://www.haskell.org/ghc/docs/7.0.1/html/users_guide/release-7-0-1.html –

+0

To trochę trudniejsze niż "-fno-ghci-piaskownica", obawiam się. Na komputerze Mac, SDL musi zostać skompilowany, ponieważ redefiniuje 'main' jako makro. Wersje GLFW ulegają awarii w GHCi z powodu innych niezgodności, których nie rozumiem. –

+1

GLFW-b nie powinien ulegać awarii z GHCi, jeśli używasz '-fno-ghci-sandbox'. Jeśli tak, to trafisz na nowy błąd, więc zgłoś raport! :) –

Powiązane problemy