C/C++ & Visual C++Kurz DirectX (36.) |
|
┌vodem | DatovΘ struktury | Kurz DirectX | Downloads | Otßzky a odpov∞di |
|
V dneÜnφ lekci se op∞t vracφm k DirectX a naÜemu projektu. Velkou Φßst Φlßnku v∞nuji dotazu ohledn∞ vytvß°enφ objekt∙ p°es poΦφtßnφ referencφ. Celou tuto problematiku se pokusφm jeÜt∞ jednou vysv∞tlit na jednoduchΘm p°φkladu. V dalÜφ Φßsti se budeme op∞t zab²vat projektem D3DEngine, kter² jsme postupn∞ vytvß°eli v minul²ch lekcφch. 36.1. Vytvß°enφ objekt∙ pomocφ rozhranφV projektu D3DEngine jsem zavedl vytvß°enφ objekt∙ p°es jejich rozhranφ, z klienta tedy nikdy nepracujeme s objektem p°φmo, ale v₧dy jen s rozhranφm objektu. Rozhranφ je v podstat∞ taky t°φda (v jin²ch programovacφch jazycφch je pro rozhranφ vyhrazen zcela jin² typ). Tato t°φda se ale vyznaΦuje n∞kolika zvlßÜtnostmi, kterΘ je pot°eba dodr₧et, abychom tuto t°φdu prohlßsit za rozhranφ jinΘ t°φdy. Tφmto jsem zßrove≥ popsal hrubΘ principy technologie COM (Component object model), ze kterΘho je tento nßÜ model odvozen (ale samoz°ejm∞ je zjednoduÜen pro naÜe pot°eby). Nynφ si ukß₧eme popsanΘ principy na tom nejjednoduÜφm principu, kter² pak p°evedeme do projektu D3DEngine. T°φda CSpaceship: public: public: T°φda CUnique: class CUnique : public IUnique public: public: public: /* public: /* public: Rozhranφ obsahujφ pouze Φist∞ virtußlnφ metody (stejnΘ jako jejich t°φdy, to ale samoz°ejm∞ neznamenß, ₧e by t°φdy nemohly obsahovat i jinΘ metody, ale klient bude moci volat pouze metody rozhranφ). Stejn∞ tak nemß smysl, aby rozhranφ m∞lo n∞jakΘ soukromΘ metody. UrΦit∞ vßs te∩ zajφmß jak budou vypadat metody AddRef() a Release(). U₧ jsme se s nimi setkali mnohokrßt, ale pro ·plnost, tady jsou: HRESULT CSpaceship::AddRef() HRESULT CSpaceship::Release() Proto₧e jsou u obou t°φd toto₧nΘ, nebudu zde uvßd∞t dvakrßt tent²₧ k≤d a zam∞°φm se na d∙le₧it∞jÜφ v∞ci. O tom, jak metody pracujφ jsem se zmφnil ji₧ v ·vodu, zde to mßte prakticky. Metoda AddRef() vracφ inkrementovan² poΦet referencφ na objekt (nezapome≥te prom∞nnou m_ulRef nulovat v konstruktoru t°φdy). Metoda Release() je o n∞co mßlo slo₧it∞jÜφ. Pokud je poΦet referencφ v∞tÜφ ne₧ 0, znamenß to, ₧e klient uvol≥uje rozhranφ a proto je t°eba ΦφtaΦ snφ₧it. Pokud existuje dalÜφ rozhranφ na tent²₧ objekt, funkce vrßtφ snφ₧en² poΦet referencφ, ale objekt z∙stane v pam∞ti (stßle je ₧ßdßn). Naopak v p°φpad∞, ₧e poΦet referencφ klesne na nulovou hodnotu, znamenß to, ₧e o objekt u₧ nikdo nestojφ a proto se sßm zniΦφ. Poznßmka: Typ HRESULT je op∞t p°evzat z COMu, ale je to prost² unsigned long. Mßme t°φdy, mßme rozhranφ, co dßl? Nynφ pot°ebujeme n∞jakou funkci (opravdu funkci, nikoliv metodu), pomocφ kterΘ zφskßme po₧adovanΘ rozhranφ! Je to asi nejd∙le₧it∞jÜφ v∞c celΘho modelu. V projektu D3DEngine se tyto funkce jmenovaly t°eba CreateDisplayObject(). Zde nenφ ₧ßdnΘ Display, proto uvedenou funkci nazvu prost∞ CreateObject(), ale bude fungovat ·pln∞ stejn∞. Deklarace a definice tΘto funkce je v souborech Manager.h a Manager.cpp: /* /* Typ EXIID identifikuje rozhranφ, kterΘ zde mßme - tedy pouze dv∞. Funkce CreateObject() mß dva parametry, prvnφm urΦφme, o kterΘ rozhranφ mßme zßjem, p°es druh² se pak p°edßvß ukazatel rozhranφ. Podφvejme se na implementaci funkce: /* /* HRESULT CreateObject(EXIID InterfaceID, void ** ppv) Za prvΘ je t°eba vlo₧it hlaviΦkovΘ soubory vÜech t°φd, ke kter²m mßme rozhranφ. Na prvnφm °ßdku funkce jsou ukazatele na unikßtnφ objekty (samoz°ejm∞ na jejich rozhranφ). Tento ukazatel se p°i prvnφm volßnφ vytvo°φ, ale p°i dalÜφch volßnφ se vracφ po°ßd prvnφ (tφm je zp∙sobena jeho unikßtnost). Hlavnφ Φßst funkce je p°φkaz switch, kde se podle ID rozhranφ rozhodujeme, kter² objekt vytvo°φme. Proto₧e t°φdy jsou odvozeny od sv²ch rozhranφ m∙₧eme napsat toto: IUnique unq = new CUnique; Odborn∞ se tato technika naz²vß polymorfismus, ale zßrove≥ pat°φ mezi zßkladnφ techniky objektovΘho programovßnφ, Φili to tu rozebφrat nebudu. NicmΘn∞ tφmto zp∙sobφme, ₧e aΦkoliv unq je typu IUnique, budeme volat metody naÜφ t°φdy CUnique. Na konci ka₧dΘ v∞tve nesmφme zapomenout zavolat metodu AddRef(). Nynφ u₧ zb²vß cel² systΘm vyzkouÜet na krßtkΘm progrßmku ve funkci main(): IUnique *theONE, *theSecond; CreateObject(EXIID_IUnique, (void**) &theONE); printf("Druhe rozhrani vypisuje zpravu:\n"); printf(" - Zprava je porad stejna.\n\n"); theONE->Release(); V prvnφ Φßsti otestujeme unikßtnost t°φdy CUnique. Vytvo°φme prvnφ rozhranφ a nechßme si vypsat jeho adresu. Nastavφme n∞jak² °et∞zec a nechßme ho vypsat (zatφm pouze prvnφm rozhranφm). Nynφ vytvo°φme druhΘ rozhranφ stejnΘho typu a op∞t nechßme vypsat na obrazovku! Ukazatele jsou stejnΘ! Zavolßme metodu Message() druhΘho rozhranφ a "neΦekan∞" se vypφÜe zprßva prvnφho rozhranφ, tak₧e opravdu pracujeme po°ßd se stejn²m objektem! Nakonec ob∞ rozhranφ uvolnφme, Φφm₧ zruÜφme i objekt CUnique. Nynφ se podφvejme na test druhΘ rozhranφ ISpaceship: ISpaceship *ship1, *ship2; ship1->Init(5); ship2->Init(3); ship1->Move(11); printf("\n"); Doufßm, ₧e Vßm toto malΘ vysv∞tlenφ staΦilo. SystΘm v naÜem velkΘ projektu je na puntφk stejn², jen je vÜe ve v∞tÜφm m∞°φtku. 36.2. Reorganizace projektu D3DEngineProblΘm naÜeho projektu tkvφ v tom, ₧e t°φda CTerrain je souΦßst projektu Tester. Proto vytvo°φme dalÜφ knihovnu Engine, kterou vlo₧φme mezi Tester a Display. Engine bude vyu₧φvat Display a Input a nakonec Tester bude testovat vÜechny uvedenΘ knihovny. Tento ·kon se zdß na prvnφ pohled jednoduch², ale v novΘ knihovn∞ musφme vytvo°it Manager objekt∙ a hlavn∞ musφme vytvo°it rozhranφ t°φdy CTerrain. Nejprve tedy vlo₧φme nov² projekt Engine! Vytvo°te Windows DLL. Do vytvo°enΘho adresß°e Engine zkopφrujte soubory z vedlejÜφho projektu (t°eba Input): Common.h, Manager.h, Manager.cpp a Interfaces.h. Nakonec jeÜt∞ p°ekopφrujte soubory Terrain.h a Terrain.cpp z projektu Tester. VÜechny uvedenΘ soubory do projektu Engine p°idejte t°eba p°es kontextovΘ menu (polo₧ka Add Existing Item). Nynφ budeme soubory postupn∞ upravovat. Common.h: /* #include "interfaces.h" Manager.h: enum ENGIID // Call This function to get desired interface Manager.cpp: #include "StdAfx.h" #include "Interfaces.h" #include "../Common/Common.h" #include "Manager.h" // Call This function to get desired interface V tomto souboru bude implementace funkce CreateEngineObject(). Zatφm mßme pouze jedno rozhranφ ITerrain (za chvilku ho nadefinujeme) a objekt skr²vajφcφ se za tφmto rozhranφm bude navφc unikßtnφ. Jak tato funkce pracuje jsem probral o pßr °ßdk∙ v²Üe. Interfaces.h: enum TERRAIN_METHOD class ENGINE_API ITerrain virtual void SetFlag(DWORD dwFlag) = 0; virtual HRESULT Render() = 0; public: A je tu poslednφ p°idan² soubor (tedy zcela nov² soubor). Zde nadefinujeme rozhranφITerrain nßle₧ejφcφ t°φd∞ CTerrain. Vlo₧φme do n∞j vÜechny metody CTerrain, kterΘ jsou ve°ejnΘ a p°idßme dv∞ metody AddRef() a Release(), kterΘ pat°φ do ka₧dΘho rozhranφ. VÜechny metody budou Φist∞ virtußlnφ. Na ·pln² zßv∞r musφme jeÜt∞ trochu upravit t°φdu CTerrain v souborech Terrain.h a Terrain.cpp. Za prvΘ je t°eba odvodit CTerrain od ITerrain: class CTerrain : public ITerrain ... ... public: CTerrain(void); P°idßme atribut m_dwRef (ΦφtaΦ na poΦφtßnφ referencφ) a dv∞ metody rozhranφ. Do souboru Terrain.cpp p°idßme implementaci metod AddRef() a Release(): HRESULT CTerrain::AddRef() HRESULT CTerrain::Release() Nezapome≥te v konstruktoru vynulovat atribut m_dwRef. Nynφ upravme projekt Tester (soubor Tester2.cpp). Vlo₧φme nov² hlaviΦkov² soubor: #include "../engine/common.h" Z prom∞nnΘ g_theTerrain musφme ud∞lat ukazatel na ITerrain: ITerrain *g_theTerrain = NULL; Dßle vytvo°φme objekt CTerrain s rozhranφm ITerrain: if(S_OK == (dwRet = CreateEngineObject(ENGIID_ITerrain, (void**) &g_theTerrain))) Ve zbytku souboru staΦφ opravit g_theTerrain s teΦkou (.) na g_theTerrain se Üipkou (->). Aby Üel projekt zkompilovat, musφte jeÜt∞ upravit nastavenφ projektu Engine. V²stupnφ soubor (Output file) musφ b²t zm∞n∞n na ../Release/Engine.dll, aby se v²slednß dll knihovna vytvo°ila v adresß°i Release celΘho projektu D3DEngine. Jako dalÜφ knihovny (Additional dependencies), kterΘ projekt bude pot°ebovat musφte nastavit tyto dv∞: ../common/common.lib D3dx8.lib. Nezapome≥te ob∞ nastavenφ ud∞lat i pro Debug. Nynφ zm∞≥te nastavenφ zßvislostφ v projektu D3DEngine tak, aby projekt Engine byl zßvisl² na Display a Input, a projekt Tester na novΘm projektu Engine. Teprve te∩ odstra≥te soubory Terrain.h a Terrain.cpp z projektu Tester. Upraven² projekt si samoz°ejm∞ m∙₧ete stßhnout v sekci Downloads pod nßzvem D3DEngine. 36.3 Zßv∞rTak a je tu konec dneÜnφ lekce. Doufßm, ₧e jsem Vßm op∞t trochu pomohl. P°φÜt∞ se budeme zab²vat Φßsticov²mi systΘmy (particle systems), pomocφ kter²ch se vytvß°φ t°eba mraky nebo kou°. T∞Üφm se p°φÜt∞ nashledanou.
|
|