Prvnφ opravdu rozsßhl² tutorißl - jak u₧ plyne z gigantickΘho nßzvu. Doufejme, ₧e takovß spousta informacφ a technik dokß₧e ud∞lat Ü¥astn²m opravdu ka₧dΘho. Strßvil jsem dva dny k≤dovßnφm a kolem dvou t²dn∙ psanφm tohoto HTML souboru. Pokud jste n∞kdy hrßli hru Admiar, lekce vßs vrßtφ do vzpomφnek. ┌kol hry sestßvß z vypln∞nφ jednotliv²ch polφΦek m°φ₧ky. Samoz°ejm∞ se musφte vyh²bat vÜem nep°ßtel∙m.
Nßm∞t tΘto lekce je vcelku slo₧it². Vφm, ₧e spousta z vßs je unavena studiem zßklad∙. Ka₧d² by zem°el pro zvlßÜtnosti 3D objekt∙, multitexturingu a podobn∞. T∞mto lidem se omlouvßm, proto₧e chci zachovat postupnΘ nabalovßnφ znalostφ. Po velkΘm skoku vp°ed nenφ u kr∙Φku zp∞t snadnΘ udr₧et zßjem Φtenß°∙. Jß osobn∞ preferuji konstantnφ tempo. Mo₧nß jsem ztratil n∞kolik z vßs, ale doufßm, ₧e ne p°φliÜ mnoho. Do dneÜka se ve vÜech m²ch tutorißlech objevovaly polygony, obdΘlnφky a troj·helnφky. Pravd∞podobn∞ jste si vÜimli ne·myslnΘ diskriminace :-) Φar, p°φmek, linek a podobn²ch jednorozm∞rn²ch ·tvar∙. O n∞kolik hodin pozd∞ji zaΦal vznikat Line Tutorißl. Vypadal v klidu, ale totßln∞ nudn²! Linky jsou skv∞lΘ, ale v porovnßnφ s n∞kter²mi efekty nic moc. Shrnuto: rozhodl jsem se napsat multi-tutorißl. Na konci lekce bychom m∞li mφt vytvo°enu jednoduchou hru typu 'Admiar'. Misφ bude vyplnit polφΦka m°φ₧ky. HrßΦe nesmφ chytit nep°ßtelΘ - jak jinak. Implementujeme levely, etapy, ₧ivoty, zvuky a k≤dy - k pr∙chodu skrz levely, kdy₧ se v∞ci stanou p°φliÜ obtφ₧n²mi. AΦkoli hru spustφte i na Pentiu 166 s Voodoo 2, rychlejÜφ procesor nebude na Ükodu.
RozÜφ°φme standardnφ k≤d z lekce jedna. P°idßme pot°ebnΘ hlaviΦkovΘ soubory - stdio.h pro souborovΘ operace a stdarg.h kv∙li v²stupu prom∞nn²ch (level, obtφ₧nost ap.).
#include <windows.h>// HlaviΦkov² soubor pro Windows
#include <stdio.h>// HlaviΦkov² soubor pro standardnφ vstup/v²stup
#include <stdarg.h>// HlaviΦkov² soubor pro funkce s prom∞nn²m poΦtem parametr∙
#include <gl\gl.h>// HlaviΦkov² soubor pro OpenGL32 knihovnu
#include <gl\glu.h>// HlaviΦkov² soubor pro Glu32 knihovnu
#include <gl\glaux.h>// HlaviΦkov² soubor pro Glaux knihovnu
HDC hDC = NULL;// Privßtnφ GDI Device Context
HGLRC hRC = NULL;// Trval² Rendering Context
HWND hWnd = NULL;// Obsahuje Handle naÜeho okna
HINSTANCE hInstance;// Obsahuje instanci aplikace
bool keys[256];// Pole pro uklßdßnφ vstupu z klßvesnice
bool active = TRUE;// Ponese informaci o tom, zda je okno aktivnφ
bool fullscreen = TRUE;// Ponese informaci o tom, zda je program ve fullscreenu
Deklarujeme prom∞nnΘ. Pole vline uklßdß zßznamy o 121 vertikßlnφch linkßch, kterΘ tvo°φ m°φ₧ku. 11 p°φmek zleva doprava a 11 Φas ze shora dol∙. Hline uklßdß 121 horizontßlnφch p°φmek. Ap pou₧φvßme ke zjiÜt∞nφ stisku klßvesy A. Filled je nastaveno na FALSE, jestli₧e m°φ₧ka nenφ kompletn∞ vypln∞nφ a TRUE pokud je. Gameover ukonΦuje hru. Pokud se anti rovnß TRUE je zapnut antialiasing objekt∙.
bool vline[11][10];// Uklßdß zßznamy o vertikßlnφch linkßch
bool hline[10][11];// Uklßdß zßznamy o horizontßlnφch linkßch
bool ap;// Stisknuto 'A'?
bool filled;// Bylo ukonΦeno vypl≥ovßnφ m°φ₧ky?
bool gameover;// Konec hry?
bool anti = TRUE;// Antialiasing?
P°ichßzejφ na °adu celoΦφselnΘ prom∞nnΘ. Loop1 a loop2 u₧φvßme k oznaΦenφ bod∙ v hernφ m°φ₧ce, zjiÜt∞nφ zda do nßs nep°φtel nevrazil a k vygenerovßnφ randomovΘ pozice. Zastavenφ pohybu nep°ßtel je implementovßno ΦφtaΦem delay. Po dosa₧enφ urΦitΘ hodnoty se zaΦnou znovu h²bat a delay se zpßtky vynuluje.
Prom∞nnß adjust je specißlnφ. I kdy₧ program obsahuje timer, tento timer pouze zjiÜ¥uje, zda je poΦφtaΦ (pr∙b∞h programu) p°φliÜ rychl² a v takovΘm p°φpad∞ ho zpomalφme. Na grafickΘ kart∞ GeForce hra b∞₧φ hodn∞ rychle. Po testu s PIII/450 s Voodoo 3500 TV si nelze nevÜimnout extrΘmnφ lenosti. ProblΘm spoΦφvß v k≤du pro Φasovßnφ, kter² hru pouze zpomaluje. Zrychlenφ jφm nelze provΘst. Vytvo°il jsem prom∞nnou adjust, kterß m∙₧e nab²vat nuly a₧ p∞ti. ╚φm vyÜÜφ hodnota, tφm rychleji se objekty pohybujφ - podpora starÜφch systΘm∙. NicmΘn∞ nezßle₧φ, jak rychlß je hra, absolutnφ rychlost provßd∞nφ programu se nikdy nezv²Üφ. Nastavenφm adjust na trojku vytvo°φme kompromis pro pomalΘ i rychlΘ systΘmy. Vφce o Φasovßnφ dßle.
Lives uklßdß poΦet ₧ivot∙, level u₧φvßme k zaznamenßvßnφ obtφ₧nosti. Nenφ to level, kter² se zobrazuje na monitoru. Level2 zaΦφnß se stejnou hodnotou, ale m∙₧e b²t inkrementovßn donekoneΦna - zßle₧φ na obratnosti hrßΦe. Pokud dokß₧e dosßhnout t°etφho levelu, prom∞nnß level se p°estane zvyÜovat. urΦuje pouze vnit°nφ obtφ₧nost hry. Stage definuje konkrΘtnφ etapu hry.
int loop1;// ╪φdφcφ prom∞nnß cykl∙
int loop2;// ╪φdφcφ prom∞nnß cykl∙
int delay;// Doba zastavenφ nep°ßtel
int adjust = 3;// Rychlostnφ kompenzace pro pomalΘ systΘmy
int lives = 5;// PoΦet ₧ivot∙ hrßΦe
int level = 1;// Vnit°nφ obtφ₧nost hry
int level2 = level;// Zobrazovan² level
int stage = 1;// Etapa/fßze hry
Definujeme strukturu objektu - hrßΦ, nep°φtel ap. Vnit°nφ prom∞nnΘ fx a fy uklßdajφ pomocnou polohu pro plynul² pohyb (fx = fine x). X a y definujφ pozici na m°φ₧ce. Mohou nab²vat hodnot od nuly do deseti. Kdybychom se s hrßΦem po scΘn∞ pohybovali pomocφ t∞chto dvou prom∞nn²ch m∞li bychom jedenßct pozic vodorovn∞ a jedenßct svisle. HrßΦ by p°eskakoval z jednoho mφsta na druhΘ. Proto p°i pohybu pou₧φvßme up°es≥ujφcφ fx a fy. Poslednφ prom∞nnou spin pou₧φvßme pro otßΦenφ objekt∙ okolo osy z.
struct object// Struktura objektu ve h°e
{
int fx, fy;// Pohybovß pozice
int x, y;// Absolutnφ pozice
float spin;// OtßΦenφ objektu dokola
};
Na zßklad∞ struktury vytvo°φme hrßΦe, dev∞t nep°ßtel a jeden specißlnφ objekt - sklen∞nΘ p°es²pacφ hodiny, kterΘ se sem tam objevφ. Pokud je stihnete sebrat, nep°φtel se na chvφli zastavφ.
struct object player;// HrßΦ
struct object enemy[9];// Nep°ßtelΘ
struct object hourglass;// Sklen∞nΘ hodiny
Abychom prom∞nnΘ pro ΦasovaΦ m∞li pohromad∞, slouΦφme je do struktury. Frekvenci ΦasovaΦe deklarujeme jako 64-bitovΘ celΘ Φφslo. Resolution je perioda (obrßcenß hodnota frekvence). Mm_timer_start a mm_timer_elapsed udr₧ujφ poΦßteΦnφ a uplynul² Φas. Pou₧φvßme je pouze tehdy, pokud poΦφtaΦ nemß performance counter (v p°ekladu: ΦφtaΦ provedenφ nebo v²konu, z∙stanu u anglickΘho termφnu). Logickß prom∞nnß performance_timer bude nastavena na TRUE pokud program detekuje, ₧e poΦφtaΦ mß performance counter. Pokud ho nenajde budeme pro Φasovßnφ pou₧φvat mΘn∞ p°esn², ale celkov∞ dostaΦujφcφ multimedißlnφ timer. Poslednφ dv∞ prom∞nnΘ jsou op∞t 64-bitovΘ integery, kterΘ uklßdajφ Φas spuÜt∞nφ a uplynul² Φas performance counteru. Prom∞nnou na bßzi tΘto struktury pojmenujeme timer.
struct // Informace pro ΦasovaΦ
{
__int64 frequency;// Frekvence
float resolution;// Perioda
unsigned long mm_timer_start;// Startovnφ Φas multimedißlnφho timeru
unsigned long mm_timer_elapsed;// Uplynul² Φas multimedißlnφ timeru
bool performance_timer;// U₧φvßme Performance Timer?
__int64 performance_timer_start;// Startovnφ Φas Performance Timeru
__int64 performance_timer_elapsed;// Uplynul² Φas Performance Timeru
} timer;// Struktura se jmenuje timer
Nßsledujφcφ pole si m∙₧eme p°edstavit jako tabulku rychlostφ. objekt ve h°e se m∙₧e pohybovat rozdφln²mi rychlostmi. VÜe zßvisφ na prom∞nnΘ adjust (v²Üe). Pokud se jejφ hodnota rovnß nule pohybujφcφ se o pixel za urΦit² Φas, pokud p∞ti, rychlost Φinφ dvacet pixel∙. Inkrementovßnφm adjust se na pomal²ch poΦφtaΦφch zv²Üφ rychlost (ale i "trhanost") hry. PoΦet pixel∙ kroku je v tabulce. Adjust pou₧φvßme jako index do tohoto pole.
int steps[6]={ 1, 2, 4, 5, 10, 20 };// Krokovacφ hodnota pro p°izp∙sobenφ pomalΘho videa
Deklarujeme pole dvou textur - pozadφ a bitmapov² font. Base ukazuje na prvnφ display list fontu (viz. minulΘ tutorißly). Funkce pro nahrßvßnφ a vytvß°enφ textur nebudu opisovat, byly tu u₧ tolikrßt, ₧e je musφte znßt na zpam∞¥ (p°ekladatel).
GLuint texture[2];// Dv∞ textury
GLuint base;// Zßkladnφ display list pro font
Inicializujeme ΦasovaΦ. ZaΦneme vynulovßnφm vÜech prom∞nn²ch. Potom zjistφme, zda budeme moci pou₧φvat performance counter. Pokud ano,ulo₧φme frekvenci do timer.frequency, pokud ne budeme pou₧φvat multimedißlnφ timer - nastavφme timer.performance_timer na FALSE a naΦteme do poΦßteΦnφ hodnoty aktußlnφ Φas. Timer.resolution definujeme na 0.001 (P°ekladatel: d∞lenφ je celkem zbyteΦnΘ) a timer.frequency na 1000. Proto₧e jeÜt∞ neuplynul ₧ßdn² Φas, p°i°adφme uplynulΘmu Φasu startovnφ Φas.
void TimerInit(void)// Inicializace timeru
{
memset(&timer, 0, sizeof(timer));// Vynuluje prom∞nnΘ struktury
// Zjistφ jestli je Performance Counter dostupn² a pokud ano, bude naΦtena jeho frekvence
if (!QueryPerformanceFrequency((LARGE_INTEGER *) &timer.frequency))
{
// Performance Counter nenφ dostupn²
timer.performance_timer = FALSE;// Nastavφ Performance Timer na FALSE
timer.mm_timer_start = timeGetTime();// Zφskßnφ aktußlnφho Φasu
timer.resolution = 1.0f/1000.0f;// Nastavenφ periody
timer.frequency = 1000;// Nastavenφ frekvence
timer.mm_timer_elapsed = timer.mm_timer_start;// Uplynul² Φas = poΦßteΦnφ
}
Mß-li poΦφtaΦ performance counter projdeme touto v∞tvφ. Nastavφme poΦßteΦnφ hodnotu a oznßmφme, ₧e m∙₧eme pou₧φvat performance counter. PotΘ spoΦφtßme periodu pomocφ frekvence zφskanΘ v if() v²Üe. Perioda je p°evrßcenß hodnota frekvence. Nakonec nastavφme uplynul² Φas na startovnφ. VÜimn∞te si, ₧e mφsto sdφlenφ prom∞nn²ch obou timer∙, jsem se rozhodl pou₧φt r∙znΘ. Ob∞ cesty by pracovaly, ale tato je p°ehledn∞jÜφ.
else
{
// Performance Counter je mo₧nΘ pou₧φvat
QueryPerformanceCounter((LARGE_INTEGER *) &timer.performance_timer_start);// PoΦßteΦnφ Φas
timer.performance_timer = TRUE;// Nastavenφ Performance Timer na TRUE
timer.resolution = (float) (((double)1.0f)/((double)timer.frequency));// SpoΦφtßnφ periody
timer.performance_timer_elapsed = timer.performance_timer_start;//Nastavφ uplynul² Φas na poΦßteΦnφ
}
}
V nßsledujφcφ funkci naΦteme timer a vrßtφme uplynul² Φas v milisekundßch. Deklarujeme 64-bitovΘ celΘ Φφslo, do kterΘho naΦteme souΦasnou hodnotu ΦφtaΦe. Op∞t v∞tvφme program podle p°φtomnosti performance timeru. Prvnφ °ßdkou v if() naΦteme obsah ΦφtaΦe. Dßle od n∞j odeΦteme poΦßteΦnφ Φas, kter² jsme zφskali p°i inicializaci ΦasovaΦe. Zφskan² rozdφl nßsobφme periodou ΦφtaΦe. Abychom v²sledek v sekundßch p°evedli na milisekundy nßsobφme ho tisφcem. Tuto hodnotu vrßtφme. Nepou₧φvßme-li performance counter, provede se v∞tev else, kterß d∞lß analogicky to samΘ. NaΦteme souΦasn² Φas, odeΦteme od n∞j poΦßteΦnφ, nßsobφme periodou a potΘ tisφcem. Op∞t zφskßme uplynul² Φas v milisekundßch a vrßtφme ho.
float TimerGetTime()// Zφskß Φas v milisekundßch
{
__int64 time;// ╚as se uklßdß do 64-bitovΘho integeru
if (timer.performance_timer)// Performance Timer
{
QueryPerformanceCounter((LARGE_INTEGER *) &time);// NaΦte aktußlnφ Φas
// Vrßtφ uplynul² Φas v milisekundßch
return ((float)(time - timer.performance_timer_start) * timer.resolution)*1000.0f;
}
else// Multimedißlnφ timer
{
// Vrßtφ uplynul² Φas v milisekundßch
return ((float)(timeGetTime() - timer.mm_timer_start) * timer.resolution)*1000.0f;
}
}
V dalÜφ funkci se resetuje pozice hrßΦe na lev² hornφ roh a poloha nep°ßtel na randomovΘ body. Lev² hornφ roh scΘny mß sou°adnice [0;0]. P°i°adφme je hrßΦov∞ x a y. Proto₧e je na zaΦßtku linek, nepohybuje se, tak₧e i up°es≥ujφcφ pohybovΘ pozice nastavφme na nulu.
void ResetObjects(void)// Reset hrßΦe a nep°ßtel
{
player.x = 0;// HrßΦ bude vlevo naho°e
player.y = 0;// HrßΦ bude vlevo naho°e
player.fx = 0;// Pohybovß pozice
player.fy = 0;// Pohybovß pozice
P°ejdeme k inicializaci polohy nep°ßtel. Jejich aktußlnφ poΦet (zobrazen²ch) je roven vnit°nφmu levelu nßsobenΘmu jeho souΦasnou obtφ₧nostφ/etapou. Zapamatujte si, ₧e maximßlnφ poΦet level∙ je t°i a maximßlnφ poΦet etap v levelu je takΘ t°i. Z toho plyne, ₧e m∙₧eme mφt nejvφce dev∞t nep°ßtel. V cyklu nastavφme x pozici ka₧dΘho nep°φtele na p∞t a₧ deset a y pozici na nula a₧ deset. Nechceme, aby se pohybovali ze starΘ pozice na novou, tak₧e se ujistφme, ₧e se fx a fy budou rovnat x krßt dΘlka linky (60) a y krßt v²Üka linky (40).
for (loop1=0; loop1<(stage*level); loop1++)// Prochßzφ nep°ßtele
{
enemy[loop1].x = 5 + rand() % 6;// Nastavφ randomovou x pozici
enemy[loop1].y = rand() % 11;// Nastavφ randomovou y pozici
enemy[loop1].fx = enemy[loop1].x * 60;// Pohybovß pozice
enemy[loop1].fy = enemy[loop1].y * 40;// Pohybovß pozice
}
}
Funkce glPrint() se moc nezm∞nila. Narozdφl od minul²ch tutorißl∙ jsem p°idal mo₧nost v²pisu hodnot prom∞nn²ch. Zapneme mapovßnφ textur, resetujeme matici a p°esuneme se na urΦenou pozici. Pokud je zvolena prvnφ (nultß) znakovß sada, zm∞nφme m∞°φtko tak, aby byl font dvakrßt vyÜÜφ a jeden a p∙l krßt ÜirÜφ. Pomocφ tΘto finty budeme moci vypsat titul hry v∞tÜφmi pφsmeny. Na konci vypneme mapovßnφ textur.
GLvoid glPrint(GLint x, GLint y, int set, const char *fmt, ...)// V²pis text∙
{
char text[256];// Bude uklßdat v²sledn² °et∞zec
va_list ap;// Ukazatel do argument∙ funkce
if (fmt == NULL)// Nebyl p°edßn °et∞zec
return;// Konec
va_start(ap, fmt);// Rozd∞lφ °et∞zec pro prom∞nnΘ
vsprintf(text, fmt, ap);// Konvertuje symboly na Φφsla
va_end(ap);// V²sledek je ulo₧en v text
if (set>1)// Byla p°edßna Üpatnß znakovß sada?
{
set=1;// Pokud ano, zvolφ se kurzφva
}
glEnable(GL_TEXTURE_2D);// Zapne texturovΘ mapovßnφ
glLoadIdentity();// Reset matice
glTranslated(x,y,0);// P°esun na po₧adovanou pozici
glListBase(base-32+(128*set));// Zvolφ znakovou sadu
if (set==0)// Pokud je urΦena prvnφ znakovß sada font bude v∞tÜφ
{
glScalef(1.5f,2.0f,1.0f);// Zm∞na m∞°φtka
}
glCallLists(strlen(text),GL_UNSIGNED_BYTE, text);// V²pis textu na monitor
glDisable(GL_TEXTURE_2D);// Vypne texturovΘ mapovßnφ
}
Implementace zm∞ny velikosti okna je novß. Namφsto perspektivnφ scΘny pou₧ijeme pravo·hlou projekci (ortho view). Jejφ hlavnφ charakteristikou je, ₧e se p°i zm∞n∞ vzdßlenosti pozorovatele (translace do hloubky) objekty nezmenÜujφ - vypnutß perspektiva. Osa z je mΘn∞ u₧iteΦnß, n∞kdy dokonce ztrßcφ v²znam. V tomto tutorißlu s nφ nebudeme pracovat v∙bec.
ZaΦneme nastavenφm viewportu, ·pln∞ stejn∞, jako p°i perspektivnφ scΘn∞. PotΘ zvolφme projekΦnφ matici (analogie filmovΘmu projektoru; obsahuje informace, jak se zobrazφ obrßzek) a resetujeme ji.
Inicializujeme pravo·hlou projekci. Prvnφ parametr 0.0f urΦuje pozici levΘ hrany scΘny. Druhß p°edßvanß hodnota oznaΦuje polohu pravΘ hrany. Pokud by m∞lo okno velikost 640 x 480, tak ve width bude ulo₧ena hodnota 640. ScΘna by zaΦφnala na ose x nulou a konΦila 640 - p°esn∞ jako okno. T°etφm parametrem oznaΦujeme spodnφ okraj scΘny. B²vß zßporn², ale proto₧e chceme pracovat s pixely urΦφme spodek okna rovnu jeho v²Üce. Nula, Φtvrt² parametr, definuje hornφ okraj. Poslednφ dv∞ hodnoty nßle₧φ k ose z. V tΘto lekci se o ni nestarßme, tak₧e nastavφme rozmezφ od -1.0f do 1.0f. VÜechno budeme vykreslovat v hloubce nula, tak₧e uvidφme vÜe.
Po nastavenφ pravo·hlΘ scΘny, zvolφme matici modelview (informace o objektech, lokacφch, atd.) a resetujeme ji.
GLvoid ReSizeGLScene(GLsizei width, GLsizei height)// Inicializace a zm∞na velikosti okna
{
if (height==0)// Proti d∞lenφ nulou
{
height=1;// V²Üka se rovnß jednΘ
}
glViewport(0,0,width,height);// Reset Viewportu
glMatrixMode(GL_PROJECTION);// Zvolφ projekΦnφ matici
glLoadIdentity();// Reset projekΦnφ matice
glOrtho(0.0f,width,height,0.0f,-1.0f,1.0f);// Vytvo°φ pravo·hlou scΘnu
glMatrixMode(GL_MODELVIEW);// Zvolφ matici modelview
glLoadIdentity();// Reset matice modelview
}
P°i inicializaci se vyskytne n∞kolik nov²ch p°φkaz∙. ZaΦneme klasicky loadingem textur a kontrolou ·sp∞Ünosti tΘto akce, potΘ vygenerujeme znakovou sadu fontu. Zapneme jemnΘ stφnovßnφ, nastavφme ΦernΘ pozadφ a vyΦistφme hloubku jedniΦkou.
glHint() oznamuje OpenGL, jak mß vykreslovat. V tomto p°φpad∞ po₧adujeme, aby vÜechny linky byly nejhezΦφ, jakΘ OpenGL dokß₧e vytvo°it. Tφmto p°φkazem zapφnßme antialiasing. TakΘ zapneme blending a zvolφme jeho m≤d tak, abychom umo₧nili, ji₧ zmφn∞n², antialiasing linek. Blending je pot°eba, pokud chceme p∞kn∞ skombinovat (smφchat, zpr∙hlednit - blend with) s obrßzkem na pozadφ. Pokud chcete vid∞t, jak Üpatn∞ budou linky vypadat, vypn∞te blending. Je d∙le₧itΘ poukßzat na fakt, ₧e antialiasing se nemusφ zobrazovat sprßvn∞(? p°ekl.). Objekty ve h°e jsou docela malΘ, tak₧e si nemusφte vÜimnout, ₧e n∞co nenφ v po°ßdku. Podφvejte se po°ßdn∞. VÜimn∞te si, jak se linky na nep°ßtelφch zjemnφ pokud je antialiasing zapnut². HrßΦ a hodiny by m∞li vypadat mnohem lΘpe.
int InitGL(GLvoid)// Nastavenφ OpenGL
{
if (!LoadGLTextures())// Loading textur
{
return FALSE;
}
BuildFont();// Vytvo°enφ fontu
glShadeModel(GL_SMOOTH);// Zapne jemnΘ stφnovßnφ
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);// ╚ernΘ pozadφ
glClearDepth(1.0f);// Nastavenφ hloubkovΘho bufferu
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);// Nastavenφ antialiasingu linek
glEnable(GL_BLEND);// Zapne blending
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);// Typ blendingu
return TRUE;
}
Na °adu p°ichßzφ vykreslovßnφ. Sma₧eme obrazovku a hloubkov² buffer a zvolφme texturu fontu - texture[0]. Abychom slova "GRID CRAZY" vypsali purpurovou barvou nastavφme R a G naplno, G s poloviΦnφ intenzitou. Nßpis vypφÜeme na sou°adnice [207;24]. Pou₧ijeme prvnφ (nultou) znakovou sadu, tak₧e bude text velk²mi pφsmeny. PotΘ zam∞nφme purpurovou barvu za ₧lutou a vypφÜeme "Level" s obsahem prom∞nnΘ level2. Dvojka v %2i urΦuje maximßlnφ poΦet Φφslic. Pomocφ i oznamujeme, ₧e se jednß o celoΦφselnou prom∞nnou (integer). O trochu nφ₧e, tou samou barvou, zobrazφme "Stage" s konkrΘtnφ etapou hry.
int DrawGLScene(GLvoid)// VÜechno kreslenφ
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Sma₧e obrazovku a hloubkov² buffer
glBindTexture(GL_TEXTURE_2D, texture[0]);// Zvolφ texturu fontu
glColor3f(1.0f,0.5f,1.0f);// Purpurovß barva
glPrint(207,24,0,"GRID CRAZY");// VypφÜe logo hry
glColor3f(1.0f,1.0f,0.0f);// Älutß barva
glPrint(20,20,1,"Level:%2i",level2);// VypφÜe level
glPrint(20,40,1,"Stage:%2i",stage);// VypφÜe etapu
Zkontrolujeme konec hry. Pokud je gameover rovno TRUE zvolφme nßhodnou barvu. Pou₧φvßme glcolor3ub(), proto₧e je mnohem jednoduÜÜφ vygenerovat Φφslo od 0 do 255 ne₧ od 0.0f do 1.0f. Doprava od titulku hry vypφÜeme "GAME OVER" a o °ßdek nφ₧e "PRESS SPACE". Upozor≥ujeme hrßΦe, ₧e zem°el a ₧e pomocφ mezernφku m∙₧e hru resetovat.
if (gameover)// Konec hry?
{
glColor3ub(rand()%255,rand()%255,rand()%255);// Nßhodnß barva
glPrint(472,20,1,"GAME OVER");// VypφÜe GAME OVER
glPrint(456,40,1,"PRESS SPACE");// VypφÜe PRESS SPACE
}
Pokud mu vÜak n∞jakΘ ₧ivoty zbyly, zobrazφme doprava od titulku hry animovanΘ obrßzky hrßΦe. Vytvo°φme cyklus, kter² jde od nuly do aktußlnφho poΦtu ₧ivot∙ mφnus jedna. JedniΦku odeΦφtßme, proto₧e jeden obrßzek se zobrazuje do hracφho pole.
for (loop1=0; loop1<lives-1; loop1++)// Cyklus vykreslujφcφ ₧ivoty
{
Uvnit° cyklu resetujeme matici a provedeme translaci doprava na pozici, kterou zφskßme v²poΦtem: 490 plus °φdφcφ prom∞nnß krßt 40. Tφmto zp∙sobem budeme moci vykreslit ka₧d² animovan² ₧ivot hrßΦe o 40 pixel∙ doprava od minulΘho. PotΘ orotujeme pohled proti sm∞ru hodinov²ch ruΦiΦek v zßvislosti na hodnot∞ ulo₧enΘ v player.spin. Zßporn²m znamΘnkem zp∙sobφme, ₧e se budou ₧ivoty otßΦet opaΦn²m sm∞rem ne₧ hrßΦ.
glLoadIdentity();// Reset matice
glTranslatef(490+(loop1*40.0f),40.0f,0.0f);// P°esun doprava od titulku
glRotatef(-player.spin,0.0f,0.0f,1.0f);// Rotace proti sm∞ru hodinov²ch ruΦiΦek
Zvolφme zelenou barvu a zaΦneme zobrazovat. Kreslenφ linek je ·pln∞ stejnΘ, jako kreslenφ polygon∙. ZaΦneme s glBegin(GL_LINES). Tφm oznßmφme OpenGL, ₧e chceme kreslit p°φmky. Pro jednu staΦφ pouze dva body. My zadßvßme body pomocφ glVertex2d(), proto₧e nepot°ebujeme hloubku, ale samoz°ejm∞ lze pou₧φt i glVertex3f() pro plnohodnotn² bod ve 3D prostoru.
glColor3f(0.0f,1.0f,0.0f);// Zelenß barva
glBegin(GL_LINES);// ZaΦßtek kreslenφ ₧ivot∙
glVertex2d(-5,-5);// Lev² hornφ bod
glVertex2d( 5, 5);// Prav² dolnφ bod
glVertex2d( 5,-5);// Prav² hornφ bod
glVertex2d(-5, 5);// Lev² dolnφ bod
glEnd();// Konec kreslenφ
Po vykreslenφ X (X - tvar hrßΦe), znovu natoΦφme scΘnu, ale tentokrßt pouze o polovinu ·hlu. Zadßme tmavÜφ zelenou barvu a vykreslφme +, ale trochu v∞tÜφ ne₧ X. Proto₧e je + pomalejÜφ a tmavÜφ, X vypadß, jako by se otßΦelo na jeho vrcholu.
glRotatef(-player.spin*0.5f,0.0f,0.0f,1.0f);// Rotace proti sm∞ru hodinov²ch ruΦiΦek
glColor3f(0.0f,0.75f,0.0f);// TmavÜφ zelenß barva
glBegin(GL_LINES);// PokraΦovßnφ kreslenφ ₧ivot∙
glVertex2d(-7, 0);// Lev² st°edov² bod
glVertex2d( 7, 0);// Prav² st°edov² bod
glVertex2d( 0,-7);// Hornφ st°edov² bod
glVertex2d( 0, 7);// Dolnφ st°edov² bod
glEnd();// Konec kreslenφ
}
Nakreslφme hernφ m°φ₧ku. Nastavenφm prom∞nnΘ filled na TRUE oznßmφme programu, ₧e u₧ byla m°φ₧ka kompletn∞ vypln∞nß (vφce dßle). UrΦφme Üφ°ku Φßry na 2.0f - linky ztloustnou a m°φ₧ka bude opticky vφce definovanß. P°esto₧e se zhorÜφ kvalita grafickΘho v²stupu, vypneme antialiasing. Velmi zat∞₧uje procesor a pokud nemßte hodn∞ dobrou grafickou kartu, zaznamenßte obrovskΘ zpomalenφ. VyzkouÜejte si a konejte, jak uznßte za vhodnΘ.
filled=TRUE;// P°ed testem je vÜechno vypln∞nΘ
glLineWidth(2.0f);// èirÜφ Φßry
glDisable(GL_LINE_SMOOTH);// Vypne antialiasing
glLoadIdentity();// Reset matice
Po resetu matice deklarujeme dva vno°enΘ cykly. Prvnφm prochßzφme m°φ₧ku zleva doprava a druh²m ze shora dol∙. Nastavφme barvu na modrou a pokud je prßv∞ kreslenß linka ji₧ p°ejetß hrßΦem, p°ebijeme modrou barvu bφlou. Dßle zkontrolujeme, zda se nechystßme kreslit p°φliÜ vpravo. Pokud ano p°eskoΦφme kreslenφ.
for (loop1=0; loop1<11; loop1++)// Cyklus zleva doprava
{
for (loop2=0; loop2<11; loop2++)// Cyklus ze shora dol∙
{
glColor3f(0.0f,0.5f,1.0f);// Modrß barva
if (hline[loop1][loop2])// Byla u₧ linka p°ejetß?
{
glColor3f(1.0f,1.0f,1.0f);// Bφlß barva
}
if (loop1<10)// Nekreslit ·pln∞ vpravo
{
Otestujeme, jestli u₧ byla horizontßlnφ linka p°ejetß. Pokud ne, p°i°adφme do filled FALSE a tφm oznßmφme, ₧e jeÜt∞ nejmΘn∞ jedna linka nebyla vypln∞nß, a tudφ₧ jeÜt∞ nem∙₧eme tento level opustit.
if (!hline[loop1][loop2])// Nebyla linka jeÜt∞ p°ejetß?
{
filled=FALSE;// VÜechno jeÜt∞ nenφ vypln∞no
}
PotΘ koneΦn∞ vykreslφme horizontßlnφ linku. Proto₧e je vodorovnß, p°i°adφme y-ovΘ hodnot∞ obou bod∙ stejnou velikost. P°iΦφtßme sedmdesßtku, aby nad hracφm polem z∙stalo volnΘ mφsto pro informace o poΦtu ₧ivot∙, levelu ap. Hodnoty na ose x se liÜφ tφm, ₧e druh² bod je posunut o Üedesßt pixel∙ doprava (80-20=60). Op∞t p°iΦφtßme konstantu, v tomto p°φpad∞ dvacφtku, aby hracφ pole nebylo namaΦkßno na lev² okraj a vpravo nebyla zbyteΦnß mezera. VÜimn∞te si, ₧e linky jsou kresleny zleva doprava. Toto je d∙vod, proΦ nechceme kreslit jedenßctou - neveÜla by se na obrazovku.
glBegin(GL_LINES);// ZaΦßtek kreslenφ horizontßlnφch linek
glVertex2d(20+(loop1*60),70+(loop2*40));// Lev² bod
glVertex2d(80+(loop1*60),70+(loop2*40));// Prav² bod
glEnd();// Konec kreslenφ
}
Na °adu p°ichßzejφ vertikßlnφ linky. K≤d je tΘm∞° stejn², tak₧e text popisu nebudu zbyteΦn∞ opisovat. Linky se kreslφ ze shora dol∙ namφsto zleva doprava - jedinß odliÜnost.
glColor3f(0.0f,0.5f,1.0f);// Modrß barva
if (vline[loop1][loop2])// Byla u₧ linka p°ejetß?
{
glColor3f(1.0f,1.0f,1.0f);// Bφlß barva
}
if (loop2<10)// Nekreslit ·pln∞ dol∙
{
if (!vline[loop1][loop2])// Nebyla linka jeÜt∞ p°ejetß?
{
filled=FALSE;// VÜechno jeÜt∞ nebylo vypln∞no
}
glBegin(GL_LINES);// ZaΦßtek kreslenφ vertikßlnφch linek
glVertex2d(20+(loop1*60),70 +(loop2*40));// Hornφ bod
glVertex2d(20+(loop1*60),110+(loop2*40));// Dolnφ bod
glEnd();// Konec kreslenφ
}
ScΘna je dohromady sesklßdanß z obdΘlnφk∙ o velikosti jednΘ desetiny obrßzku scΘny. Na ka₧d² z nich je namapovanß urΦitß Φßst velkΘ textury, proto musφme zapnout mapovßnφ textur. Proto₧e nechceme, aby m∞l kreslen² obdΘlnφk barevn² nßdech, nastavφme barvu na bφlou. TakΘ nesmφme zapomenout zvolit texturu.
glEnable(GL_TEXTURE_2D);// Zapne mapovßnφ textur
glColor3f(1.0f,1.0f,1.0f);// Bφlß barva
glBindTexture(GL_TEXTURE_2D, texture[1]);// Zvolφ texturu
Dßle prov∞°φme, jestli aktußlnφ obdΘlnφk ve scΘn∞ jeÜt∞ existuje (nenφ za hranou hracφ plochy). Nachßzφme se v cyklech, kterΘ postupn∞ vykreslujφ 11 linek vodorovn∞ a 11 svisle. NicmΘn∞ nevykreslujeme 11 obdΘlnφk∙, ale pouze 10! Ov∞°φme, jestli se nechystßme kreslit na jedenßctou pozici - loop1 i loop2 musφ b²t menÜφ ne₧ deset (0-9).
if ((loop1<10) && (loop2<10))// Pouze pokud je obdΘlnφk v hracφ ploÜe
{
Zjistφme p°ejetφ vÜech okolnφch linek obdΘlnφku. Kraje testujeme v po°adφ: hornφ, dolnφ, lev² a prav². Po ka₧dΘm pr∙chodu vnit°nφm cyklem se inkrementuje loop1 a tφm se z pravΘho okraje stßvß lev² okraj nßsledujφcφho obdΘlnφku. V p°φpad∞ pr∙chodu vn∞jÜφ smyΦkou se ze spodnφch hran obdΘlnφk∙ v °ßdku stßvajφ hornφ okraje nov²ch obdΘlnφk∙ v °ßdku o jedno nφ₧e. VÜe by m∞lo b²t z°ejmΘ z diagramu.
Pokud jsou vÜechny okraje projetΘ (rovnajφ se TRUE), m∙₧eme namapovat texturu a vykreslit obdΘlnφk. D∞lßme to stejn²m stylem, jako jsme roz°ezßvali texturu znakovΘ sady na jednotlivß pφsmena. Ani te∩ se neobejdeme bez matematiky. D∞lφme loop1 i loop2 deseti, proto₧e chceme rozd∞lit texturu mezi sto obdΘlnφk∙ (10x10). Koordinßty jsou v rozmezφ od nuly do jednΘ s krokem jednΘ desetiny (1/10=0,1).
Tak₧e abychom dostali prav² hornφ roh, vyd∞lφme hodnotu prom∞nn²ch loop deseti a p°iΦteme 0,1 k x-ovΘmu koordinßtu. Lev² hornφ roh zφskßme d∞lenφm bez ₧ßdn²ch dalÜφch komplikacφ. Lev² dolnφ bod spoΦφvß op∞t v d∞lenφ deseti a p°iΦtenφ 0,1 k ypsilonovΘ slo₧ce. Dostßvßme se k pravΘmu dolnφmu rohu, u kterΘho se po vyd∞lenφ p°iΦφtß 0,1 k ob∞ma sou°adnicov²m slo₧kßm. Doufßm, ₧e to dßvß smysl (Jß taky - p°ekl.).
Pokud budou oba loopy rovny devφti, ve v²sledku dostaneme kombinaci 0,9 a 1,0, kterΘ dosadφme do parametr∙ funkce glTexCoord2f(x,y). sou°adnice vrchol∙ obdΘlnφk∙ pro glVertex2d(x,y) zφskßme analogicky jako okraje linek m°φ₧ky. P°iΦφtßme k nim, ale jeÜt∞ konstanty (1, 59, 1, 39), kterΘ zajiÜ¥ujφ zmenÜenφ obdΘlnφk∙ - aby se veÜly do polφΦek m°φ₧ky a p°itom nic nep°ekryly.
// Jsou p°ejety vÜechny Φty°i okraje obdΘlnφku?
if (hline[loop1][loop2] && hline[loop1][loop2+1] && vline[loop1]loop2] && vline[loop1+1][loop2])
{
glBegin(GL_QUADS);// Vykreslφ otexturovan² obdΘlnφk
glTexCoord2f(float(loop1/10.0f)+0.1f,1.0f-(float(loop2/10.0f)));
glVertex2d(20+(loop1*60)+59,(70+loop2*40+1));// Prav² hornφ
glTexCoord2f(float(loop1/10.0f),1.0f-(float(loop2/10.0f)));
glVertex2d(20+(loop1*60)+1,(70+loop2*40+1));// Lev² hornφ
glTexCoord2f(float(loop1/10.0f),1.0f-(float(loop2/10.0f)+0.1f));
glVertex2d(20+(loop1*60)+1,(70+loop2*40)+39);// Lev² dolnφ
glTexCoord2f(float(loop1/10.0f)+0.1f,1.0f-(float(loop2/10.0f)+0.1f));
glVertex2d(20+(loop1*60)+59,(70+loop2*40)+39);// Prav² dolnφ
glEnd();// Konec kreslenφ
}
}
V zßv∞ru vypneme mapovßnφ textur a po opuÜt∞nφ obou cykl∙ vrßtφme Üφ°ku Φßry na p∙vodnφ hodnotu.
glDisable(GL_TEXTURE_2D);// Vypne mapovßnφ textur
}
}
glLineWidth(1.0f);// èφ°ka Φßry 1.0f
V p°φpad∞, ₧e je anti rovno TRUE, zapneme zjem≥ovßnφ linek (antialiasing).
if (anti)// Mß b²t zapnut² antialiasing?
{
glEnable(GL_LINE_SMOOTH);// Zapne antialiasing
}
Abychom usnadnili hru, p°idßme specißlnφ objekt - p°es²pacφ hodiny, jejich₧ sebrßnφm se nep°ßtelΘ na chvφli zastavφ. Pro jejich umφst∞nφ v hracφm poli pou₧φvßme prom∞nnΘ x a y, nicmΘn∞ proto₧e se nebudou pohybovat, m∙₧eme vyu₧φt nepot°ebnΘ fx jako p°epφnaΦ (0 jsou viditelnΘ, 1 nejsou, 2 hrßΦ je sebral). Fy implementujeme pro ΦφtaΦ, jak dlouho by m∞ly b²t viditelnΘ.
ZaΦneme testem viditelnosti. Pokud se nemajφ zobrazit, p°eskoΦφme vykreslenφ. Pokud ano, resetujeme matici a translacφ je umφstφme. Proto₧e m°φ₧ka zaΦφnß na dvacφtce, p°iΦteme tuto hodnotu k x*60. Ze stejnΘho d∙vodu na ose y p°iΦφtßme 70. Dßle orotujeme matici okolo osy z o ·hel ulo₧en² v hourglass.spin. P°ed vykreslenφm jeÜt∞ zvolφme nßhodnou barvu.
if (hourglass.fx==1)// Hodiny se majφ vykreslit
{
glLoadIdentity();// Reset Matice
glTranslatef(20.0f+(hourglass.x*60),70.0f+(hourglass.y*40),0.0f);// Umφst∞nφ
glRotatef(hourglass.spin,0.0f,0.0f,1.0f);// Rotace ve sm∞ru hodinov²ch ruΦiΦek
glColor3ub(rand()%255,rand()%255,rand()%255);// Nßhodnß barva
Pomocφ GL_LINES oznßmφme kreslenφ linek. Hornφ lev² bod zφskßme odeΦtenφm p∞ti pixel∙ v obou sm∞rech. Konec p°φmky le₧φ p∞t pixel∙ sm∞rem vpravo dol∙ od aktußlnφ pozice. Druhou linku zaΦneme vpravo naho°e a skonΦφme vlevo dole. Tvar pφsmene X doplnφme o hornφ a dolnφ uzavφracφ linku.
glBegin(GL_LINES);// Vykreslenφ p°es²pacφch hodin
glVertex2d(-5,-5);// Lev² hornφ bod
glVertex2d( 5, 5);// Prav² dolnφ bod
glVertex2d( 5,-5);// Prav² hornφ bod
glVertex2d(-5, 5);// Lev² dolnφ bod
glVertex2d(-5, 5);// Lev² dolnφ bod
glVertex2d( 5, 5);// Prav² dolnφ bod
glVertex2d(-5,-5);// Lev² hornφ bod
glVertex2d( 5,-5);// Prav² hornφ bod
glEnd();// Konec kreslenφ
}
Dßle vykreslφme hrßΦe. Op∞t resetujeme matici a urΦφme pozici ve scΘn∞. VÜimn∞te si, ₧e pro jemn² neskokov² pohyb pou₧φvßme fx a fy. NatoΦφme matici o ulo₧en² ·hel, zvolφme sv∞tle zelenou barvu a pomocφ linek vykreslφme tvar pφsmene X.
glLoadIdentity();// Reset Matice
glTranslatef(player.fx+20.0f,player.fy+70.0f,0.0f);// P°esun na pozici
glRotatef(player.spin,0.0f,0.0f,1.0f);// Rotace po sm∞ru hodinov²ch ruΦiΦek
glColor3f(0.0f,1.0f,0.0f);// Zelenß barva
glBegin(GL_LINES);// Vykreslenφ hrßΦe
glVertex2d(-5,-5);// Lev² hornφ bod
glVertex2d( 5, 5);// Prav² dolnφ bod
glVertex2d( 5,-5);// Prav² hornφ bod
glVertex2d(-5, 5);// Lev² dolnφ bod
glEnd();// Konec kreslenφ
Aby nevypadal a₧ tak nudn∞, p°idßme jeÜt∞ tvar znamφnka +, kterΘ se otßΦφ trochu rychleji, mß tmavÜφ barvu a je o dva pixely v∞tÜφ.
glRotatef(player.spin*0.5f,0.0f,0.0f,1.0f);// Rotace po sm∞ru hodinov²ch ruΦiΦek
glColor3f(0.0f,0.75f,0.0f);// TmavÜφ zelenß barva
glBegin(GL_LINES);// PokraΦovßnφ kreslenφ hrßΦe
glVertex2d(-7, 0);// Lev² st°edov² bod
glVertex2d( 7, 0);// Prav² st°edov² bod
glVertex2d( 0,-7);// Hornφ st°edov² bod
glVertex2d( 0, 7);// Dolnφ st°edov² bod
glEnd();// Konec kreslenφ
JeÜt∞ zb²vß vykreslit nep°ßtele, tak₧e se do nich pustφme. Deklarujeme cyklus prochßzejφcφ vÜechny nep°ßtele, kte°φ jsou viditelnφ v konkrΘtnφm levelu. Tento poΦet zφskßme vynßsobenφm levelu s obtφ₧nostφ. Jejich maximßlnφ poΦet je dev∞t. Uvnit° smyΦky resetujeme matici a umφstφme prßv∞ vykreslovanΘho nep°φtele pomocφ fx a fy (m∙₧e se pohybovat). Zvolφme r∙₧ovou barvu a pomocφ linek vykreslφme Φtverec postaven² na ÜpiΦku, kter² nerotuje.
for (loop1=0; loop1<(stage*level); loop1++)// Vykreslφ nep°ßtele
{
glLoadIdentity();// Reset matice
glTranslatef(enemy[loop1].fx+20.0f,enemy[loop1].fy+70.0f,0.0f);// P°esun na pozici
glColor3f(1.0f,0.5f,0.5f);// R∙₧ovß barva
glBegin(GL_LINES);// Vykreslenφ nep°ßtel
glVertex2d( 0,-7);// Hornφ bod
glVertex2d(-7, 0);// Lev² bod
glVertex2d(-7, 0);// Lev² bod
glVertex2d( 0, 7);// Dolnφ bod
glVertex2d( 0, 7);// Dolnφ bod
glVertex2d( 7, 0);// Prav² bod
glVertex2d( 7, 0);// Prav² bod
glVertex2d( 0,-7);// Hornφ bod
glEnd();// Konec kreslenφ
P°idßme krvav∞ ΦervenΘ X, kterΘ se otßΦφ okolo osy z a potΘ ukonΦφme obrovskou vykreslovacφ funkci.
glRotatef(enemy[loop1].spin,0.0f,0.0f,1.0f);// Rotace vnit°ku nep°φtele
glColor3f(1.0f,0.0f,0.0f);// Krvavß barva
glBegin(GL_LINES);// PokraΦovßnφ kreslenφ nep°ßtel
glVertex2d(-7,-7);// Lev² hornφ bod
glVertex2d( 7, 7);// Prav² dolnφ bod
glVertex2d(-7, 7);// Lev² dolnφ bod
glVertex2d( 7,-7);// Prav² hornφ bod
glEnd();// Konec kreslenφ
}
return TRUE;// Konec funkce
}
Zm∞n ve funkci WinMain() bude takΘ trochu vφc. Proto₧e se jednß o hru, musφme oÜet°it ovlßdßnφ klßvesnicφ, Φasovßnφ a vÜe ostatnφ, co jsme dosud neud∞lali.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
BOOL done=FALSE;
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?", MB_YESNO|MB_ICONQUESTION) == IDNO)
{
fullscreen=FALSE;
}
Zm∞nφme titulek okna na "NeHe's Line Tutorial" a p°idßme volßnφ funkce ResetObjects(), kterß inicializuje pozici hrßΦe na lev² hornφ roh a nep°ßtel∙m p°ed∞lφ nßhodnΘ umφst∞nφ, nejmΘn∞ vÜak p∞t polφΦek od hrßΦe. PotΘ zavolßme funkci pro inicializaci timeru.
if (!CreateGLWindow("NeHe's Particle Tutorial",640,480,16,fullscreen))
{
return 0;
}
ResetObjects();// Inicializuje pozici hrßΦe a nep°ßtel
TimerInit();// Zprovozn∞nφ timeru
while(!done)
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.message==WM_QUIT)
{
done=TRUE;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
Te∩ zajistφme, aby pracoval k≤d pro Φasovßnφ. P°edtφm ne₧ vykreslφme scΘnu, nagrabujeme aktußlnφ Φas a ulo₧φme jej do desetinnΘ prom∞nnΘ nazvanΘ start. Potom vykreslφme scΘnu a prohodφme buffery.
float start=TimerGetTime();// Nagrabujeme aktußlnφ Φas
if ((active && !DrawGLScene()) || keys[VK_ESCAPE])
{
done=TRUE;
}
else
{
SwapBuffers(hDC);
}
Vytvo°φme ΦasovΘ zpo₧d∞nφ a to tak, ₧e vklßdßme prßzdnΘ p°φkazy tak dlouho, dokud je aktußlnφ hodnota ΦasovaΦe (TimerGetTime()) menÜφ ne₧ poΦßteΦnφ hodnota seΦtenß s rychlostφ kroky hry krßt dva. tφmto velmi jednoduÜe zpomalφme OPRAVDU rychlΘ systΘmy.
Proto₧e pou₧φvßme krokovßnφ rychlosti (urΦenΘ prom∞nnou adjust) program v₧dy pob∞₧φ stejnou rychlostφ. Nap°φklad, pokud je hodnota kroku rovna jednΘ, m∞li bychom Φekat dokud timer nebude v∞tÜφ nebo roven dv∞ma (2*1). Ale pokud zv∞tÜφme rychlost kroku na dva (zp∙sobφ, ₧e se hrßΦ bude pohybovat o dvakrßt tolik pixel∙ najednou), zpo₧d∞nφ se zv∞tÜφ na Φty°i (2*2). AΦkoli se pohybujeme dvakrßt tak rychle, zpo₧d∞nφ trvß dvakrßt dΘle a tudφ₧ hra b∞₧φ stejn∞ rychle (ale vφce trhan∞).
Spousta lidφ jde ale jinou cestou ne₧ my. Je t°eba brßt v ·vahu Φas kter² ub∞hl mezi jednotliv²mi cykly ve kter²ch se renderuje. Na zaΦßtku ka₧dΘho cyklu se ulo₧φ aktußlnφ Φas, od kterΘho se odeΦte Φas v minulΘm cyklu a tφmto rozdφlem se vyd∞lφ rychlost, kterou se mß objekt pohybovat. Nap°φklad: mßme auto, kterΘ mß jet rychlostφ 10 jednotek za sekundu. Vφme, ₧e mezi tφmto a p°edchozφm cyklem ub∞hlo 20 ms. Objekt musφme tedy posunout o 10/(20*1000) = 0,0005 jednotek. Bohu₧el v tomto programu to takto provΘst nem∙₧eme, proto₧e pou₧φvßme m°φ₧ku a ne nap°. otev°enou krajinu. Hodnoty fx a fy musφ b²t p°esn∞ urΦenΘ. Pokud hrßΦova fx bude °ekn∞me 59 a poΦφtaΦ rozhodne posunout hrßΦe o dva pixely doprava, tak po stisku Üipky nahoru hrßΦ nep∙jde po "Üedesßt²ch pixelech", ale o kousek vedle.
Poznßmka p°ekladatele: NicmΘn∞ i naÜe metoda mß jeden velk² error - okno nem∙₧e v Φekacφch cyklech zpracovßvat ₧ßdnΘ zprßvy. ╪ekn∞me, ₧e bude (pon∞kud p°e₧enu) ΦasovΘ zpo₧d∞nφ 5 sekund. Okno nenφ aktivnφ a u₧ivateli p°ipadß, ₧e v programu nastala fatßlnφ chyba. Pokusφ se ho ukonΦit, ale i to se mu poda°φ a₧ za t∞chto p∞t sekund. A pokud se bude zpomalovacφ k≤d volat Φast∞ji (nap°. po ka₧dΘm p°ekreslenφ)... chßpete? I u nßs je tento problΘm trochu znateln². Pokud se pokouÜφte zatoΦit do urΦitΘ linky, n∞kdy se strefφte a₧ na n∞kolikßt² pokus - program nezareaguje vΦas. ProΦ vlastn∞ vzniklo vφcevlßknovΘ programovßnφ? Aby odstranilo zdßnliv∞ "spadnutΘ programy" p°i nßroΦn²ch a dlouho trvajφcφch v²poΦtech. Jß osobn∞, bych se takovΘmuto Φasovßnφ vyhnul.
// Pl²tvß cykly procesoru na rychl²ch systΘmech
while(TimerGetTime() < start + float(steps[adjust] * 2.0f))
{
}
if (keys[VK_F1])
{
keys[VK_F1]=FALSE;
KillGLWindow();
fullscreen =! fullscreen;
if (!CreateGLWindow("NeHe's Line Tutorial",640,480,16,fullscreen))
{
return 0;
}
}
P°ejdeme k ovlßdßnφ klßvesnicφ. Po stisku 'A' znegujeme prom∞nnou anti a tφm oznßmφme k≤du pro kreslenφ, ₧e mß nebo nemß pou₧φvat antialiasing.
if (keys['A'] && !ap)// Stisk A
{
ap = TRUE;// Nastavφ p°φznak
anti=!anti;// Zapne/vypne antialiasing
}
if (!keys['A'])// Uvoln∞nφ A
{
ap=FALSE;// Vypne p°φznak
}
Te∩ pohyb a logika nep°ßtel. Cht∞l jsem udr₧et k≤d opravdu jednoduch², tak₧e neΦekejte ₧ßdnΘ zßzraky. Pracuje tak, ₧e nep°ßtelΘ zjistφ, kde je hrßΦ a potΘ se vydajφ jeho sm∞rem (na pozici x, y). Mohou nap°φklad vid∞t, ₧e je v hracφm poli naho°e, ale v Φase, kdy testovali pozici x, hrßΦ u₧ m∙₧e b²t dφky fx ·pln∞ n∞kde jinde. ╚astokrßt se dostanou tam, kde byl o krok p°edtφm. N∞kdy vypadajφ opravdu zmaten∞.
ZaΦneme ujiÜt∞nφm se, jestli u₧ nenφ konec hry a jestli je okno aktivnφ. Pokud se nap°φklad minimalizovalo, nep°ßtelΘ se nebudou na pozadφ pohybovat.
Vytvo°φme cyklus, kter² i tentokrßt prochßzφ vÜechny nep°ßtele.
if (!gameover && active)// Nenφ-li konec hry a okno je aktivnφ
{
for (loop1=0; loop1<(stage*level); loop1++)// Prochßzφ vÜechny nep°ßtele
{
V p°φpad∞, ₧e bude x pozice nep°φtele menÜφ ne₧ x pozice hrßΦe a zßrove≥ se takΘ musφ rovnat y*40 pozici y (jsme v pr∙seΦφku vertikßlnφ a horizontßlnφ linky) posuneme nep°φtele doprava. Analogick²m zp∙sobem implementujeme i pohyb doleva, nahoru a dol∙.
Poznßmka: po zm∞n∞ pozic x a y nelze vid∞t ₧ßdn² pohyb, proto₧e p°i vykreslovßnφ objekty umφs¥ujeme pomocφ prom∞nn²ch fx a fy. Zm∞nou x a y jenom urΦujeme po₧adovan² sm∞r pohybu.
if ((enemy[loop1].x < player.x) && (enemy[loop1].fy==enemy[loop1].y*40))
{
enemy[loop1].x++;// P°esun o polφΦko doprava
}
if ((enemy[loop1].x > player.x) && (enemy[loop1].fy==enemy[loop1].y*40))
{
enemy[loop1].x--;// P°esun o polφΦko doleva
}
if ((enemy[loop1].y < player.y) && (enemy[loop1].fx==enemy[loop1].x*60))
{
enemy[loop1].y++;// P°esun o polφΦko dol∙
}
if ((enemy[loop1].y > player.y) && (enemy[loop1].fx==enemy[loop1].x*60))
{
enemy[loop1].y--;// P°esun o polφΦko nahoru
}
Nßsledujφcφ k≤d provßdφ opravdov² pohyb. Zjistφme, zda je prom∞nnß delay v∞tÜφ ne₧ t°i mφnus level. Pokud jsme v levelu jedna, program pojde cyklem dvakrßt (3-1=2), p°edtφm ne₧ se nep°φtel opravdu pohne. V levelu t°i (nejvyÜÜφ mo₧n²) se nep°ßtelΘ budou pohybovat stejnou rychlostφ jako hrßΦ - tedy bez zpo₧d∞nφ. TakΘ ov∞°ujeme, jestli se hourglas.fx nerovnß dv∞ma. Tato prom∞nnß oznaΦuje hrßΦovo sebrßnφ p°es²pacφch hodin. V takΘm p°φpad∞ nep°φtelem nepohybujeme.
Pokud je zpo₧d∞nφ vyÜÜφ ne₧ t°i mφnus level a hrßΦ nesebral hodiny, pohneme nep°φtelem ·pravou prom∞nn²ch fx a fy. Nejprve vynulujeme zpo₧d∞nφ, tak₧e ho budeme moci znovu poΦφtat a potom op∞t deklarujeme cyklus, kter² prochßzφ vÜechy viditelnΘ nep°ßtele.
if (delay > (3-level) && (hourglass.fx!=2))// HrßΦ nesebral p°es²pacφ hodiny
{
delay=0;// Reset delay na nulu
for (loop2=0; loop2<(stage*level); loop2++)// Prochßzφ vÜechny nep°ßtele
{
Nep°φtel se v₧dy pohybuje pomocφ fx/fy sm∞rem k x/y. V prvnφm if zjistφme jestli je fx menÜφ ne₧ x*60. V takovΘm p°φpad∞ ho posuneme doprava o vzdßlenost steps[adjust]. TakΘ zm∞nφme jeho ·hel natoΦenφ, aby vznikl dojem rolovßnφ doprava.
┌pln∞ stejn∞ provedeme pohyby doleva, dol∙ a nahoru.
if (enemy[loop2].fx < enemy[loop2].x*60)// Fx je menÜφ ne₧ x
{
enemy[loop2].fx+=steps[adjust];// Zv²Üit fx
enemy[loop2].spin+=steps[adjust];// Rotace ve sm∞ru hodinov²ch ruΦiΦek
}
if (enemy[loop2].fx > enemy[loop2].x*60)// Fx je v∞tÜφ ne₧ x
{
enemy[loop2].fx-=steps[adjust];// Snφ₧it fx
enemy[loop2].spin-=steps[adjust];// Rotace proti sm∞ru hodinov²ch ruΦiΦek
}
if (enemy[loop2].fy < enemy[loop2].y*40)// Fy je menÜφ ne₧ y
{
enemy[loop2].fy+=steps[adjust];// Zv²Üit fy
enemy[loop2].spin+=steps[adjust];// Rotace ve sm∞ru hodinov²ch ruΦiΦek
}
if (enemy[loop2].fy > enemy[loop2].y*40)// Fy je v∞tÜφ ne₧ y
{
enemy[loop2].fy-=steps[adjust];// Snφ₧it fy
enemy[loop2].spin-=steps[adjust];// Rotace proti sm∞ru hodinov²ch ruΦiΦek
}
}
}
Pohyb tedy mßme. nynφ pot°ebujeme vy°eÜit nßraz nep°ßtel do hrßΦe. V p°φpad∞, ₧e se ob∞ fx i ob∞ fy rovnajφ... hrßΦ zem°e. Dekrementujeme ₧ivoty a v p°φpad∞ jejich nulovΘ hodnoty prohlßsφme hru za skonΦenou. Resetujeme vÜechny objekty a nechßme zahrßt ·mrtnφ skladbu.
Zvuky jsou v naÜich tutorißlech novinkou. rozhodl jsem se pou₧φt tu nejzßkladn∞jÜφ dostupnou rutinu... PlaySound(). P°edßvßme jφ t°i parametry. Prvnφ urΦuje cestu k souboru se zvukem. Druh² parametr pomocφ nulovΘho ukazatele ignorujeme. T°etφ parametr je flag stylu. Dva nejΦast∞ji pou₧φvanΘ jsou: SND_SYNC, kter² zastavφ provßd∞nφ programu, dokud p°ehrßvßnφ zvuku neskonΦφ. Druhß mo₧nost, SND_ASYNC, p°ehrßvß zvuk nezßvisle na b∞hu programu. Dßme p°ednost maliΦkΘmu zpo₧d∞nφ, tak₧e funkci p°edßme SND_SYNC.
Na zaΦßtku tutorißlu jsem zapomn∞l na jednu v∞c: Abychom mohli pou₧φvat funkci PlaySound(), pot°ebujeme inkludovat knihovnu WINMM.LIB (Windows Multimedia Library). Ve Visual C++ to lze provΘst v nabφdce Project/Setting/Link.
// Setkßnφ nep°φtele s hrßΦem
if ((enemy[loop1].fx==player.fx) && (enemy[loop1].fy==player.fy))
{
lives--;// HrßΦ ztrßcφ ₧ivot
if (lives==0)// Nulov² poΦet ₧ivot∙
{
gameover=TRUE;// Konec hry
}
ResetObjects();// Reset pozice hrßΦe a nep°ßtel
PlaySound("Data/Die.wav", NULL, SND_SYNC);// Zahraje umφrßΦek
}
}
OÜet°φme stisk kurzorov²ch klßves. Vy°eÜφme Üipku doprava, ostatnφ sm∞ry jsou zcela analogickΘ. Abychom nevypadli pryΦ z hracφho pole musφ b²t player.x menÜφ ne₧ deset (Üφ°ka m°φ₧ky). Nechceme, aby mohl zm∞nit sm∞r uprost°ed p°esunu a tak kontrolujeme, zda se fx==player.x*60 a fy==player.y*40. Nastanou-li ob∞ rovnosti, m∙₧eme s urΦitostφ °φci, ₧e se nachßzφ v pr∙seΦφku rovnob∞₧nΘ se svislou linkou a tedy dokonΦil sv∙j pohyb. Platφ-li vÜechny podmφnky, oznaΦφme linku pod hrßΦem jako p°ejetou a posuneme jej na nßsledujφcφ pozici.
if (keys[VK_RIGHT] && (player.x<10) && (player.fx==player.x*60) && (player.fy==player.y*40))
{
hline[player.x][player.y]=TRUE;// OznaΦenφ linky
player.x++;// Doprava
}
if (keys[VK_LEFT] && (player.x>0) && (player.fx==player.x*60) && (player.fy==player.y*40))
{
hline[player.x][player.y]=TRUE;// OznaΦenφ linky
player.x--;// Doleva
}
if (keys[VK_DOWN] && (player.y<10) && (player.fx==player.x*60) && (player.fy==player.y*40))
{
vline[player.x][player.y]=TRUE;// OznaΦenφ linky
player.y++;// Dol∙
}
if (keys[VK_UP] && (player.y>0) && (player.fx==player.x*60) && (player.fy==player.y*40))
{
vline[player.x][player.y]=TRUE;// OznaΦenφ linky
player.y--;// Nahoru
}
HrßΦe mßme, dß se °φci, p°esunutΘho - ale pouze v programu! Je viditeln² stßle na stejnΘm mφst∞, proto₧e ho vykreslujeme pomocφ fx a fy. Provnßme, polohu fx vzhledem k x a pokud se nerovnajφ, snφ₧φme vzdßlenost mezinimi o p°esn∞ dan² ·sek. Po n∞kolika p°ekreslenφch se zaΦnou ob∞ hodnoty rovnat, co₧ znaΦφ, ₧e dokonΦil pohyb a nynφ se nachßzφ v pr∙seΦφku linek. P°i nßslednΘm stisku klßvesy m∙₧eme zaΦφt hrßΦe znovu posunovat (viz. k≤d v²Üe).
if (player.fx<player.x*60)// Fx je menÜφ ne₧ x
{
player.fx+=steps[adjust];// Zv∞tÜφ fx
}
if (player.fx>player.x*60)// Fx je v∞tÜφ ne₧ x
{
player.fx-=steps[adjust];// ZmenÜφ fx
}
if (player.fy<player.y*40)// Fy je menÜφ ne₧ y
{
player.fy+=steps[adjust];// Zv∞tÜφ fy
}
if (player.fy>player.y*40)// Fy je v∞tÜφ ne₧ y
{
player.fy-=steps[adjust];// ZmenÜφ fy
}
}
Nastane-li konec hry, projde program v∞tvφ else. V nφ je pouze test stisku mezernφku, kter² znovu spustφ hru. Nastavφme filled na TRUE a dφky tomu si program bude myslet, ₧e je m°φ₧ka kompletn∞ vypln∞nß - resetuje se pozice hrßΦe i nep°ßtel. Abychom byli p°esnφ, program si vlastn∞ myslφ, ₧e jsme dokonΦili level, a proto inkrementuje do stage p°i°azenou nulu na jedna. P°esn∞ tohle chceme. Äivot vrßtφme na poΦßteΦnφ hodnotu.
else// Jinak (if (!gameover && active))
{
if (keys[' '])// Stisknut² mezernφk
{
gameover = FALSE;// Konec hry
filled = TRUE;// M°φ₧ka vypln∞nß
level = 1;// Level
level2 = 1;// Zobrazovan² level
stage = 0;// Obtφ₧nost hry
lives = 5;// PoΦet ₧ivot∙
}
}
Nßsledujφcφ Φßst testuje, zda je m°φ₧ka kompletn∞ vypln∞nß. Filled m∙₧e b²t nastaveno na TRUE celkem dv∞ma zp∙soby. Bu∩ je m°φ₧ka ·pln∞ vypln∞nß, nebo skonΦila hra (zabitφm hrßΦe; nula ₧ivot∙) a u₧ivatel stiksl mezernφk, aby ji restartoval.
if (filled)// Vypln∞nß m°φ₧ka?
{
A¥ u₧ to zp∙sobil kter²koli p°φpad je nßm to celkem jedno. V₧dy zahrajeme zvuk znaΦφcφ ukonΦenφ levelu. U₧ jsme jednou vysv∞tloval, jak PlaySound() pracuje. P°edßnφm SND_SYNC vytvo°φme ΦasovΘ zpo₧d∞nφ, kdy program Φekß a₧ zvuk dohraje.
PlaySound("Data/Complete.wav", NULL, SND_SYNC);// Zvuk ukonΦenφ levelu
Potom inkrementujeme stage a zjistφme, jestli nenφ v∞tÜφ ne₧ t°i. Pokud ano, vrßtφme ho na jedno, zv∞tÜφme vnit°nφ i zobrazovan² level o jedniΦku.
stage++;// Inkrementace obtφ₧nosti
if (stage > 3)// Je v∞tÜφ ne₧ t°i?
{
stage=1;// Reset na jedniΦku
level++;// Zv∞tÜφ level
level2++;// Zv∞tÜφ zobrazovan² level
Pokud bude vnit°nφ level v∞tÜφ ne₧ t°i, vrßtφme ho zp∞t na trojku a p°idßme hrßΦi jeden ₧ivot, ale pouze do maximßlnφch p∞ti. Vφce ₧iv² nikdy nebude.
if (level>3)// Je level v∞tÜφ ne₧ t°i?
{
level=3;// Vrßtφ ho zpßtky na t°i
lives++;// Äivot navφc
if (lives > 5)// Mß vφc ₧ivot∙ ne₧ p∞t?
{
lives = 5;// Maximßlnφ poΦet ₧ivot∙ p∞t
}
}
}
Resetujeme vÜechny objekty ve h°e (h°ßΦ, nep°ßtelΘ) a vynulujeme flag projetφ vÜech linek na FALSE. Pokud bychom to neud∞lali, dalÜφ level by byl p°edΦasn∞ ukonΦen - program by op∞t skoΦil do tohoto k≤du. Mimochodem, je ·pln∞ stejn² jako k≤d pro vykrelsovßnφ m°φ₧ky.
ResetObjects();// Reset pozice hrßΦe a nep°ßtel
for (loop1=0; loop1<11; loop1++)// Cyklus skrz x koordinßty m°φ₧ky
{
for (loop2=0; loop2<11; loop2++)// Cyklus skrz y koordinßty m°φ₧ky
{
if (loop1 < 10)// X musφ b²t menÜφ ne₧ deset
{
hline[loop1][loop2] = FALSE;// Nulovßnφ
}
if (loop2 < 10)// Y musφ b²t menÜφ ne₧ deset
{
vline[loop1][loop2] = FALSE;// Nulovßnφ
}
}
}
}
Pokusφme se umplementovat hrßΦovo sebrßnφ p°es²pacφch hodin. Äe si musφ polohy odpovφdat je, myslφm si, jasnΘ. NicmΘn∞ p°idßvßme jeÜt∞ podmφnku hourgalss.fx==1. Nejednß se o ₧ßdnou polohu. Fx pou₧φvßme jako indikßtor toh, ₧e jsou zobrazenΘ na monitoru.
// HrßΦ sebral p°es²pacφ hodiny
if ((player.fx==hourglass.x*60) && (player.fy==hourglass.y*40) && (hourglass.fx==1))
{
Nechßme zahrßt zvuk zmrazenφ. Aby zvuk zn∞l na pozadφ, pou₧φvßme SND_ASYNC. Dφky OR-ovßnφ se symbolickou konstantou SND_LOOP docφlφme toho, ₧e se po dokonΦenφ p°ehrßvßnφ zvuku sßm znovu spustφ. Zastavit ho m∙₧eme bu∩ po₧adavkem na zastvenφ, nebo p°ehrßnφm jinΘho zvuku.
Aby hodiny nebyly dßle zobrazenΘ nastavφme fx na dva. TakΘ p°i°adφme do fy nulu. Fy je n∞co jako ΦφtaΦ, kter² inkrementujeme do urΦitΘ hodnoty, po jejφm₧ p°eteΦenφ zm∞nφme hodnotu fx.
PlaySound("Data/freeze.wav", NULL, SND_ASYNC | SND_LOOP);// Zvuk zmrazenφ
hourglass.fx=2;// Skryje hodiny
hourglass.fy=0;// Nuluje ΦφtaΦ
}
Nßsledujφcφ k≤d zajiÜ¥uje nar∙stßnφ rotace hrßΦe o polovinu ni₧Üφ rychlostφ ne₧ mß hra. V p°φpad∞, ₧e bude hodnota vyÜÜφ ne₧ 360░ odeΦteme 360. Tφm zajistφme, aby nebyla moc vysokß.
player.spin += 0.5f * steps[adjust];// Rotace hrßΦe
if (player.spin>360.0f)// ┌hel je v∞tÜφ ne₧ 360░
{
player.spin -= 360;// OdeΦte 360
}
Aby se hodiny toΦily opaΦn²m sm∞rem ne₧ hrßΦ, namφsto zvyÜovßnφ, ·hel sni₧ujeme. Rychlost je Φtvrtinovß oproti rychlosti hry. Op∞t oÜet°φme podteΦenφ prom∞nnΘ.
hourglass.spin-=0.25f*steps[adjust];// Rotace p°es²pacφch hodin
if (hourglass.spin < 0.0f)// ┌hel je menÜφ ne₧ 0░
{
hourglass.spin += 360.0f;// P°iΦte 360
}
Zv∞tÜφme hodnotu ΦφtaΦe p°es²pacφch hodin, o kterΘ jsme mluvili p°ed chvφlφ. Op∞t podle rychlosti hry. Dßle zjistφme, jestli se hourglass.fx rovnß nule (nejsou zobrazenΘ) a zßrove≥ jelsti je ΦφtaΦ v∞tÜφ ne₧ 6000 d∞leno level. V takovΘm p°φpad∞ p°ehrajeme zvuk zobrazenφ, vygenerujeme novou pozici a p°es fx=1 hodiny zobrazφme. Vynulujeme ΦφtaΦ, aby mohl poΦφtat znovu.
hourglass.fy+=steps[adjust];// Zv∞tÜenφ hodnoty ΦφtaΦe p°es²pacφch hodin
if ((hourglass.fx==0) && (hourglass.fy > 6000/level))// Hodiny jsou skrytΘ a p°etekl ΦφtaΦ
{
PlaySound("Data/hourglass.wav", NULL, SND_ASYNC);// Zvuk zobrazenφ hodin
hourglass.x = rand()%10+1;// Nßhodnß pozice
hourglass.y = rand()%11;// Nßhodnß pozice
hourglass.fx = 1;// Zobrazenφ hodin
hourglass.fy = 0;// Nulovßnφ ΦφtaΦe
}
P°ekl-li ΦφtaΦ v dob∞, kdy jsou hodiny viditelnΘ (fx==1), schovßme je a op∞t vynulujeme ΦφtaΦ.
if ((hourglass.fx==1) && (hourglass.fy>6000/level))// Hodiny jsou zobrazenΘ a p°etekl ΦφtaΦ
{
hourglass.fx = 0;// Skr²t hodiny
hourglass.fy = 0;// Nulovßnφ ΦφtaΦe
}
P°i hrßΦov∞ sebrßnφ hodin jsme zmrazili vÜechny nep°ßtele. Nynφ je rozmrazφme. Fx==2 indikuje, ₧e byly hodiny sebrßny. Fy porovnßvßme s vypoΦtenou hodnotou. Jsou-li ob∞ podmφnyk pravdivΘ, vypneme zvuk, kter² znφ ve smyΦce na pozadφ a to tak, ₧e p°ehrajeme nulov² zvuk. Zneviditelnφme hodiny a vynulujeme jejich ΦφtaΦ.
if ((hourglass.fx==2) && (hourglass.fy>500+(500*level)))// Nep°ßtelΘ zmrazenφ a p°etekl ΦφtaΦ
{
PlaySound(NULL, NULL, 0);// Vypne zvuk zmrazenφ
hourglass.fx = 0;// Skr²t hodiny
hourglass.fy = 0;// Nulovßnφ ΦφtaΦe
}
Na samΘm konci hlavnφ smyΦky programu inkrementujeme prom∞nnou delay. To je, myslφm si, vÜe.
delay++;// Inkrementuje ΦφtaΦ zpo₧d∞nφ nep°ßtel
}
}
KillGLWindow();// ZruÜφ okno
return (msg.wParam);// UkonΦφ program
}
Psanφm tohoto tutorißlu jsem strßvil spoustu Φasu. ZaΦφnal jako zcela jednoduch² tutorißl o linkßch, kter² se ·pln∞ neΦekan∞ rozvinul v menÜφ hru. Doufejme, ₧e budete moci ve sv²ch programech vyu₧φt vÜe, co jste se zde nauΦili. Vφm, ₧e se spousta z vßs ptala po h°e s kostiΦkami a polφΦky. Nemohli jste dostat vφce kostiΦkovat∞jÜφ a vφce polφΦkovat∞jÜφ hru ne₧ je tato. AΦkoli lekce nevysv∞tluje mnoho nov²ch v∞cφ o OpenGL, myslφm si, ₧e Φasovßnφ a zvuky jsou takΘ d∙le₧itΘ - zvlßÜ¥ ve hrßch. Co jeÜt∞ napsat? Asi nic...
napsal: Jeff Molofee - NeHe
p°elo₧il: Michal Turek - Woq