Kirjautuminen

Haku

Tehtävät

Opasarkisto: Peliohjelmointi Visual Basicilla: Osa 2 - Grafiikka ja pelitasot

  1. Osa 1 - Suunnittelu
  2. Osa 2 - Grafiikka ja pelitasot
  3. Osa 3 - Kokonaisuus ja viimeistely

Kirjoittaja: Antti Laaksonen (2003).

⚠ Huomio! Tämä opas on vanhentunut. Oppaan sisältöön ei voi enää luottaa. Opas on säilytetty vain sen historiallisen arvon vuoksi. ⚠


Jalokivijahdissa kaikki pelin yleiset aliohjelmat ja funktiot ovat moduulissa. Samassa moduulissa on myös muutaman WinAPI-funktion, vakioiden sekä globaalien muuttujien määrittelyt. Globaali muuttuja on näkyvissä kaikissa ohjelman moduuleissa ja formeissa. Tällaiset osat kannattaa koota moduuliin erityisesti silloin, kun pelissä on mukana useampia formeja.

Tavallisesti ohjelman suoritus alkaa formista, mutta se voi yhtä hyvin alkaa myös moduulissa olevasta Main-aliohjelma. Tällöin Project->Properties-valikosta avautuvasta ikkunasta on valittava Startup Objectiksi Sub Main. Jalokivijahdissa Main-aliohjelma näyttää seuraavalta:

Sub Main()
   'näytetään peli-ikkuna
   jakija.Show
   'annetaan hetki aikaa järjestelmän tapahtumille, jotta
   'äskeinen ikkuna todella ilmestyy näkyviin
   DoEvents
   'jos ohjelman hakemistosta löytyy kuvat.bmp-tiedosto...
   If TiedostoOlemassa(App.Path & "\kuvat.bmp") Then
       '...ladataan se peli-ikkunan näkymättömään kuvakehykseen
       jakija.pKuvat.Picture = LoadPicture(App.Path & "\kuvat.bmp")
   Else
       '...muussa tapauksessa näytetään virheilmoitus...
       MsgBox "Tiedostoa 'kuvat.bmp' ei löydy pelin hakemistosta!"
       '...ja suljetaan ohjelma heti alkuunsa
       End
   End If
   'aloitetaan uusi peli
   UusiPeli
End Sub

Kun peli-ikkuna on avattu, sen näkymättömään kuvakehykseen ladataan ohjelman kanssa samassa hakemistossa oleva BMP-kuva, jossa on kaikki pelissä käytettävät palikat. pKuvat-kuvakehyksen AutoRedraw-ominaisuuden arvo täytyy olla True, jotta kuva pysyy tallessa formin alla. Pelin hakemisto on tietystikin eri jokaisella pelaajalla, minkä vuoksi se täytyy hakea App.Path-muuttujasta. TiedostoOlemassa on funktio, joka on tosi, jos parametrina annettu tiedosto on olemassa.

Function TiedostoOlemassa(nimi As String) As Boolean
   'Dir-funktion varsinainen käyttötarkoitus on hakemiston
   'tiedostojen läpikäyminen, mutta samalla Dir(tiedosto) on
   'tyhjä merkkijono, jos tiedostoa ei ole olemassa
   If Dir(nimi) <> "" Then
       TiedostoOlemassa = True
   Else
       TiedostoOlemassa = False
   End If
End Function

Dir-funktion varsinaisesta käyttötarkoituksesta on lisää tietoa hakemistossa.

Kuvien näyttäminen

Kuvakehystä (PictureBox) tai kuvaa (Image) ei kannata koskaan käyttää liikkuvan kuvan näyttämiseen, koska ne ovat hitaita ja vilkkuvat ja välkkyvät. WinApin BitBlt-funktio on sen sijaan hyvä valinta tähän tarkoitukseen. BitBlt kopioi kuvan tai osan kuvasta toiseen kuvaan. Funktio ja siihen liittyvä vakio täytyy määritellä moduulin alussa:

Public Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long

Public Const SRCCOPY = &HCC0020

On myös kätevää määritellä joukko vakioita, jotka kuvaavat palikoiden kuvien järjestystä tiedostossa. Vakioiden ansiosta vaikeasti muistettavilla numerotunnuksilla on nyt helposti muistettavat kirjaintunnukset.

Public Const KAYTAVA = 0
Public Const AVAINP = 1, AVAINS = 2, AVAINV = 3, AVAINK = 4
Public Const LUKKOP = 5, LUKKOS = 6, LUKKOV = 7, LUKKOK = 8
Public Const SEINA = 9
Public Const PALLO = 10
Public Const LOPPU = 11
Public Const JALOKIVI = 12

