článek
Udělej si vlastní hru – VII. část
V závěrečném dílu tohoto seriálu již mnoho nového, co by se zároveň přímo týkalo programování hry neprobereme. Jen pro jakousi kompletnost si v rychlosti ukážeme, jak hře zajistit základní ozvučení - pomocí oblíbené knihovny fmod - ale možných řešení se opět, podobně jako třeba v případě grafického rozhraní, nabízí spousta, takže toto rozhodně nikomu nevnucuji.
Jako další si ukážeme ještě jednoduchý timer, čili jakýsi měřák času a věcí s tím souvisejících. timer také není věc, která přímo souvisí s herními mechanismy, nicméně většina her ho různými způsoby využívá (ať už to je jen k výpočtu FPS, nebo i v míře větší).
Na úplný závěr můžeme udělat takové malé shrnutí a také naznačit, co by se ještě do hry slušelo a patřilo dodělat, aby s ní potenciální vývojář mohl "jít ven" :-).
Zvuk
Jak jsem již několikrát uvedl, použijeme knihovnu fmod. Zvuková knihovna je poměrně dost oblíbená, mimo jiné i pro to, že je multiplatformní. Základní informace i dokumentaci, ale i instalátor vývojářského API můžete získat na adrese www.fmod.org . My použijeme jen základy - načteme několik zvuků, které v okamžiku, kdy k tomu nastane vhodná doba, přehrajeme. Podrobně nebudu práci jednotlivých funkcí rozebírat (jako jsem to nedělal ani u funkcí grafického API), k tomu slouží jejich dokumentace. Nejprve si vytvoříme datovou strukturu SOUND, která bude sloužit k reprezentaci jednotlivých zvuků v paměti. Tato struktura vypadá následovně:
struct SOUND
{
FSOUND_SAMPLE *pSound; //ukazatel na zvukový objekt
int soundChannel; //kanál pro přehrávání zvuku
unsigned timer, len; //časovač a délka zvuku
};
Proměnné pSound a soundChannel slouží knihovně fmod pro práci se zvukem při jeho přehrávání, proměnná timer slouží při cyklickém přehrávání zvuku (např. kroky při chůzi) k časování (podobně jako při animaci) a proměnná len udává délku trvání daného zvuku (v milisekundách).
Na začátku je potřeba zvuky načíst, to provedeme funkcí LoadSounds:
void LoadSounds(void)
{
if(FSOUND_Init(44100, 32, FSOUND_INIT_GLOBALFOCUS))
{
music.pSound = FSOUND_Sample_Load (FSOUND_FREE, "data/sound/bgmusic.mp3",0,0);
boom.pSound = FSOUND_Sample_Load(FSOUND_FREE,"data/sound/boom.wav",0,0);
step.pSound = FSOUND_Sample_Load(FSOUND_FREE,"data/sound/step.wav",0,0);
step.len = 450;
boom.len = EXPLOSION_TIME / 3;
music.len = 126000;
}
else
{
MessageBox(NULL,"Selhala inicializace zvuku!","SOUND ERROR",MB_OK | MB_ICONWARNING);
}
}
Zde načítáme celkem 3 zvuky, a to hudbu (music), zvuk výbuchu (boom) a zvuk kroku (step). Před tím jsme tyto zvuky samozřejmě deklarovali v globálních proměnných. Po jejich načtení ještě "ručně" nastavíme jejich délku - ta by šla ovšem zjišťovat i některou funkcí, vypadalo by to i elegantněji...
Nyní si ukážeme ještě funkci PlaySounds, která již podle svého názvu slouží k přehrávání jednotlivých zvuků během hry:
void PlaySounds(void)
{
if(Mario.walking && GetTickCount() - step.timer >= step.len)
{
step.timer = GetTickCount();
step.soundChannel = FSOUND_PlaySound(FSOUND_FREE,step.pSound);
}
for(int i=0; i < BOMB_TYPES; ++i)
{
if(bombs[i].explosion_st == 1 && GetTickCount() - boom.timer >= boom.len)
{
boom.timer = GetTickCount();
boom.soundChannel = FSOUND_PlaySound(FSOUND_FREE,boom.pSound);
}
}
if(GetTickCount() - music.timer >= music.len)
{
music.timer = GetTickCount();
music.soundChannel = FSOUND_PlaySound(FSOUND_FREE,music.pSound);
}
}
Funkce PlaySounds je volána cyklicky před každým zavoláním funkce Render.
Nejprve zjišťujeme, zda se má přehrát zvuk chůze. Tato situace nastane pokud se Mario pohybuje (walking je true) a zároveň již skončilo minulé přehrávání tohoto zvuku (k tomu nám poslouží proměnné timer a len). Dále prověříme. zda se má přehrát zvuk explodující bomby - to nastane tehdy, pokud některá bomba má explosion_st roven 1 - tedy právě začala její exploze. Nakonec zkontrolujeme, zda se má znovu začít přehrávat doprovodná hudba, pokud ano, tak ji začneme přehrávat znovu.
Zvuky výbuchu a kroku jsem převzal z freewarových zdrojů, hudbu si můžete doplnit dle vlastního vkusu, nezapomeňte však nastavit její správnou délku ve funkci LoadSounds.
Timer
Pro funkci timeru si zavedeme několik globálních proměnných:
__int64 lastTime, m_timerStart, m_frequency, m_lastTime, m_currentTime;
float m_resolution;
double m_frameTime,m_fpsCounter,m_fps,m_fpsMax;
int m_frames;
Funkcí CreateTimer provedem jeho vytvoření (inicializaci). Tato funkce budiž volána např. ve funkci InitD3D nebo jinde na začátku programu.
void Create_timer()
{
if (QueryPerformanceFrequency((LARGE_INTEGER*)&m_frequency))
{
QueryPerformanceCounter((LARGE_INTEGER*)&m_timerStart);
m_resolution = (float)(1.0 / (double) m_frequency) * 1.0f;
}
}
Funkce QueryPerformanceFrequency a QueryPerformanceCounter jsou deklarovány ve windows.h. My vytvoříme timer o frekvenci, která bude uložena v m_frequency, počáteční čas (počet tiků) ve chvíli vytvoření timeru uložíme do m_timerStart.
Na čas se v případě potřeby budeme dotazovat funkcí GetTime
double GetTime()
{
__int64 timeElapsed;
QueryPerformanceCounter((LARGE_INTEGER*)&timeElapsed);
timeElapsed -= m_timerStart;
return timeElapsed * m_resolution;
}
Její práce je vcelku zřejmá - vrátí čas, který uplynul od vytvoření timeru. Ještě budeme používat funkci UpdateTimer, která bude sloužit k průběžnému počítání FPS:
void UpdateTimer()
{
m_currentTime = (__int64)GetTime();
m_frameTime = (double)(m_currentTime - m_lastTime) / 1.0;
m_frames++;
m_fpsCounter += m_frameTime;
if (m_fpsCounter >= 0.25)
{
m_fps = (m_frames / m_fpsCounter);
m_fpsCounter = 0.0;
m_frames = 0;
if (m_fps > m_fpsMax)
{
m_fpsMax = m_fps;
}
}
m_lastTime = m_currentTime;
}
Její funkce je též celkem zřejmá - funkcí zjistíme jednak čas potřebný na jeden snímek a zároveň i počet snímků za sekundu (tedy FPS).
A jako poslední ukážu deklaraci funkce FPSLimit, která bude sloužit k omezení počtu FPS, aby hra na některých počítačích neběžela příliš rychle:
void FPSLimit(int FPSmax)
{
if(FPSmax!=NULL)
{
if (m_frameTime < (float)(1.0/FPSmax))
{
double a=0.0,lim=GetTime();
while(a-lim<(float)(1.0/FPSmax)-m_frameTime)
{
a=GetTime();
}
}
}
}
Pokud je hodnota FPS příliš vysoká, pak funkce "pozdrží" každý snímek tak dlouho, aby hodnota FPS klesla na požadovanou mez - tímto tedy nahradíme dosavadně používanou funkci Wait.
Funkce FPSLimit i funkce UpdateTimer jsou cyklicky volané během kreslení každého snímku - tedy opět při každém volání funkce Render.
Závěr
Tímto jsme se tedy dopracovali až na úplný konec našeho seriálu. Ukázali jsme si implementaci všech důležitých herních mechanismů a i té nejnutnější "omáčky" okolo. Ná Vás je již buď další kosmetické vylepšení této herní kostry - např. vytvořit úvodní menu, jednotlivé levely, počítání skore apod. , nebo se můžete jen inspirovat a od základů vybudovat čistě Váš herní projekt...
Přeji Vám mnoho úspěchů a málo bugů!
Hudební soubory: 322_sedma_cast.zip
Související odkazy:
- Udělej si vlastní hru - úvod
- Udělej si vlastní hru - I. část
- Udělej si vlastní hru - II. část
- Udělej si vlastní hru - III. část
- Udělej si vlastní hru - IV. část
- Udělej si vlastní hru - V. část
- Udělej si vlastní hru - VI. část
autor
/ jan_kohout
Publikováno: 27.06.2007
další články této kategorie
diskuze
phanka | 21.04.15 v 21:29
raději to nechám odborníkům
m-> 29 | 12.11.09 v 20:59
Ja som skôr myslel zdrojové súbory na články "Udělej si vlastní hru – #. část" Linky na články "TVORBA: Vytvoř si svou bludišťovku – #" mi fungujú :-)
Freegame | 12.11.09 v 00:44
Odkaz na ZIP s priklady je zde:
http://download1.freegame.cz/bludistovka_zdroj/bludistovka_zdroje.zip
m-> 29 | 10.11.09 v 22:13
No, neviem či to tu už nie je mŕtve, alebo ako, ale nejdú mi stiahnuť zdrojové kódy v žiadnom z článkov. Nemôže ich niekto niekam uploadnúť alebo poslať mi na mail? Najlepšie by ale bolo opraviť linky ;-) Dík moc
MUDr. Mstibor Háborů | 15.03.09 v 12:49
A ten exáč by byl k čemu, prosím pěkně? Smyslem článku je ukázat, jak se hry programují.
Bildo | 14.03.09 v 23:32
Taky si myslim, ze na zaver by se mozna hodil i exac? Nebo nee?
Nixk | 05.10.08 v 18:39
a Ten progra by nebyl???