GrafickΘ aplikace ve Visual C++ (7.) Na konci minulΘ lekce, jsem vßm slφbil, ₧e si tΘto lekci vytvo°φme t°φdu CSprite, kterß bude p°edstavovat pohybujφcφ se obrßzky, kter²m °φkßme sprity. Äe nevφte co jsou to sprity? Prßv∞ od toho je tu dneÜnφ lekce. Op∞t je k dispozici funkΦnφ p°φklad i zdrojov² k≤d, kter² zkompilujete pod VC++ s nainstalovan²m DirectX SDK 8.0. 7.1 Co je to sprite?Mo₧nß je to prvnφ otßzka, kterß vßm jako prvnφ vyvstane v mysli, ale spφÜ bych °ekl, ₧e pokud se ji₧ n∞jakou dobu v∞nujete grafice, urΦit∞ vφte co je sprite. Sprite z anglickΘho p°ekladu znamenß duch nebo p°φzrak. V terminologii programovßnφ (konkrΘtn∞ programovßnφ grafick²ch aplikacφ) je sprite jak²koliv pohybujφcφ se nebo statick² "obrßzek". Vezmeme si p°φklad z jakΘkoliv netextovΘ hry. VeÜker²m grafick²m objekt∙ jako nap°φklad grafickß tlaΦφtka, budovy, jednotky, stromy, ale i texty na obrazovce, m∙₧eme °φkat sprity. 7.2 Typy sprit∙Sprity jdou dßle rozd∞lit na statickΘ (statickΘ tlaΦφtko) a animovanΘ (animovanß postava). Dßle na sprity, kterΘ jsou stßle na stejnΘm mφst∞ obrazovky a na sprity, kterΘ se pohybujφ b∞hem b∞hu programu. P°φ dalÜφm, jeÜt∞ podrobn∞jÜφm d∞lenφ, m∙₧eme vytvo°it sprity, kterΘ ovlßdß um∞lß inteligence, a kterΘ ovlßdß u₧ivatel-hrßΦ. 7.3 T°φda CSprite
Zßkladem aplikace, kterß chce pou₧φvat sprity je t°φda
CSprite, kterß uchovßvß data pot°ebnß k vykreslenφ spritu, k posunutφ
spritu a ke zm∞n∞ animaΦnφ fßze spritu. Tyto t°i operace budou zajiÜ¥ovat t°i
ΦlenskΘ funkce: DrawSprite(), MoveSprite() a
ChangeAnimationPhase().
Kdy₧ to celΘ dßme dohromady, vznikne takov²to nßvrh t°φdy CSprite:
Nynφ m∙₧eme p°istoupit k deklaraci t°φdy:
Vidφte, ₧e staΦφ implementovat pouze 5 metod, proto₧e ostatnφ jsou inline funkce. Pus¥me se tedy do nich. 1) CreateStaticSprite()
Funkce p°ijφmß dva parametry a to sice ukazatel na °et∞zec, ve kterΘm je ulo₧ena cela cesta k bitmap∞ spritu a poΦßteΦnφ poloha spritu. Tuto poΦßteΦnφ hodnotu ihned ulo₧φme do ΦlenskΘ prom∞nnΘ t°φdy. V dalÜφm kroku vytvo°φme povrch napln∞n² bitmapou. K tomu pou₧ijeme odkaz na t°φdu CDisplay, kter² jsme inicializovali v konstruktoru. Funkci CreateSurfaceFromBitmap() jsme probφrali minule tak₧e jen zkrßcen∞: funkce mß t°i parametry: ukazatel na adresu budoucφho povrchu, °et∞zec jmΘna bitmapy a po₧adovanou v²Üku a Üφ°ku. V dalÜφm kroku nastavφme Colorkey pro povrch. Nastavφme natvrdo Φernou barvu jako pr∙hlednou, ale m∙₧ete tento parametr ud∞lat variabilnφ (v∞tÜinou toti₧ pracujete s Φern²m pozadφm). Dßle pot°ebujeme zjistit Üφ°ku a v²Üku jednoho polφΦka p°φpadn∞ Üφ°ku a v²Üku spritu, to vlastn∞ znamenß zjistit velikost vytvo°enΘho povrchu (velikost je stejnß jako bitmapa). Parametry povrchu zjistφme funkcφ GetSurfaceDesc() rozhranφ IDirectDrawSurface7. My ovÜem mßme ukazatel na CSurface a ne na IDirectDrawSurface7. CSurface ovÜem obsahuje funkci, kterß vracφ p°φsluÜn² ukazatel, pomocφ kterΘho m∙₧eme zavolat zmi≥ovanou funkci. Funkce GetSurfaceDesc() p°ijφmß ukazatel na strukturu DDSURFACEDESC2. U tΘto struktury je pot°eba nejd°φve inicializovat velikost, to znamenß atribut dwSize. Nakonec vezmeme pot°ebnou Üφ°ku a v²Üku. VÜimn∞te si, ₧e Üφ°ka je d∞lenß poΦtem snφmk∙ animace (u statickΘho spritu je poΦet snφmk∙ 1, tak₧e je v²slednß Üφ°ka vlastn∞ stejnß). Pot°ebujeme toti₧ zjistit Üφ°ku jednoho polφΦka. 2) CreateAnimatedSprite()
Tuto funkci pou₧ijete chcete-li vytvo°it animovan² sprite (tj. sprite kter² mß vφce jak jeden animaΦnφ krok). Prvnφ dva parametry mß ·pln∞ stejnΘ jako p°edchozφ funkce. DalÜφ dva urΦujφ poΦet snφmku animace a rychlost p°ehrßvßnφ animace v milisekundßch. V prvnφm kroku ulo₧φme vstupnφ parametry do Φlensk²ch prom∞nn²ch t°φdy. Pak staΦφ za zavolat funkci pro vytvo°enφ statickΘho spritu, postup je toti₧ stejn². 3) DrawSprite()
Tato funkce nep°ijφmß ₧ßdnΘ parametry a vracφ to co vrßtφ blitovacφ funkce.
Funkce ColorKeyBlt() pot°ebuje zdrojov² obdΘlnφk tzn. odkud
mß kopφrovat zdrojovß data. Tak₧e v prvnφm kroku tento obdΘlnφk sestavφme.
Postup sestavenφ osv∞tlφ nßsledujφcφ obrßzek: Nynφ u₧ najdete spojitost s tφm co vidφte v k≤du a s tφm co vidφte na obrßzku. Z tohoto vypl²vß, ₧e snφmky v bitmap∞ musφ b²t ulo₧eny horizontßln∞ za sebou jak je vid∞t na obrßzku. Tak₧e prom∞nnß m_iCurrentPhase musφ b∞hat u animovanΘho spritu v rozmezφ 0 a₧ (m_iPhasesCount - 1). To za°φdφ funkce ChangeAnimatioSprite(). Tak₧e te∩ u₧ vφte jak sestavit zdrojov² obdΘlnφk a nynφ ji₧ staΦφ jen zavolat sprßvnou blitovacφ funkci. Pou₧φvßme Colorkey, tak₧e zavolejte ColorKeyBlt(). Ta p°ijφmß za prvΘ pozici kam se bude kreslit (to je naÜe poloha spritu), dßle ukazatel na povrch (op∞t se jednß o rozhranφ IDirectDrawSurface7) a jako poslednφ parametr posφlßme ukazatel na zdrojov² obdΘlnφk (ten kter² jsme p°ed chvilkou sestavili). 4) MoveSprite()
Funkce MoveSprite() je ·pln∞ nejsnazÜφ. Prost∞ jen p°iΦte hodnoty aktußlnφ rychlosti (v obou sm∞rech) k aktußlnφ pozici a tφm posune sprite. Jak vidφte, vracφ v₧dy 0. Do tΘto funkce by p°φpadn∞ p°ibyl clipping spritu. To jest ochrana proti tomu, aby sprite nep°esßhl okraj obrazovky. K tomu pot°ebujeme rozliÜenφ obrazovky, tak₧e se pravd∞podobn∞ budete muset p°idat n∞jakΘ funkce, kterΘ rozliÜenφ vracφ. Princip je snadn², po ka₧dΘ zm∞n∞ polohy zkontrolujte jestli tato novß poloha nep°ekraΦuje mimo obrazovku, pokud ano, zm∞≥te polohu spritu tak, aby vyhovovala p°edchozφ podmφnce. 5) ChangeAnimationSprite()
Funkce ChangeAnimationPhase() je mo₧nß naopak nejslo₧it∞jÜφ. Fßze se nesmφ m∞nit po ka₧dΘm zavolßnφ tΘto funkce, ale jen jednou za dobu urΦenou prom∞nnou m_iAnimationSpeed. Princip je v tom, ₧e si pamatujeme Φas, kdy jsme naposledy m∞nili fßzi a od tΘ doby kontrolujeme, kdy doba od poslednφ zm∞ny p°ekroΦφ zmi≥ovanou animaΦnφ rychlost. Pak op∞t m∞nφme fßzi a op∞t si zapamatujeme Φas zm∞ny. Zßrove≥ kontrolujeme, aby fßze nep°ekroΦila maximßlnφ poΦet snφmk∙ (pokud se k tΘto hodnot∞ p°iblφ₧φ, je aktußlnφ fßze nastavena na 0 a sekvence jede znovu). Funkce GetTickCount() vracφ Φas (v milisekundßch) od startu systΘmu (vracφ celkem obludnΘ Φφslo). SpoΦφtßme rozdφl mezi p°edchozφm (Φas p°edchozφ animace) a souΦasn²m Φasem a kdy₧ tento rozdφl je v∞tÜφ ne₧ animaΦnφ rychlost, inkrementujeme aktußlnφ fßzi (p°itom kontrolujeme hornφ mez fßze) a op∞t si zapamatujeme Φas, kdy k tΘto zm∞n∞ doÜlo. A to je vlastn∞ vÜe. Poznßmka: Kdyby animaΦnφ fßze p°ekroΦila mez urΦenou poΦtem snφmk∙, doÜlo by ve funkci DrawSprite() k fatßlnφ chyb∞, proto₧e bychom se pokouÜeli kopφrovat bitmapu z neexistujφcφ oblasti. Poznßmka: Nezapome≥te napsat konstruktor, kde budou inicializovanΘ prom∞nnΘ tak, ₧e se vytvo°φ implicitn∞ statick² sprite (poΦet snφmku musφ b²t 1). D∙le₧it²m krokem v konstruktoru je takΘ inicializace odkazu na t°φdu CDisplay, kter² je vytvo°en v CControl. Tak₧e budete muset p°idat pßr funkcφ do t°φdy aplikace a CControl, aby jste tento odkaz dostali (v p°φkladu, kter² je na CD, je vÜe vid∞t). 7.4 P°φkladI tentokrßt jsem pro vßs p°ipravil p°φklad, kter² vyu₧φvß znalosti z dneÜnφ lekce. Op∞t se jednß o upraven² projekt z minulΘ lekce. Za prvΘ musφte vlo₧it novou t°φdu CSprite a upravit ji tak, jak je ukßzßno v²Üe. Dßle musφte n∞kde vytvo°it objekty t°φdy CSprite. NejlΘpe ud∞lßte, kdy₧ vytvo°φte sprity p°φmo v objektu CControl. V p°φkladu vidφte dva ukßzkovΘ sprity. Jeden je statick² a druh² animovan². Dßle musφte tyto dva sprity inicializovat ve funkci DDInit(). Pak staΦφ volat z funkce UpdateFrame() ΦlenskΘ funkce CSprite, tak aby se sprite vykresloval p°φpadn∞ pohyboval nebo m∞nil animaΦnφ fßze. To je vÜe. Kdy₧ zkompilujete projekt p°ilo₧en² na CD, uvidφte dva sprity, z nich₧ jeden je statick², ale pohybuje se a druh² je animovan², ale stojφ na stßle stejnΘm mφst∞. P°φklad m∙₧ete stßhnout v sekci Downloads. 7.5 Zßv∞rProblematika sprit∙ je velice rozsßhlß. Je mo₧no vytvo°it velice slo₧itou strukturu na sob∞ zßvisl²ch t°φd, kterΘ dohromady tvo°φ engine. Na ukßzku vßm ukß₧u jednoduch² obrßzek, na kterΘm je vid∞t podobnß struktura (velice zjednoduÜenß) ze hry Age of Empires 2. Vidφte, ₧e se zde hojn∞ vyu₧φvß d∞diΦnosti a polyformismu jazyka C++, co₧ je dobrΘ si uv∞domit a zamyslet se nad tφm, kterΘ znaky jednotliv²ch sprit∙ jsou podobnΘ a generalizovat tyto znaky do zßkladnφ t°φdy (BaseObject). Dßle vytvo°φte t°φdu statick²ch sprit∙ (podobn∞ jako jsme to d∞lali v tΘto lekci), kterß je odvozenß od zßkladnφ t°φdy (StaticObject). Dßle chceme pohybujφcφ se objekty (nap°. jednotky). To zajiÜ¥uje t°φda MovingObject. Nakonec tu mßme t°φdu MissileObject, kter² vytvß°φ sprity munice (Üφpy). VÜimn∞te si virtußlnφ funkce update(). Na ·pln² zßv∞r vßm prozradφm, co nßs Φekß v p°φÜtφ lekci. Jsme prakticky na konci kurzu DirectDraw. V p°φÜtφ lekci vßm jeÜt∞ povφm n∞co uvol≥ovßnφ objekt∙ DirectDraw a o "ztrßceni povrch∙". A tφm prakticky zakonΦφme DirectDraw. Proto₧e vφm, ₧e problematika je skuteΦn∞ rozsßhlß a ka₧dΘho m∙₧e zajφmat n∞co jinΘho, je pot°eba abyste mi dali v∞d∞t o sv²ch problΘmech. DalÜφ velice d∙le₧itou komponentou DirectX je DirectInput, kterß se rovn∞₧ velmi hodφ pro programovßnφ her a jin²ch multimedißlnφch aplikacφ. Tak₧e dßle budu pokraΦovat prßv∞ komponentou DirectInput, kterß nenφ zdaleka tak slo₧itß jako DirectDraw.
© 2001 Vogel Publishing, design by ET NETERA
|