Kuvan piirtävästä aliohjelmasta on tehty helppokäyttöinen. Parametrit ovat kuvan tunnus (joku ylläolevista vakioista), pelitason sarake ja pelitason rivi. Esimerkiksi seuraavalla komennolla voidaan piirtää jalokiven kuva kolmannen rivin viidennen sarakkeen kohdalle:

PiirraKuva JALOKIVI, 5, 3

Aliohjelma itsessään on tässä. Jokaisella formilla ja kuvakehyksellä on oma kahva eli hDC. Kahvojen avulla Windowsin grafiikkafunktiot tietävät, minne kuva täytyy piirtää tai mistä se täytyy kopioida. Visual Basicissa kahva on luettavissa oleva ominaisuus. Palikka siis kopioidaan näkymättömästä kuvakehyksestä (pKuvat) pelitasolle (pTaso). Piirtokohdan laskeminen on kuvattu koodissa.

Sub PiirraKuva(tunnus As Integer, x As Integer, y As Integer)
   'tähän muuttujaan tallennetaan funktion palautusarvo,
   'jolla ei kylläkään tee tässä tapauksessa mitään
   Dim a As Long
   'BitBlt-funktion parametrit:
   '  o jakija.pTaso.hDC on kohteena olevan kuvakehyksen kahva
   '  o x * 32 ja y * 32 määrittävät palikan piirtokohdan, joka
   '    riippuu annetuista parametreista ja palikan koosta
   '  o palikan leveys ja korkeus ovat 32
   '  o jakija.pKuvat.hDC on sen kuvakehyksen kahva, josta
   '    piirrettävä kuva kopioidaan
   '  o tunnus * 32 + tunnus laskee kopioitavan palan sijainnin
   '    kuvassa ottaen huomioon palikoiden välillä olevan
   '    yhden pikselin levyisen suikaleen
   '  o y-koordinaatti on aina 0, mikä tarkoittaa kuvan ylälaitaa
   '  o SRCCOPY merkitsee tavallista kopiointia
   a = BitBlt(jakija.pTaso.hDC, x * 32, y * 32, 32, 32, jakija.pKuvat.hDC, tunnus * 32 + tunnus, 0, SRCCOPY)
End Sub

Tällaisten aliohjelmien käyttäminen selkeyttää koodia huimasti.

Taulukoita ja muuttujia

Seuraavaksi lisätään moduulin alkuun pari pelin kulkuun liittyvää taulukkoa. Kaksiulotteiseen Taso-taulukkoon tallennetaan tieto jokaisesta pelitason palikasta (edelleen yksi yllä määritellyistä vakioista). Koska taulukon indeksointi aloitetaan nollasta, sen koko on 10 x 10. Avaimet-taulukkoon taas tallennetaan punaisten, sinisten, keltaisten ja vihreiden avainten lukumäärä. Taulukko indeksoidaan vakioiden avulla.

'10x10-taulukko, joka sisältää tiedon jokaisesta pelitason palikasta
Public Taso(9, 9) As Integer
'taulukko, jossa on kunkin väristen avaimien lukumäärä
Public Avaimet(AVAINP To AVAINK) As Integer

Tämän lisäksi määritellään joukko muuttujia: Jalokivet kuvaa pelitasolla jäljellä olevia jalokiviä. Kun puuttuvien jalokivien määrä on nolla, pallo saa jatkaa seuraavalle tasolle. NTaso on senhetkinen pelitason numero. Aika on jäljellä oleva aika sekunteina. Jos aika loppuu, peli päättyy. Kaynnissa on joko True (tosi) tai False (epätosi). PalloX ja PalloY ovat pallon koordinaatit.

'puuttuvien jalokivien määrä
Public Jalokivet As Integer
'tämänhetkinen pelitaso
Public NTaso As Integer
'jäljellä oleva aika
Public Aika As Integer
'tosi, jos peli on käynnissä
Public Kaynnissa As Boolean
'pallon x- ja y-koordinaatti
Public PalloX As Integer, PalloY As Integer

Main-aliohjelman viimeisellä rivillä kutsuttiin UusiPeli-aliohjelmaa, joka näyttää seuraavalta:

Sub UusiPeli()
   'ladataan ensimmäinen taso
   LataaTaso 1
   'piirretään palikat ikkunaan
   PiirraTaso
   'näytetään ylälaidassa olevat tiedot
   NaytaTiedot
   'peli on nyt käynnissä
   Kaynnissa = True
End Sub

Nyt on aika ottaa selville, miten pelitasot tallennetaan tiedostoon, luetaan sieltä taulukkoon ja lopuksi näytetään peli-ikkunassa.

Pelitason lukeminen

Pelitasot on tallennettu erillisiin tiedostoihin, jotka ovat muotoa [tason numero].tas. Tiedoston päätteellä ei ole mitään väliä, mutta kannattaa valita sellainen pääte, joka ei sekoitu johonkin yleiseen tiedostotyyppiin. Pelitasot on tallennettu tekstimuodossa, jolloin niitä on helppo muokata Windowsin Notepadilla tai millä tahansa muulla tekstieditorilla. Tässä on pelin toinen taso, tiedosto 2.tas:

