Írjunk, egy Application osztályt. Erre úgy érdemes gondolni, mint a program fő osztálya. Ennek a leszármazottai fogják tartalmazni a lehetséges Scene-eket, illetve van egy aktív Scene változója, amely Scene-nek ő fogja meghívni a megfelelő időben a virtuális függvényeit. (event, render, update). Singleton! --------- A main_loop() metódusa egyetlen meghívásra megcsinál egy kör program iterációt. Ez 4 dolgot jelent, először a bementek feldolgozását (elküldi őket a scene-nek!), majd a frissítést (scene->update(delta)), majd a rajzolást, hogy a jelenlegi legújabb belső állapot alapján legyen újrarajzolva a képernyő, majd a delta változó karbantartását végzi el, és ha szükséges, akkor pihenteti a program futását SDL_Delay-el. (Hogy max a target_fps-nyi képkockát rajzoltassunk ki, a képernyőre.) ---- A delta változóra ú.n. "framerate independent" vagy magyarul képkocka sebességtől független számolások elvégzéséhez van szükség. (Talán láttatok már régi játékokat, amik modern számítógépeken elindítva - valószínűleg már csak dosboxban - nagyon gyorsan futnak. Ez azért van, mert amikor anno fejlesztették őket, ezeket a számolásokat nem végeztették el a programmal, hanem csak fixért ékekkel módosították a játék objektumainak pl. a helyzetét. Azaz ezek a játékok csak akkor működnek jól, ha a fejlesztett kjörnyezet fps számával futnak, egyébként gyorsabbak, vagy lassabbak lesznek. (Ugye ekkor még csak 1-2 féle számítógép volt, és nem minden esetben gondoltak rá, hogy ezt is figyelembe kell venni. - Vagy például a fejlesztőnek csak konzolos fejlesztési tapasztalata volt, ott pedig ilyet alapfeltételt gond nélkül lehetett használni. Mellesleg, úgy hallottam, hogy néhány mai konzolos játék netkódjában még mindíg előfordulnak hasonló alapfeltevések - csak itt az, hogy a konzolt úgyse lehet feltörni, azaz nincs anti-cheat. -) Ez a gyakorlatban csak annyit jelent, hogy ha valamit képkocka sebesség dependencia nélkül szeretnétek kiszámolni, akkor azt az értéket csak meg kell szorozni a delta változóval. (A delta egy float, amely az előző képkocka óta eltelt időt MÁSODPERCBEN tárolja (relatíve kis szám)). ---- A program futásának pihentetése az SDL_Delay-el: Viszont nem szabad elfelejteni, hogy kirajzolni, maximum a monitor frissítési rátájával tudunk (vsync-nek hívják), mivel ennél többet úgysem fogunk tudni megjeleníteni. Természetesen vannak olyanok, akik preferálják, ha kikapcsolható. Ha valaki akarja építse bele egy bool-al ezt a funkcionalitást. (Természetesen az is működik, ha csak a target fps nagyon nagyra van állítva.) Azt amúgy nem szabad elfelejteni, hogy több munka elvégeztetése a számítógéppel magasabb áramfogyasztást is eredményez, amely azt jelenti, hogy például egy telefont jóval hamarabb le tudunk meríteni, ha sokat fölöslegesen dolgoztatjuk a cput! (Például egy limitálatlan üres loop is képes lehet kimaxolni egy cpu core-t!) Amúgy mostanában telefonokon / laptopokon simán több mint megfelezhetjük az üzemidejét, ha ilyenre nem figyelünk! Ne feledjük el, hogy az sdl alapú programjaink, mind elindíthatóak telefonokon! (És elég sok más platformon. - Ha C++ ba írtátok őket, akkor minden sdl2-által támogatott platformon használható lesz a program. - plusz ti is írhattok saját SDL platformot /Nem olyan nehéz/.) Amúgy, ezt tudja alapból az SDL is csinálni, de szemléletesebb, ha mi írjuk meg. ---- Még egy fontos dolog, a main_loop() ról, hogy nem tartalmazza magát a ciklust! (Valószínűleg jobb lenne iterate()-nek hívni emiatt, de a hangzatosság miatt meghagytam main_loop() nak.) Erre azért van szükség, mivel az sdl fordítható emscripten-el is, és ilyenkor az emscriptennek kell tudnia meghívni a fő ciklus egy iterációját. (Az emscripten képes javascriptté fordítani c++ kódot. - böngészőkben fog futni!) A böngészők felépítése miatt nem lehet javascript kódba végtelen ciklust csinálni, mert akkor befagy az adott tab. --------- Ne felehjtsétek el, hogy a statikus változót is definiálni kell a .cpp fájlban! így: Application* _instance = nullptr; Szükség lessz az std::chrono könyvtárra! (Elég a .cpp-fájlban is beincludeolni.) Így: #include --------- Akkor az Application osztály UML diagramja: |---------------------------------------------------------------------------------------| | class Application | |---------------------------------------------------------------------------------------| | + bool running | | + int target_fps | | | | + virtual void event(const SDL_Event ¤t_event) | | + virtual void update(float delta) | | + virtual void render() | | | | + void main_loop() | | | | + Application() | | + virtual ~Application() | | | | + Scene *scene | | | | + static Application* get_singleton() | | | | + double frame_delta = 0 | | | | # static Application* _instance | |---------------------------------------------------------------------------------------| ÉS a függvények pszeudokódjai: ------------------------------------------------------------------------------------------ void event(const SDL_Event ¤t_event): switch (current_event.type): case SDL_QUIT: running = false break scene->event(current_event) ------------------------------------------------------------------------------------------ void update(float delta): scene->update(delta) ------------------------------------------------------------------------------------------ void render(): scene->render() Renderer::get_singleton()->present() ------------------------------------------------------------------------------------------ void main_loop(): std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now() SDL_Event current_event while (SDL_PollEvent(¤t_event)): event(current_event) update(frame_delta) render() std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now() std::chrono::duration elapsed_seconds = end - start double t = elapsed_seconds.count() double tfps = 1.0 / static_cast(target_fps) double remaining = tfps - t if (remaining > 0): Uint32 fms = static_cast(remaining * 1000.0) frame_delta = tfps SDL_Delay(fms) else: frame_delta = t ------------------------------------------------------------------------------------------ Application(): running = true target_fps = 60 scene = nullptr _instance = this ------------------------------------------------------------------------------------------ ~Application(): _instance = nullptr ------------------------------------------------------------------------------------------ Application* get_singleton(): return _instance ------------------------------------------------------------------------------------------