mirror of
https://github.com/Relintai/programming_tutorials.git
synced 2025-04-21 21:51:22 +02:00
199 lines
8.3 KiB
Plaintext
199 lines
8.3 KiB
Plaintext
|
|
Í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 <chrono>
|
|
|
|
---------
|
|
|
|
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<double> elapsed_seconds = end - start
|
|
double t = elapsed_seconds.count()
|
|
|
|
double tfps = 1.0 / static_cast<double>(target_fps)
|
|
|
|
double remaining = tfps - t
|
|
|
|
if (remaining > 0):
|
|
Uint32 fms = static_cast<Uint32>(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
|
|
|
|
------------------------------------------------------------------------------------------
|