##########
#    p  ##
# ##### ##
#l##### ##
####### ##
###     ##
### ###P##
### ### ##
### ### ##
a   ###j##

Risuaidat kuvaavat seiniä ja välilyönnit käytävää. a on aloituspaikka (pallo) ja l on lopetuspaikka (l). Avaimet merkitään pienillä kirjaimilla ja lukot isoilla kirjaimella. p on siis punainen avain ja P punainen lukko. Tämä taso tulee näyttämään pelissä seuraavalta:

Tällaista tiedostoa voi lukea Visual Basicissa Input-tilassa. Rivin lukemiseen on käytettävä nimenomaan Line Input -lausetta Inputin sijaan, koska muuten rivien alussa olevat välilyönnit aiheuttaisivat ongelmia. Paras tapa muuttaa tiedostossa käytetyt kirjaintunnukset ohjelman käyttämään numerointiin on muodostaa merkkijono, jossa kirjaimet on lueteltu samassa järjestyksessä kuin niitä vastaavat palikat ovat kuvatiedostossa. Tämän jälkeen kirjainta vastaavan tunnuksen selvittäminen onnistuu helposti InStr-funktiolla ja se tallennetaan Taso-taulukkoon.

Jos luettu palikka on jalokivi, puuttuvien jalokivien määrää kasvatetaan yhdellä. Jos taas palikka on pallo, asetetaan sen aloituskoordinaatit muuttujiin PalloX ja PalloY. Tämän lisäksi aliohjelmassa vaihdetaan kunkin avaimen määräksi nolla, lasketaan pelaajan käytössä oleva aika tason numeron perusteella ja käynnistetään sekunnin välein reagoiva ajastin.

Tässä on koko pitkä LataaTaso-aliohjelma, jonka parametrina on ladattavan tason numero:

Sub LataaTaso(num As Integer)
   'silmukassa käytettävät muuttujat
   Dim i As Integer, j As Integer
   'luettu rivi ja palikoita vastaavat merkit tiedostossa
   Dim rivi As String, merkit As String

   'tässä muuttujassa on tiedostossa käytetyt palikoiden
   'merkit samassa järjestyksessä, kuin palikat ovat kuvassa
   merkit = " psvkPSVK#alj"
   'kerättävien jalokivien määrä on aluksi nolla
   Jalokivet = 0

   'avataan pelitason sisältävä tiedosto lukemista varten
   Open App.Path & "\" & num & ".tas" For Input As #1
   'luetaan tiedostosta yhdeksän riviä
   For i = 0 To 9
       'kukin rivi luetaan vuorollaan rivi-muuttujaan
       Line Input #1, rivi
       'luetaan riviltä yhdeksän merkkiä
       For j = 0 To 9
           'Mid-funktio palauttaa merkkijonon yksittäisen merkin;
           'merkin paikka merkit-muuttujassa, joka taas saadaan
           'selville Instr-funktiolla, kertoo suoraan palikan
           'sijainnin kuvatiedostossa ja näin ollen sen tunnuksen
           Taso(j, i) = InStr(merkit, Mid(rivi, j + 1, 1)) - 1
           'jos luettu palikka on jalokivi...
           If Taso(j, i) = JALOKIVI Then
               '...kasvatetaan kerättävien jalokivien määrää yhdellä
               Jalokivet = Jalokivet + 1
           'jos luettu palikka on pallo...
           ElseIf Taso(j, i) = PALLO Then
               '...asetetaan sen aloituskoordinaatit
               PalloX = j
               PalloY = i
           End If
       Next
   Next
   'suljetaan tiedosto, kun kaikki tiedot on luettu
   Close #1

   'tyhjennetään taulukko, koska avaimia ei ole alussa laisinkaan
   For i = AVAINP To AVAINK
       Avaimet(i) = 0
   Next

   'tallennetaan ladatun tason numero muuttujaan
   NTaso = num
   'lasketaan pelaajalle annettava aika aloitusarvon
   'ja tason numeron perusteella (1. taso 30 s,
   '2. taso 40 s, 3. taso 50 s jne.)
   Aika = 20 + NTaso * 10
   'käynnistetään ajan vähennyksestä huolehtiva ajastin
   '(1000 ms = 1 s)
   jakija.Timer1.Interval = 1000
End Sub

Pelitason piirtäminen

Pelitason piirtäminen on helppoa, kun se on kerran luettu. Aluksi piirretään pelitason reunat yhdellä For-silmukalla. Sen jälkeen piirretään kaikki muut palikat, jotka löytyvät nyt siis Taso-taulukosta.

