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 FunctionFunktiota 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 SubMuista, 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 SubNyt 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 FunctionJä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.