Lekce 21

Lekce 21 - P°φmky, antialiasing, Φasovßnφ, pravo·hlß projekce, zßkladnφ zvuky a jednoduchß hernφ logika

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φ

}

Textura hracφ plochy

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

{

Diagram

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

ZdrojovΘ k≤dy

Lekce 21

<<< Lekce 20 | Lekce 22 >>>