Sub PiirraTaso()
   'silmukoiden käyttämät muuttujat
   Dim i As Integer, j As Integer

   'piirretään pelitason reunoilla olevat kiinteät seinäpalikat
   For i = 0 To 11
       'yläseinä
       PiirraKuva SEINA, i, 0
       'alaseinä
       PiirraKuva SEINA, i, 11
       'vasen seinä
       PiirraKuva SEINA, 0, i
       'oikea seinä
       PiirraKuva SEINA, 11, i
   Next

   'piirretään muut palikat
   For i = 0 To 9
       For j = 0 To 9
           'seinien vuoksi piirtokohtaan lisätään 1
           PiirraKuva Taso(i, j), i + 1, j + 1
       Next
   Next
End Sub

Esimerkiksi kuvakehyksellä olevaan AutoRedraw-ominaisuuteen kannattaa kiinnittää erityistä huomiota. Jos ohjelmaikkunan päälle tulee muita ikkunoita, kuvakehykseen piirretty kuva ei automaattisesti pysy tallessa. AutoRedraw-ominaisuuden ollessa True Visual Basic huolehtii itse kuvan tallentamisesta ja palauttamisesta. Kuitenkaan AutoRedraw-ominaisuutta ei voi käyttää "ulkopuolisen" BitBlt-funktion kanssa. Siispä kuvan päivittämisestä tarvittaessa täytyy huolehtia itse. Tämä tapahtuu kuvakehyksen Paint-tapahtuma-aliohjelmassa:

Private Sub Form_Paint()
   'annetaan hetki aikaa järjestelmälle...
   DoEvents
   '...ja piirretään koko pelitaso uudestaan
   PiirraTaso
End Sub

Tietojen näyttäminen

Peli-ikkunan ylälaidassa näkyvät tason numero, puuttuvien jalokivien määrä sekä jäljellä oleva aika. Näiden tietojen päivittäminen tapahtuu NaytaTiedot-aliohjelman avulla:

Sub NaytaTiedot()
   'NTaso on pelitaso, Jalokivet puuttuvien jalokivien määrä
   'ja Aika jäljellä oleva aika; Space(43) vastaa 43 välilyöntiä
   jakija.lTiedot.Caption = NTaso & Space(43) & Jalokivet & Space(43) & Aika
End Sub

Ohjelmointivinkkejä: Muuttujat

Muuttujat, aliohjelmat ja funktiot kannattaa nimetä huolella. Näin ohjelman kulusta saa hyvän kuvan ilman kommenttejakin, ja kunnolliset nimet helpottavat itse ohjelmointiakin. Suomenkielisten nimien käyttäminen on muuten erinomainen idea.

Jokaisen formin ja moduulin alkuun on hyvä kirjoittaa Option Explicit. Määritys tarkoittaa sitä, että kaikki muuttujat on määriteltävä ennen niiden käyttöä. Tämä saattaa kuulostaa vaivalloiselta, jos niin ei ole ennen tehnyt, mutta vaiva kannattaa: kirjoitusvirheet muuttujien nimissä eivät enää aiheuta kummallisia virheitä ohjelmassa. Variant-tietotyyppiä – joka on määrittelemättömien muuttujien oletustyyppi – kannattaa välttää viimeiseen saakka. Tällaiset muuttujat vievät paljon tilaa muistissa ja niiden käsittely on hidasta. Muuttujan tyyppi täytyy valita aina käyttötarkoituksen mukaan.

Muuttujat, jotka on määritelty moduulissa Public- tai Global-avainsanan avulla, ovat käytössä kaikissa ohjelman formeissa ja moduuleissa. Muuttujat, jotka on määritelty Private- tai Dim-avainsanan avulla ovat käytössä ainoastaan samassa formissa tai moduulissa. Sama pätee WinAPI-määrittelyihin, vakioihin, aliohjelmiin ja funktioihin. Jos aliohjelman tai funktion sisällä määrittelee muuttujan Static-avainsanalla, muuttujan arvo säilyy kutsukertojen välillä.


Kommentit

Sami M [11.03.2005 19:49:16]

#

lainaus:

Public Const SRCCOPY = &HCC0020x

Se väittää tuota "Const" -sanaa vääräksi. Miten tuonkin ongelman pystyy ratkaisemaan?

VBSamppa [11.04.2006 18:14:29]

#

jaa empä tiiä... kandies ehkä koittaa pelin tekoo cool basicilla ja kartat tilesterillä. (tilester tulee mukana)

moptim [27.08.2006 11:14:22]

#

sami m, mikä vb sulla on? kai nyt vakiot pitäisi olla mahdollisia...

Dude [08.09.2007 21:23:57]

#

pitää vääntää qbeelle UGLlän kans

Kirjoita kommentti

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.

Muista lukea kirjoitusohjeet.
Tietoa sivustosta