FPS: Konstantní rychlost animace

FPS je zkratka z počátečních písmen slov Frames Per Second, která by se dala do češtiny přeložit jako počet snímků za sekundu. Tato tři písmena jsou spásou při spouštění programů na různých počítačích. Vezměte si hru, kterou programátor začátečník vyvíjí doma na svém počítači o rychlosti, řekněme, Pentium II. Dá ji kamarádovi, aby se na ni podíval a zhodnotil. Kamarád má doma P4, spustí ji a vše je šíleně rychlé. Díky FPS se toto nikdy nestane, na jakémkoli počítači půjde hra vždy stejně rychle.

Základem všeho je čas, který uběhl mezi jednotlivými průchody renderovací funkcí. Uvedu příklad. Máme raketu, která má letět řekněme 50 jednotek za sekundu. Víme, že mezi tímto a předchozím vykreslením uběhlo 0,1s, takže ji posuneme o 5 jednotek. Tímto způsobem budou posuny objektů v animaci za určitý čas vždy konstantní, nezávisí na rychlosti počítače ani aktuální zátěži procesoru.

Výsledkem celého tohoto článku bude velmi jednoduchá třída, která všechny potřebné operace implementuje a ukázka jejího použití.

Třída FPS obsahuje tři atributy: čas při minulém průchodu vykreslovací funkcí, aktuální čas a hodnotu FPS. O aktualizaci proměnných se stará funkce Vypocet(), která se musí volat stejně často jako překreslení scény, nejlépe přímo ve funkci, která ho má na starosti. GetFPS() je vloženo z důvodu zapouzdřenosti dat.

class FPS// Třída FPS

{

private:

unsigned int stary_cas;

unsigned int aktualni_cas;

double fps;// Počet snímků za sekundu

public:

void Vypocet();// Volat před každým překreslením scény

inline double GetFPS();// Kvůli zapouzdřenosti dat

{

return fps;

}

};

Pro získání aktuálního času můžeme použít libovolnou funkci. Ve Windows se většinou používá GetTickCount(), která vrací počet milisekund od spuštění systému. Protože jsem začal při programování používat knihovnu SDL (Umožňuje snadné portování aplikace do různých operačních systémů - Linux, Win, Mac OS, BeOS, FreeBSD...), použiji v tomto článku SDL_GetTicks(), která vrací počet milisekund od inicializace SDL. Výpočet FPS je velmi jednoduchý. Převedeme rozdíl časů na sekundy a vydělíme jím jedničku, která reprezentuje jedno vykreslení. Na konci přiřadíme starému času aktuální čas.

void FPS::Vypocet()

{

aktualni_cas = SDL_GetTicks();// Vrátí počet milisekund od inicializace SDL

// aktualni_cas = GetTickCount();// Specifické pro Windows

fps = 1.0 / ((aktualni_cas - stary_cas) / 1000.0);// Počet snímků za sekundu

stary_cas = aktualni_cas;// Pro další průchod

}

Jak použít tuto třídu? Řekněme, že v OpenGL potřebujeme krychli, která rotuje o 45° za sekundu. Při každé aktualizaci úhlu natočení k němu přičteme plných 45°, které ale musíme vydělit hodnotou FPS. Jednotlivé posuny se rozfázují tak, aby za sekundu byla krychle natočená o požadovaných 45°.

FPS fps;// Objekt třídy

double uhel = 0.0;// Úhel natočení krychle

void DrawGLScene(GLvoid)// Vykreslování

{

fps.Vypocet();// Aktualizace FPS

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Vymaže obrazovku a hloubkový buffer

glLoadIdentity();// Reset matice

glTranslated(0.0, 0.0, -6.0);// Posun do hloubky

glRotated(uhel, 0.0, 1.0, 0.0);// Otočí krychli v závislosti na FPS

// Vykreslení krychle

uhel += 45.0 / fps.GetFPS();// Aktualizace úhlu natočení v závislosti na FPS

}

Je nutné poznamenat, že nic není idální. Pokud by jedno vykreslení bylo tak náročné, že by trvalo, přeženu, deset sekund, rychlost by sice zůstala na konstantních 45° za sekundu, ale vykreslovalo by se až po celých deseti sekundách (FPS = 0,1). Dovedete si představit to trhání?! Obecně se říká, že by FPS nikdy nemělo klesnout pod 30. Nic nám nebrání, abychom vložili za výpočet FPS podmínku if(fps.GetFPS < 30) sniž kvalitu renderingu; Mohlo by jím být například změna kvality textur GL_LINEAR na GL_NEAREST, vypnutí antialiasingu, odstranění některých efektů a podobně.

FPS však není jedinou možností, jak zajistit plynulost animace. Můžeme mít systémový timer (ve Windows zpráva WM_TIMER), který periodicky volá renderovací funkci. Tuto techniku osobně používám jen když není žádná jiná možnost (vždy je jiná možnost), protože automatické vykreslování v hlavní smyčce je vždy rychlejší a spolehlivější. Pokud nastavíte timer na moc krátký čas (cca. 50 ms a méně), budou se vícenásobně poslané zprávy mazat. Aby nedošlo k zahlcení fronty zpráv, systém do ní nikdy neumístí více než jednu zprávu WM_TIMER. Ostatní se neberou v úvahu, jako by se vůbec neposlali.

napsal: Michal Turek - Woq <WOQ (zavináč) seznam.cz>

Zdrojové kódy