Jak zaimplementować pętlę gry w reactive-banana?
To pytanie jest specyficzne dla symulacji reaktywno-bananowych i w czasie rzeczywistym z komponentem fizycznym i wizualnym (np., gry).
Zgodnie z Napraw swój Timestep! idealny sposób na skonfigurowanie pętli gry (zakładając, że fizyka musi być powtarzalna), wymaga stałego czasu między klatkami. Po rozważeniu wielu prawdziwych komplikacji , autor przybywa do tej pętli gry:
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 );
}
Streszczenie jest takie, że symulacja fizyki jest zawsze karmiona tym samym przyrostem czasu (dt
) dla stabilności numerycznej. Organizując to, należy wziąć pod uwagę, że fizyka i grafika mogą być aktualizowane z różnymi częstotliwościami i nie chcesz być zbyt daleko w tyle.
Na przykład możesz chcieć aktualizacji z częstotliwością 20 Hz, ale wizualnej aktualizacji z szybkością klatek 60 Hz. Ta pętla wykonuje liniową interpolację fizyki, aby uzupełnić różnicę między aktualizacjami fizyki a aktualizacjami graficznymi.
Dodatkowo, gdy różnica w czasie między klatkami jest znacznie większa niż {[1] } istnieje pętla do obsługi stopniowania aktualizacji w kawałkach dt
. Notatka o spirali śmierci odnosi się tylko do przypadku, gdy obliczenia fizyki po prostu nie mogą nadążyć z żądaną częstotliwością aktualizacji, więc pozwalasz mu pominąć niektóre aktualizacje.
W tej dyskusji najbardziej interesuje mnie ułożenie tak, aby wywołanie silnika fizyki (wywołanie do integrate
) było zawsze poprzedzone dt
. Czy reactive-banan pozwala użytkownikowi napisać ten styl pętla? Jeśli tak, to w jaki sposób? Może przykład wykonywania symulacji fizyki w czasie rzeczywistym jest w porządku (lub już istnieje)?
1 answers
Tak, reaktywny-banan może to zrobić.W tej dyskusji najbardziej interesuje mnie zorganizowanie tak, aby wezwanie do silnika fizyki (wezwanie do integracji) było zawsze poprzedzone dt. Czy reactive-banan pozwala użytkownikowi na zapisanie tej pętli stylu?
Chodzi o to, że piszesz niestandardową pętlę zdarzeń i hookujesz do niej reactive-banana. Biblioteka nie stawia żadnych założeń co do tego, skąd pochodzą Twoje wydarzenia, "tylko" rozwiązuje problem zgrabnie opisywanie nowych wydarzeń 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, reactive-banan jest po prostu oszałamiającą metodą pisania zwykłych funkcji zwrotnych, które utrzymują stan. Kiedy i jak wywołasz 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 przykład pętli gry w tym stylu dla starsza wersja reactive-banana, ale nie doszła do polerowania i opublikowania go na hackage. Ważne rzeczy, które chciałbym zobaczyć zakończone są:
- wybierz silnik graficzny, który jest łatwy w instalacji i działa w GHCi. Koncepcja wykorzystuje SDL, ale jest to naprawdę dość niewygodne, ponieważ nie można go używać z GHCi. Coś w rodzaju OpenGL + GLFW byłoby miłe.
- Zaproponuj małą abstrakcję, aby ułatwić pisanie fazy interpolacji. Prawdopodobnie tylko dwie rzeczy: Zdarzenie
eTimer :: Event t ()
, które reprezentuje regularne aktualizacje fizyki i zachowaniebSinceLastTimer :: Behavior t TimeDiff
, które mierzy czas od ostatniej aktualizacji fizyki, które można wykorzystać do wykonania interpolacji. (Jest to zachowanie zamiast zdarzenia, więc wewnętrzne " narysuj to!"aktualizacje są przejrzyste.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-02 17:14:22