Kirjoittaja: Blaze (2003).
⚠ Huomio! Tämä opas on vanhentunut. Oppaan sisältöön ei voi enää luottaa. Opas on säilytetty vain sen historiallisen arvon vuoksi. ⚠
Tämä opas soveltuu sekä DirectX7 -veteraaneille, että uusille DirectX -ohjelmoijille. Visual Basicin hallitsemista vähän Label1.Caption = "Hello World!" -luokkaa kattavammin suositellaan.
DirectX8 yhdisti DirectDraw:n ja Direct3D:n DirectXGraphics nimiseksi komponentiksi. Tässä uudessa mallissa kaikki piirto tehdään Direct3D:n avulla, DirectX8 siirsi näin painon 3D -ohjelmointiin. Esimerkiksi SDK:ssa ei puhuta halaistua sanaa 2D:stä. Tämä ei kuitenkaan tarkoita, että 2D -ohjelmointi DirectX8:aa käyttäen olisi mahdotonta tai jotenkin erityisen vaikeaa. 2D -ohjelmoinnissa alkuun pääseminen voi kuitenkin olla hankalampaa, kuin DirectX7:n kanssa, sillä ohjeita on hankala löytää kun muutoin ah niin loistavan DirectX4VB:nkin tutoriaali aiheesta on kiltisti sanottuna surkea. Jos olet kyseistä opasta lukenut unohda samantien kaikki lukemasi ja teeskentele, että et ole siitä koskaan kuullutkaan. Jos tavoitteena on vain näyttää bittikarttoja ruudulla ei todellakaan tarvitse säätää joidenkin verteksien kanssa.
Miksi sitten käyttää DirectX8:aa, jos sama hoituu selkeämmin seiskaversiolla? Kahdeksikolla kaikki Direct3D:n ominaisuudet ovat suoraan käytettävissä, jos niitä haluaa käyttää vaikkapa joihinkin erikoisefekteihin ja se tarjoaa myös erittäin nopean laitteistopohjaisen alfablendauksen (onko sille suomenkielistä termiä?), cooliuskerrointa unohtamatta. Alfablendaus yksin oli minulle tarpeeksi suuri syy vaihtaa versiota.
Jotta yleensä voit käyttää DirectX:ää ohjelmassasi, pitää projektiisi lisätä referenssi DirectX tyyppikirjastoon. Tämä tapahtuu valitsemalla Project -valikosta References ja etsimällä esiinpomppavasta listasta “DirectX 8 for Visual Basic Type Libraryö ja hyväksymällä valinta.
DirectX:ää aikaisemmin ohjelmoinneet muistavat pitkät koodirimpsut, joissa luodaan objekteja jos jonkinnäköisiä. Asia on ennellaan myös kasiversiossa, mutta yksityiskohdat ovat toki muuttuneet, jotta emme vahingossakaan pääsisi hyödyntämään vanhaa kokemusta.
Aluksi määrittele seuraavat globaalit muuttujat:
Dim DX As DirectX8 Dim D3D As Direct3D8 Dim D3DX As D3DX8 Dim D3DDevice As Direct3DDevice8 Dim D3DSprite As D3DXSprite Dim D3DCaps As D3DCAPS8 Dim D3DDispMode As D3DDISPLAYMODE Dim D3DPP As D3DPRESENT_PARAMETERS
Alla yleiskäyttöinen funktio, joka initialisoi Direct3D:n. Funktio ottaa parametrikseen lomakkeen kahvan (hWnd) sekä Boolean -tyyppisen arvon, jolla kerrotaan, aiotaanko ohjelmaa ajaa ikkunassa vai kokonäytössä. Kaiken onnistuessa funktio palauttaa nollan, virhetilanteessa virhenumeron. Funktio on sen verran pitkä, etten ala sitä tässä sen kummemmin selittelemään, lue koodi ja kommentit ajatuksen kanssa läpi, niin sisäistät kyllä alustamisen periaatteet.
Private Function InitialisoiD3D(ByVal hWnd As Long, _ Optional ByVal Ikkunassa As Boolean) As Long Dim DevType As CONST_D3DDEVTYPE 'Käsittelemme virheemme itse On Local Error Resume Next 'Luodaan uusi DirectX8 -objekti Set DX = New DirectX8 'Tarkistetaan, onnistuiko luominen If Err.Number Then 'Palautetaan virhenumero ja lopetetaan InitialisoiD3D = Err.Number Exit Function End If 'Luodaan uusi D3D -objekti Set D3D = DX.Direct3DCreate 'Tarkistetaan jälleen, onnistuiko sen luominen If Err.Number Then InitialisoiD3D = Err.Number Exit Function End If 'Aloitetaan laitteen luominen 'Oletuksena sörkimme laitteistokiihdytettyä laitetta '(HAL = Hardware Acceleration Layer) DevType = D3DDEVTYPE_HAL 'Selvitetään, mihin kivaan näytönohjain kykenee 'Jätämme toissijaiset adapterit toistaiseksi rauhaan D3D.GetDeviceCaps D3DADAPTER_DEFAULT, DevType, D3DCaps 'Jos virheitä ilmeni, näytönohjain ja/tai sen ajuri ei tue vähintään 'DX7:ää If Err.Number Then Err.Clear 'Koitetaan tutkiskella referenssilaitetta HAL:n sijasta DevType = D3DDEVTYPE_REF D3D.GetDeviceCaps D3DADAPTER_DEFAULT, DevType, D3DCaps If Err.Number Then 'Jos GetDeviceCaps yhä palauttaa virheen, ei 'laitteisto tue DirectX:ää ja ohjelman suorittaminen 'ei voi jatkua 'Palautetaan virhenumero ja poistutaan funktiosta InitialisoiD3D = Err.Number Exit Function End If End If 'Tiedot on saatu, aloitetaan näyttötilan konfigurointi If Ikkunassa Then 'Hankitaan tietoa tämänhetkisestä näyttötilasta D3D.GetAdapterDisplayMode D3DADAPTER_DEFAULT, D3DDispMode 'Lopetetaan, jos käytössä on 256 värin näyttötila If D3DDispMode.Format = D3DFMT_P8 Or D3DDispMode.Format = _ D3DFMT_A8P8 Then InitialisoiD3D = D3DERR_INVALIDDEVICE Exit Function End If With D3DPP 'Kerrotaan, että ohjelmaa ajetaan ikkunassa .Windowed = 1 'Asetetaan takapuskurin muoto samaksi, kuin 'näyttöpuskurin .BackBufferFormat = D3DDispMode.Format 'Asetetaan takapuskurin koko 'Koko on toki vapaasti valittavissa, esimerkissä 'käytämme 640x480 kokoista ikkunaa .BackBufferWidth = 640 .BackBufferHeight = 480 'Kommentoi pois jälkimmäinen rivi,jos et halua VSynciä .SwapEffect = D3DSWAPEFFECT_COPY_VSYNC '.SwapEffect = D3DSWAPEFFECT_DISCARD End With Else With D3DDispMode 'Asetetaan näyttötilan koko .Width = 640 .Height = 480 'Asetetaan näyttötilan värisyvyys '32-bittinen tila kahdeksan bitin alfakanavalla .Format = D3DFMT_A8R8G8B8 'Muita hyviä vaihtoehtoja ovat esim. 'Sama ilman alfakanavaa '.Format = D3DFMT_R8G8B8 '16-bittinen tila kahdeksan bitin alfalla '.Format = D3DFMT_A8R3G3B2 '16-bittinen tila ilman alfaa '.Format = D3DFMT_R5G6B5 'jne. End With With D3DPP 'Kerrotaan, että ohjelmaa ajetaan kokonäytössä .Windowed = 0 'Asetetaan takapuskurin muoto samaksi, kuin 'näyttöpuskurin .BackBufferFormat = D3DDispMode.Format 'Asetetaan takapuskurin koko .BackBufferWidth = D3DDispMode.Width .BackBufferHeight = D3DDispMode.Height 'Takapuskureiden lukumäärä '1 = tuplapuskurointi, 2 = triplapuskurointi jne. .BackBufferCount = 1 'Kerrotaan, mikä ikkuna kokonäyttöön venytetään .hDeviceWindow = hWnd .SwapEffect = D3DSWAPEFFECT_FLIP End With End If 'Kaikki on asetettu, luodaan laite Set D3DDevice = D3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _ hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, D3DPP) 'Tarkistetaan, onnistuiko laitteen luominen If Err.Number Then Err.Clear 'Koitetaan luoda referenssilaite Set D3DDevice = D3D.CreateDevice(D3DADAPTER_DEFAULT, _ D3DDEVTYPE_REF, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, _ D3DPP) 'Mikäli sekin epäonnistuu, palautetaan virhe ja lopetetaan If Err.Number Then InitialisoiD3D = Err.Number Exit Function End If End If 'Luodaan apukirjasto D3DX Set D3DX = New D3DX8 If Err.Number Then InitialisoiD3D = Err.Number Exit Function End If 'Ja lopuksi vielä Sprite-objekti, joka mahdollistaa (muun muassa) 'helpon bittikarttojen piirtämisen näytölle Set D3DSprite = D3DX.CreateSprite(D3DDevice) If Err.Number Then InitialisoiD3D = Err.Number Exit Function End If End Function
Funktiota kutsutaan päälomakkeen Load -tapahtumasta esim. seuraavaan tapaan:
If InitialisoiD3D(Me.hWnd, True) Then MsgBox "Direct3D:n alustaminen epäonnistui. Ohjelma lopetetaan." End End If
Ohjelman suorittamisen loputtua on hyvä puhdistaa sen jättämät jäljet. DirectX:n tapauksessa tämä tarkoittaa objektien vapauttamista. Objektien vapauttaminen ei ole pakollista, eikä maailma tai edes Windows kaadu, jos sen jättää tekemättä, mutta se kuuluu hyviin ohjelmointitapoihin. Homma hoituu asettamalla kaikkien objektien arvoksi Nothing. Vapauta objektit päinvastaisessa järjestyksessä, kuin mitä loit ne.
Private Sub TerminoiD3D() 'Jos objektien luominen epäonnistui, niiden vapauttaminen saattaa 'aiheuttaa virheen 'Tässä vaiheessa olemme kuitenkin jo lopettamassa ohjelmaa, jolloin 'virheenkäsittelyllä ei ole suurempaa merkitystä On Error Resume Next Set D3DSprite = Nothing Set D3DX = Nothing Set D3DDevice = Nothing Set D3D = Nothing Set DX = Nothing End Sub
Nyt osaat rakentaa ohjelman, joka alustaa DirectX:n, mahdollisesti siirtyy kokonäyttöön resoluutiota muuttaen ja lopettaa itsensä kauniisti. Seuraavaksi rakennamme pääsilmukan. Lisää aluksi yksi uusi globaali muuttuja Dim Kaynnissa As Boolean, joka kertoo ohjelmallemme, milloin on aika lopettaa. Ohjelman rungon tulisi näyttää nyt jokseenkin seuraavalta:
Private Sub Form_Load() Kaynnissa = True With Me 'Normaalisti lomake näyttäytyy vasta, kun Form_Load on 'lopettanut, siinä vaiheessa tämä ohjelma on kuitenkin jo 'lopettanut, joten näytetään lomake nyt .Show 'Asetetaan käytettäväksi yksiköksi pikselit .ScaleMode = 3 'Seuraavaksi muutetaan lomake sopivan kokoiseksi .Width = .ScaleX(640, vbPixels, vbTwips) .Height = .ScaleY(480, vbPixels, vbTwips) End With 'Jos alustaminen epäonnistuu, aloitetaan ohjelman sulkeminen If InitialisoiD3D(Me.hWnd, True) Then MsgBox "Direct3D:n alustaminen epäonnistui." Kaynnissa = False End If LataaTekstuurit 'Pääsilmukka Do While Kaynnissa 'Suoritetaan piirtäminen 'Tässä välissä suoritetaan myös kaikki muu pelimekaniikkaan 'liittyvä, esim. fysiikanmallinnus Piirra 'Annetaan Windowsille aikaa miettiä DoEvents Loop TerminoiD3D End End Sub
Muista, ettet vain kopioi koodia ja aja ohjelmaa vielä, sillä siinä ei ole mitään mekanismia, joka lopettaisi sen.
Ohjelman runko on nyt valmis, on aika laittaa se tekemään jotain. Teemme ohjelman, joka piirtää kuvan näytölle. Aloitamme määrittelemällä tekstuurin. Käytännön ohjelmassa, jossa tekstuureita kertyy enemmän kannattaa tehdä oma aliohjelma, joka tekstuurit lataa. Lataa tekstuurit jossain DirectX:n initialisoinnin ja pääsilmukkaan siirtymisen välillä.
Dim Tekstuuri As Direct3DTexture8 Private Sub LataaTekstuurit() Set Tekstuuri = D3DX.CreateTextureFromFileEx(D3DDevice, App.Path & _ "\tutorial.bmp", D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_UNKNOWN, _ D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_FILTER_NONE, 0, ByVal 0, _ ByVal 0) End Sub
CreateTextureFromFileEx:llä on paljon paremetreja, mutta yllä esitettyjä ei tarvitse muuttaa, ellei välttämättä halua, ne toimivat joka tilanteessa (tai no, tiedostonimeä lukuunottamatta, mutta se lienee itsestäänselvyys). Mielenkiintoisin noista on yhdestoista parametri, ColorKey As Long, jossa voi määrittää värin, joka toimii läpinäkyvänä, samalla tavalla, kuin läpinäkyvissä GIF -kuvissa. Väri annetaan muodossa &HAARRGGBB, jossa AA tarkoittaa alfakanavaa. Tavallisella bmp-kuvalla tämän voi asettaa FF:ksi. SDK:sta voi lukea parametreistä lisää, mikäli asia kiinnostaa.
Seuraavaksi rakennamme Piirra-aliohjelman, jota pääsilmukkamme kutsuu.
Private Sub Piirra() Dim SourceRect As RECT Dim Scaling As D3DVECTOR2 Dim RotationCenter As D3DVECTOR2 Dim Rotation As Single Dim Translation As D3DVECTOR2 Dim Color As Long 'Alustetaan muuttujat 'SourceRect määrittää, minkä osan tekstuurista haluamme piirtää 'Koko tekstuurin voi piirtää antamalle Draw:lle parametrina RECT:n 'sijasta arvon ByVal 0, mutta täytämme RECT:n kuitenkin mallin vuoksi With SourceRect .bottom = 480 .Left = 0 .Right = 640 .Top = 0 End With 'Kuvan kokoa voi muuttaa vaihtamalla Scalingin arvoja 'Nyt haluamme kuvan kuitenkin 1:1 koossa Scaling.X = 1 Scaling.Y = 1 'Kuvaa voi pyörittää seuraavia arvoja muokkaamalla 'Piirrämme kuvan nyt kuitenkin suorassa RotationCenter.X = 0.5 RotationCenter.Y = 0.5 Rotation = 0 'Translation tarkoittaa kuvan paikkaa ruudulla 'Piirrämme sen nyt piirtoalueen vasempaan ylänurkkaan Translation.X = 0 Translation.Y = 0 'Color on Draw -metodin parametreistä mielenkiintoisin, sen avulla 'piirrettyä kuvaa voi sävyttää 'Asettamalla Color:n valkoiseksi kuva toistuu alkuperäisen värisenä 'Ensimmäiset kaksi F:ää ovat jälleen alfakanava, sen arvoa 'pienentämällä kuvasta saa semi-läpinäkyvän 'Eksperimentoi erilaisilla Color:n arvoilla Color = &HFFFFFFFF 'Aloitetaan tyhjentämällä piirtoalue 'Neljäs parametri on väri, jolla tyhjennetty alue täytetään, 'esimerkissämme musta D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET, 0, 1#, 0 'Kaikki piirtäminen pitää tapahtua BeginScenen ja EndScenen välissä D3DDevice.BeginScene D3DSprite.Begin D3DSprite.Draw Tekstuuri, SourceRect, Scaling, RotationCenter, _ Rotation, Translation, Color D3DSprite.End D3DDevice.EndScene 'Kaikki piirto tapahtuu takapuskuriin, nyt vaihdetaan taka- ja 'näyttöpuskurin paikkaa ja tuodaan 'vastapiirretty kuva esille 'DirectX7 -veteraanit tunnistanevat tässä Surface.Flip:n D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0 End Sub
Nyt ohjelmassamme on toimiva renderöintisilmukka ja kunhan lisäämme vielä keinon lopettaa sen, olemme valmiita kokeilemaan ohjelmaa.
Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer) If KeyCode = vbKeyEscape Then Kaynnissa = False End Sub
Nyt voimme lopettaa ohjelman painamalla ESC:ä.
Jos kokeilit ohjelmaa, huomaat sen toimivan, mutta kaatuvan mikäli se menettää fokuksen kokonäyttötilassa, esimerkiksi jos Alt+Tab:aat johonkin toiseen ohjelmaan tai mikäli käyttäjä koettaa sulkea sen rastista ikkunatilassa. Ensimmäinen ongelma hoituu lisäämällä Piirra-aliohjelman alkuun pari tarkistusta ja yhden uuden funktion:
Dim PalautusArvo As Long 'Käsittelemme virheemme jälleen itse On Local Error Resume Next 'Selvitetään, missä tilassa laite on PalautusArvo = D3DDevice.TestCooperativeLevel If PalautusArvo = D3DERR_DEVICELOST Then 'D3DERR_DEVICELOST tarkoittaa, että ohjelma ei ole aktiivinen - 'piirtämistä on turha yrittää Exit Sub ElseIf PalautusArvo = D3DERR_DEVICENOTRESET Then 'DEVICENOTRESET tarkoittaa, että ohjelma on juuri muuttunut 'aktiiviseksi, mutta laitetta ei ole vielä palautettu - palautetaan se PalautusArvo = 0 PalautusArvo = PalautaLaite 'Jos laitteen palauttaminen epäonnistui poistutaan aliohjelmasta If PalautusArvo Then Exit Sub End If 'Poistutaan aliohjelmasta, jos ikkuna on pienennetty If Me.WindowState = vbMinimized Then Exit SubPrivate Function PalautaLaite() As Long On Local Error Resume Next 'Tuhotaan D3D, alustetaan se uudelleen ja lopuksi kutsutaan 'D3DDevice:n Reset -metodia TerminoiD3D InitialisoiD3D Me.hWnd, True D3DDevice.Reset D3DPP If Err.Number Then PalautaLaite = Err.Number Exit Function End If 'Resetointi tuhoaa tekstuurit, ladataan ne uudelleen LataaTekstuurit End Function
Jälkimmäinen hoituu joko poistamalla ruksi käytöstä asettamalla lomakkeen ControlBox-ominaisuus epätodeksi tai kirjoittamalla pikku koodinpätkän, joka laittaa ruksin sulkemaan ohjelman oikealla tavalla:
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) Kaynnissa = False Cancel = 1 End Sub
Osaat nyt alustaa ja tuhota Direct3D -objektin, luoda toimivan pääsilmukan ja piirtää bittikarttoja näytölle. Näillä eväillä saa aikaan jo vaikka keskikokoisen pelin.
Hyödyllisiä linkkejä
Karri Kahelin, 18.11.2003
Jes! Olen jo odottanutkin tälläistä.
Vaikuttaa ihan hyvältä, pitänee lukea vielä ajatuksella läpi...
Tarviikohan tähän jotain kirjastoja. En ainakaan löytänyt VB3:sen project valikosta paljon mitään sellaista.
Ei tämä toimi VB3:lla, sitä itsekin tuskailen.
VB3 on vaan jotain 16 bittistä **skaa :/
DirectX on COM-objekti, joiden käyttämiseen vaaditaan VB5 tai uudempi.
Mitä ton esimerkkiohjelman pitäs tehä? Mulla tulee pelkkä logo näyttöön ja sitten ei tapahdu mitään.
No ei se muuta tee, kuin piirtää sen logon. Jaksanu tuon monimutkasempaa esimerkkiä alkaa kasaamaan.
Mikä VB vaaditaan?
Sori, en lukenu et VB5 vaaditaan!
hyvä opas... pitänee yrittää kopioida tuo VB:hin ja yrittää ymmärtää sitä, ja sitten kun sen on ymmärtänyt...jaa-a, katsotaan onnistuuko.
Mainio opas, kerrottu juuri ne asiat, joita tarvitsee. Ainakin minun koneellani muuten tuo D3DSWAPEFFECT_DISCARD saa aikaan paljon paremman animaation kuin D3DSWAPEFFECT_COPY_VSYNC.
No nyt mulla on VB6, mutta eipä mua enää tämä opas kiinnosta kun tykkään enemmän SDL:stä. :)
Mistähän johtuu, että aina kun yritän ajaa ohjelmaa jossa lukee alussa esim: Dim DX As DX7 niin se herjaa siitä että: Compile error: User-defined type not defined, referensseihin lisäsin sen tässä tapauksessa directx7 jutun.
hyvä opas
eiks tää ollu dx8-opas? lisää referensseihin dx8 type library
Miten voi käyttää resurssikuvaa spritenä?
En löydä dx8 Referencestä... Minulla on VB6.
Voisikohan minun Windows Vista vaikuttaa asiaan??
Sain jo toimimaan, kopioin kaverin XP:stä sen dx8vb.dll...
hyvä opas vaikka en oikein tajunnutkaan
Huomio! Kommentoi tässä ainoastaan tämän oppaan hyviä ja huonoja puolia. Älä kirjoita muita kysymyksiä tähän. Jos koodisi ei toimi tai tarvitset muuten vain apua ohjelmoinnissa, lähetä viesti keskusteluun.