9. diel - SDĽ - Práca s časovačom
V dnešnom diele sa pozrieme na časovač. Ukážeme si funkcie pre uspanie, spustenie funkcie po určitom čase a nakoniec si povieme niečo o tom, ako môžeme hru optimalizovať, aby sme čo najmenej zaťažovali procesor a zároveň aby mal používateľ čo najlepší zážitok.
Uspanie vlákna
Ekvivalentom uspanie vlákna je v SDL funkcia SDL_Delay. Tá prijíma ako parameter počet milisekúnd, po ktorej vlákno uspí. Je nutné si uvedomiť, že sa nejedná o presnú hodnotu, ale o minimálnu hodnotu, počas ktorého bude vlákno uspanie. To, za ako dlho bude vlákno opäť aktivované, závisí na plánovači v operačnom systéme. Zaujímavou hodnotou je 0. Ak funkciu odovzdáme hodnotu 0, povieme vlastne operačnému systému, že teraz už nepotrebujeme nič robiť. Môžeme si to predstaviť tak, že operačný systém vlákno neúspech, len pokračuje v cykle plánovania.
Druhou vecou, ktorú si musíme uvedomiť je, že uspanie a prebudenie vlákna niečo stojí. Nejakú dobu teda trvá, než sa vlákno uspí a než sa opäť prebudí. Experimentálne bola táto hodnota určená na 10ms. Hodnoty pod 10ms teda nebudú presné a spravidla bude čas uspanie vždy vyššia.
Časovača
Aby sme mohli časovač používať, musíme do SDL_Init
odovzdať parameter SDL_INIT_TIMER
.
Časovač sa aktivuje po nami zadanej dobe. Pre prácu má SDL funkcie SDL_AddTimer a SDL_RemoveTimer. Ako prvý parameter funkcia prijíma počet milisekúnd, za ktorú sa akcie spustí. Ako druhý parameter je callback, ktorý sa zavolá. Tretím parametrom je ukazovateľ na void, ktorý sa odovzdá callbacku pri volaní. Callback prijíma ako prvý parameter interval, v ktorom SDL odovzdá čas, ktorý ubehol od posledného zavolanie. Ako druhý parameter je odovzdaný ukazovateľ, ktorý sme nastavili tretím parametrom pri vytváraní časovača. Callback vracia čas v milisekundách, ktorý má SDL čakať, než znovu funkciu zavolá. Môžeme teda interval vždy meniť, ale len v závislosti na vrátených hodnotách z callbacku. Ak callback vráti hodnotu 0, časovač sa zruší a nebude znovu zavolaný.
Dôležité je, že callback beží v samotnom vlákne. S tým súvisí aj kontext vlákna. Nemáme prístup k premenným, ktoré sme v hlavnom vlákne vytvorili. Musíme si dať tiež pozor na prístup do pamäti. Ak chceme rovnakú triedu používať v hlavnom vlákne aj v callbacku, je ju potrebné uzamknúť a používať atomické operácie. Práca s viacerými vláknovými aplikáciami je mimo rozsah tohto seriálu, a preto nebudem tieto problémy ďalej rozoberať, a odporúčam časovač používať opatrne.
Získanie uběhnutého času
Čas môžeme v SDL získať dvoma spôsobmi. Čas sa počíta od
inicializácia SDL (od volanie funkcie SDL_Init
). To tiež
znamená, že informácie sú väčšinou adekvátne vzhľadom k inému volanie
funkcie. Pokiaľ budeme chcieť zistiť, ako dlho trvá nejaká časť kódu,
zavoláme funkciu pred a po operácii. Po odpočítaní týchto hodnôt
zistíme, ako dlho operácia trvala.
Prvý z funkcií je SDL_GetTicks. Tá vracia čas v milisekundách. Vracia Uint32, preto približne po 49 dňoch hodnota pretečie a funkcia bude vracať rovnaké hodnoty ako na začiatku. Ak budeme robiť aplikáciu, ktorá môže bežať tak dlho, je potreba s tým počítať.
Druhou funkciou je SDL_GetPerformanceCounter. Je oveľa presnejšie ako SDL_GetTicks, ale vracia počet tikov. Budeme ju používať predovšetkým pre optimalizáciu, aby sme presne vedeli, ako dlho akcia trvala. Tentoraz vracia funkcie Uint64, musíme si dať teda pozor, aby sme výslednú hodnotu neuložili v jednoduchom Integer. Po prvé je to hodnota bez znamienka a za druhé by veľmi rýchlo pretiekol. Ak budeme chcieť získať čas v sekundách, budeme potrebovať funkciu SDL_GetPerformanceFrequency. Navracia počet tikov za sekundu. Po vydelení dostaneme čas v sekundách. Ak budeme chcieť získať milisekundy, musíme hodnotu vrátenú SDL_GetPerformanceCounter vynásobiť tisíckou.
Použitie iných knižníc
Použitie SDL časovanie je čisto dobrovoľné. Samozrejme existujú aj ďalšie knižnice, ktoré plnia rovnakú funkciu, a často aj oveľa lepšie. Nie je mojím cieľom tu popisovať všetky knižnice a ich použitia. Pre začiatok Odkazy na Ctíme a chrono ktoré by mali byť multiplatformový a podporované väčšinou kompilátorov. Ak máte stále málo, stačí použiť Google.
Optimalizácia FPS
Pre optimalizáciu FPS (alebo tiež frame-rate) existuje niekoľko spôsobov. Každý má svoje výhody a nevýhody. Nebudem sa konkrétne zaoberať implementáciou, popíšem iba spôsoby, akými môžeme optimalizácia docieliť.
Prečo optimalizovať
Ľudské oko má svoje obmedzenia. Do frekvencia 24-30 snímok za sekundu dokážeme rozoznať jednotlivé obrazy medzi sebou. So zvyšovaním snímok za sekundu sa obraz stáva plynulejší. Pri frekvencii nad 60Hz už oko nerozozná rozdiel, pretože väčšie frekvencia nie je schopné vnímať. Často vidíme, že sa hry spúšťajú s 60fps.
Druhou vecou je obnovovacia frekvencia monitora. Pokiaľ má monitor obnovovaciu frekvenciu 60Hz (väčšina) alebo 75Hz, viac snímok na ňom nezobrazíte, ani keby sme sa rozkrájali. Obnovovaciu frekvenciu môžeme v SDL zistiť zo štruktúry SDL_DisplayMode. K vytvoreniu štruktúry môžeme použiť funkciu SDL_GetClosestDisplayMode, ktoré odovzdáme náš vytvorený SDL_DisplayMode, a ona vráti SDL_DisplayMode, ktorý sa tomu nášmu najviac približuje. Druhou funkciou je SDL_GetCurrentDispalyMode, ktorá vráti aktuálny display mode. Naša hra by sa mala vykresľovať rovnako rýchlo, aká je obnovovacia frekvencia monitora. Posledným faktorom je spotreba procesora. Prečo budeme procesor vyťažovať na maximum, keď to nie je potrebné? Na stolnom PC o toľko nejde, to je pripojené k elektrickej sieti neustále, ale na notebookoch a tabletoch výrazne znížime dobu prevádzky, pretože to baterka nezvládne.
Popíšem spôsoby, ako môžeme FPS optimalizovať. V praxi sa väčšinou používajú dohromady. Nebudem ukazovať konkrétnej implementácie, iba uvediem teoretické fungovanie. Praktickú ukážku implementujeme až vo výslednej hre.
Stroje, na ktorých aplikácia pobeží, musíme rozlíšiť na dve skupiny. Jedna skupina dokáže vykresľovať rýchlejšie ako 60Hz, druhá naopak menej. Ak chceme, aby aplikácia správne fungovala, musíme vyriešiť oba problémy. Keby sme napríklad nastavili, že sa postava bude v každej aktualizácii posúvať o 5 pixelov, na rýchlejšom stroji sa bude postava posúvať rýchlejšie ako na stroji pomalším.
Delta timing
Ide o princíp aktualizácie scény na základe uběhnutého času. Delta v názve vychádza z matematického výrazu delta, teda rozdiel. Ak uvedieme príklad na postave v hre, delta timing bude pracovať tak, že postava bude mať nadefinovanú rýchlosť pohybu. Napríklad 10 pixelov za sekundu. V aktualizačný funkciu budeme mať uložený čas minulej aktualizácie a zistíme aktuálny čas. Na základe rozdielu určíme vzdialenosť, o ktorú sa postava posunie. Pokiaľ sa bude aktualizačný funkcia volať každých 100ms, v každom cykle sa posunie o jeden pixel.
Výhodou je použiteľnosť aj pre zostavy, ktoré nedisponujú dostatočným výkonom. Delta timing docielime to, že na všetkých zariadení sa bude postava pohybovať rovnako rýchlo, nezávisle od výkonu procesora. Nevýhodou je, že nerieši frekvencia vyššia. Delta timing vyrieši rýchlosť pohybu na všetkých strojoch, ale nedokážeme s jeho pomocou získať konštantných 60fps.
Uspávanie vlákna
Druhou možnosťou je vždy vlákno uspať. Znížime tým FPS a nebudeme vyťažovať procesor. Problémom sú stroje, ktoré nestíhajú vykresľovať 60 snímok za sekundu. Ak by sme vlákno ešte uspali, ich FPS by znížilo a hra by sa nedala hrať. V hernej slučke by museli prebiehať výpočty, podľa ktorých by sa program rozhodol, či má vlákno uspať alebo nie. Ako som spomínal, nemôžeme na funkciu SDL_Delay spoliehať, pretože jej presnosť závisí na operačnom systéme. Navyše tým celú aplikáciu zastavíme. Pre užívateľov sa bude javiť ako zamrznutá.
Uspanie ani nerieši rýchlosť pohybu. Na rozdiel od delta timingu, tu je pohyb vypočítaný vždy k jednej slučke. Pokiaľ stroj nebude zvládať potrebnú rýchlosť vykresľovanie, bude sa hra zdať pomalší. Napríklad pri posune 2 pixlov za snímku sa na stroji, ktorý aplikáciu spustí s 60 FPS, postava za sekundu posunie o 120 pixelov, zatiaľ čo na stroji, ktorý zvláda len 30 FPS, bude posun iba o 60 pixlov.
Ukážkový príklad
Do ukážkového príkladu som dnes pridal iba uspanie vlákna. Navyše som pridal časť kódu, ktorá vypisuje FPS. Všimnime si, ako sa spomalil posun prechodu, ktorý je teraz oveľa plynulejší.
Od budúcej lekcie sa budeme viac venovať udalostiam. Pozrieme sa na udalosti vyvolané myšou a klávesnicou a zbežne sa pozrieme aj na ďalšie zariadenia.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkamiStiahnuté 711x (9.57 MB)