|
|
Projektin loppuraporttiOhjelman kuvausProjektini aiheena on Bejeweled/Zookeeper -pelin uudelleentoteutus. Peli koostuu ruudukosta, jonka jokaisessa ruudussa on eläin. Pelin tarkoituksena on vaihtaa kahden eläimen paikkaa niin, että vaihdosta syntyy vähintään yksi kolmen tai useamman eläimen rivi. Jos vaihdosta muodostuu edellämainitun kaltainen rivi, se poistetaan ruudukosta, ja rivin yläpuolella olleet ruudut putoavat alaspäin, niin että ruudukon yläreunaan syntyy poistuneen rivin kokoinen (ja muotoinen) tyhjien ruutujen alue. Näihin tyhjiin ruutuihin arvotaan uudet eläimet. Jokaisesta rivistä pelaaja saa pisteitä riippuen siitä, monenko eläimen rivi poistui pelistä: mitä enemmän eläimiä, sitä enemmän pisteitä. Jos taas ruutujen vaihdosta ei synny riviä, ei vaihto ole sallittu, ja eläimet vaihdetaan takaisin omille paikoillensa. Pelin alussa tarkistetaan, ettei ruudukossa ole valmiiksi kolmen tai useamman eläimen rivejä. Toisin sanoen peli poistaa valmiita rivejä niin kauan, että ruudukossa on korkeintaan kaksi samaa eläintä vierekkäin/allekkain. Näistä aluksi poistetuista riveistä pelaaja ei luonnollisestikaan saa pisteitä. Ohjelma poistaa myös ne rivit, jotka syntyvät eläinten vaihdosta epäsuorasti siten, että poistuneen rivin johdosta alaspäin pudonneet ruudut muodostavat rivejä. Näistä epäsuorasti syntyneistä riveistä pelaaja saa pisteitä normaalisti. Ohjelma tarkistaa jokaisen syntyneen rivin jälkeen, onko siirtoja vielä mahdollista tehdä, eli voiko kahden eläimen paikkaa vaihtaa niin, että vaihdosta syntyy uusi rivi. Pelissä on kaksi pelimoodia: normaali ja aikavastus. Jos tilanne on normaalia peliä pelattaessa se, ettei siirtoja enää ole jäljellä, peli päättyy ja peli-ikkunan ala-laitaan ilmestyy teksti, joka ilmoittaa siirtojen olevan loppu. Jos taas pelaaja pelaa aikaa vastaan ja siirrot loppuvat ennen kuin aika on loppu, ruudukkoon arvotaan uudet eläimet ja pelaajalle kerrotaan tästä peli-ikkunan alalaidassa olevassa infotekstissä. Kummassakaan pelityypissä eli ole mahdollista "voittaa", vaan tavoitteena on kerätä niin paljon pisteitä kuin mahdollista. Pelin graafinen käyttöliittymä on toteutettu Swingillä. Varsinainen peli-ikkunani pohjautuu JFrameen, ja ikkunan eri näkymät (alkuvalikko, pelipaneeli ja ohjenäkymä) on toteutettu JPanelin ja siitä tekemäni aliluokan (Pelipaneeli) avulla. Alkuvalikon napit ovat JButtoneita, johon myös peliruudukossa olevat eläinnapit pohjautuvat. Pelipaneeli sisältää peliruudukon lisäksi myös JLabeleita, jotka sisältävät infotekstin sekä aika- ja pistenäytöt. Ohjenäkymässä on käytetty JButtonia ja JTextAreaa, joka on sijoitettu JScrollPanen sisään. Valikkorivi ja sen valikot sisältävät JMenu-, JMenuItem-, JMenuBar- ja JMenuCheckBoxItem -komponentteja. Pelin kolme eri näkymää näyttävät tältä: ![]()
Tekninen toteutusProjekti on toteutettu Windows XP -ympäristössä Java 1.5.0:lla. Javan valmiista luokista olen käyttänyt pakkauksista java.awt, java.awt.event ja javax.swing löytyviä luokkia, sekä java.util.Random ja java.util.ArrayList -luokkia. Koodasin projektini aiemmista harjoituksista poiketen Eclipsellä, mikä osoittautui todella hyväksi ratkaisuksi. Niin sanotuista tyhmistä virheistä aiheutuneet ongelmat hävisivät lähes kokonaan, mikä varmasti vähensi koodausaikaa huomattavasti verrattuna XEmacsilla ohjelmointiin. Ainoan miinuksen Eclipselle voi antaa siitä, että projektin siirtäminen toiselle koneelle on todella hankalaa. Oli siis virhe vaihtaa uuteen koneeseen kesken projektin. Ohjelmani koostuu viidestä luokasta ja kahdesta enumeraatiosta. Luokkia ovat Elainpeli, Pelipaneeli, Peliruudukko, Elainruutu ja Elainnappi. Laji ja Pelimoodi ovat puolestaan enumeraatioita. Rivejä ohjelmassani on Javadocit mukaan luettuna hieman yli 1800. Lisäksi pelini applet-versiossa on lähes sama koodi toiseen kertaan. ElainpeliElainpeli on ohjelmani pääohjelmaluokka. Peliä käynnistettäessä peli luodaan kokonaan valmiiksi Elainpeli-luokasta käsin, eli kaikkien muiden luokkien luominen tapahtuu Elainpelissä. Ensin luodaan pelipaneeli ja peliruudukko, sitten alkuvalikko ja ohjevalikko sekä näille näkymät. Tämän jälkeen alustetaan tapahtumakuuntelija, joka on toteutettu Elainpelin sisäluokkana, ja joka kuuntelee kaikkia muita ikkunan tapahtumia, paitsi itse peliruudukossa tapahtuvia hiirenklikkauksia. Sen jälkeen luodaan pelin valikkorivi, ja pelinäkymän ylä- ja alapaneelit, joihin sijoitetaan pelin infoteksti ja pistenäyttö. Lisäksi alustetaan pelin alkunäkymä ja viimeistellään ohjenäkymä. Lopuksi määritellään pelille oikea sulkemisoperaatio ja asetetaan peli-ikkuna näkymään oikealla tavalla. Elainpelin toinen tärkeä metodi luontimetodin lisäksi on aloitaAlusta -metodi, jota käytetään aina kun uusi peli alustetaan. Metodi ottaa parametrikseen pelimoodin, ja alustaa pelin oikein sen mukaan. Lisäksi metodi huomioi sen, mikä pelimoodi on aiemmin ollut kyseessä, ja tekee tarvittavat muutokset pelinäkymään. Jos esimerkiksi siirrytään aikavastuksesta normaalimoodiin, täytyy pelin yläpaneelissa oleva aikalaskuri pysäyttää ja aikanäyttö poistaa. Tämä ratkaisu on mielestäni erittäin toimiva, sillä näin ollen pääohjelmametodin ja luontimetodin ei tarvitse ottaa parametrinä pelimoodia, mikä helpottaa appletin toteutusta. Näiden metodien lisäksi Elainpelillä on joukko metodeita, joiden avulla voi muuttaa esim. pisteitä, peliaikaa ja infotekstiä, sekä tiedustella, mikä pelimoodi on kyseessä tai onko peli loppu. Luokan asettelijaksi valitsin CardLayoutin, jonka avulla oli todella helppo toteuttaa eri näkymät: alkuvalikko, ohjeet ja itse peli. Jokainen näkymä on oma "korttinsa", joita näytetään sen mukaan, mitä pelaaja milloinkin valitsee. Peliä käynnistettäessä näkyviin laitetaan alkuvalikko, jolloin pelaaja saa itse valita kummasta pelimoodista aloittaa. Ainoa huono puoli tässä ratkaisussa on se, että pelin luonnin yhteydessä luodaan yksi peliruudukko turhaan, sillä kumpaa tahansa pelinappia klikattaessa kutsutaan aloitaAlusta -metodia oikealla parametrillä, jolloin vanha pelipaneeli poistetaan joka tapauksessa. Tämä taas johtuu siitä, että pelistä pääsee takaisin alkuvalikkoon, jolloin on tarpeellista vaihtaa edellinen peliruudukko uuteen, jos pelaaja haluaa aloittaa uuden pelin valikon kautta. PelipaneeliPelipaneeli on pelini toinen tärkeä luokka, ja itseasiassa luokista pisin. Hyvistä aikomuksista ja "vahingosta viisastuu" -ajattelusta huolimatta Pelipaneeliin, jonka tarkoitus on toimia graafisen ja loogisen osan yhdistäjänä, on livahtanut myös osa pelilogiikasta. Tämä ei ole puhdas vahinko, sillä koodatessa tuntui siltä, että metodien järkevä kutsuminen Peliruudukosta ei ole mahdollista, sillä Peliruudukko ei tunne muita pelin luokkia. Jos nyt koodaisin projektini uudestaan, miettisin varmasti uudemman kerran metodien sijoittelua koodiin, sillä olen melko varma siitä, että ainakin osan pelilogiikkaa sisältävistä metodeista olisi saanut jollain keinolla puhtaasti pelilogiikkaa sisältävään Peliruudukko -luokkaan. Tällä suunnittelulla ja toteutuksella tekemäni ratkaisu tuntui kuitenkin itsestäni selkeimmältä ja mikä parasta, se myös toimii, vaikkei tyylipuhdas olekaan. Pelipaneeli on siis yksi pelin kolmesta paneelista. Se sisältää peliruudukon, eli varsinaisen pelin, ja siihen liittyvät informatiiviset JLabelit, kuten infotekstin sekä aika- ja pistenäytön. Pelin CardLayoutille Pelipaneeli edustaa pelinäkymää. Myös Pelipaneelilla on sisäluokka, Hiirenkuuntelija, joka kuuntelee pelin ruutujen, eli käytännössä Elainnappien klikkauksia ja on näin ollen vastuussa siitä, mitä tapahtuu kun nappeja klikataan. Pääasiassa tästä syystä myös ne metodit, joiden tarkoitus on reagoida hiiren klikkauksiin, on sijoitettu Pelipaneeliin, vaikkakin tämä toteutus rikkoo sitä periaatetta, että graafinen ja looginen puoli pitäisi pitää erillään. Lähes kaikki Pelipaneelin luokat ovat "ketjutettuna" toisiinsa, ja ne kaikki ovat vastuussa siitä, mitä tapahtuu kun peliruudukon ruutuja klikataan. Jos peli ei ole loppu, Hiirenkuuntelija -luokasta kutsutaan reagoiPainallukseen -metodia, joka puolestaan kutsuu tarkistaYmparoivatRuudut -metodia, jos kahta vierekkäistä ruutua on klikattu peräkkäin. Jos näiden ruutujen vaihto on sallittua, eli rivi syntyy, metodi kutsuu edelleen poistaRivi -metodia, joka ohjaa rivin poiston joko poistaPystyrivi- tai poistaVaakarivi -metodille, sillä näiden poistamiseksi tarvitaan erilaisia toimenpiteitä. Lisäksi jos yksikin rivi on poistettu, saa reagoiPainallukseen -metodi tästä tiedon, ja kutsuu epäsuorasti syntyneiden rivien varalta poistaValmiitRivit -metodia, joka huolehtii siitä, ettei ruudukkoon jää valmiita rivejä. Kyseinen poistaValmiitRivit -metodi toimii suurimmaksi osaksi hyvin, mutta toisinaan (noin kerran seitsemässä pelissä) ruudukkoon saattaa jäädä yksi valmis rivi. Yritin löytää tälle syytä, mutta useiden kertojen tarkistuksen jälkeenkään mitään ei löytynyt. Ruudukkoon jääneillä riveillä ei myöskään ole mitään yhteistä, sillä välillä rivit ovat vaaka- ja välillä pystyrivejä, joskus ne ovat keskellä ruudukkoa ja toisinaan taas reunassa. Valmis rivi saattaa myös syntyä heti pelin alussa (jolloin kutsutaan myös kyseistä metodia) tai kesken pelin. Tämä on sinänsä todella erikoista, sillä metodin pistäisi tarkistaa yhden kerran "liikaa", ettei valmiita rivejä ole. Myös yksi assareista pohti ongelmaa kanssani projektineuvonnassa - tuloksetta. Tämä erikoinen bugi ei varsinaisesti haittaa pelini toimintaa, sillä valmis rivi poistuu seuraavalla siirrolla joka tapauksessa, kun tätä metodia kutsutaan uudestaan, ja pelaaja saa rivistä pisteet aivan kuten kuuluukin. Siksi en ole kovin huolissani kyseisestä bugista, vaan kutsuisin sitä ennemminkin pelin ominaisuudeksi, vaikkakin haluaisin mielenkiinnosta tietää, mikä kyseisen ilmiön aiheuttaa. PeliruudukkoPeliruudukko -luokkani oli siis alunperin tarkoitus sisältää pelin logiikka kokonaisuudessaan. Tämä ei toteutunut, mutta kyseisessä luokassa on silti pelkästään logiikkaa, ja se muodostaa yhdessä Pelipaneelin kanssa koko pelin logiikan. Peliruudukon tärkein tehtävä on arpoa eläimet ruudukkoon, ja luokka on vastuussa myös eläinten paikkojen vaihdosta. Lisäksi Peliruudukko tarkistaa, onko pelissä mahdollista tehdä enää siirtoja, eli voiko kolmen tai useamman eläimen rivejä syntyä. Näiden pelilogiikan kannalta tärkeiden metodien lisäksi peliruudukolta voi kysyä korkeutta, leveyttä sekä yksittäisiä ruutuja. ElainruutuJokainen peliruudukossa oleva ruutu on Elainruutu -luokan ilmentymä. Jokainen ruutu on tietoinen sekä omasta lajistaan että sijainnistaan eli x- ja y-koordinaateistaan. Näin ollen ruudulta voi kysyä lajin lisäksi sitä, onko se jonkin toisen ruudun naapuri. Tämä tapahtuu onNaapuri -metodilla, joka ottaa parametrikseen toisen ruudun koordinaatit. Halusin toteuttaa ruudukon ruudut omana luokkanaan, sillä vitos- ja kutosharkan Miinapeliä koodatessani käytin toisenlaista ratkaisua, eli kaksiulotteisia taulukoita ruutuolioiden sijaan. ElainnappiElainnappi on puhtaasti graafiseen toteutukseen liittyvä luokka, joka huolehtii siitä, että jokaista ruutua vastaa oikeannäköinen nappi, eli jokaisella napilla on oikea eläinkuva. Lisäksi Elainnapin tehtävä on joko asettaa reunat tai ottaa ne pois klikatulta ruudulta tarpeen mukaan. Enumeraatiot Laji ja PelimoodiToteutin peliruutujen eläinlajit enumeraationa. Laje on seitsemän erilaista: kettu, hiiri, pöllö, poro, käärme, pupu ja siili. Myös pelimoodin on esitetty enumeraationa, vaikka vaihtoehtoja on vain kaksi: normaali ja aikavastus. Tämä oli kuitenkin mielestäni selkein tapa toteuttaa molemmat ominaisuudet, sillä enumeraatio on paljon yksinkertaisempi ja selkeämpi asettaa vaikkapa parametriksi, kuin toteuttaa sama kokonaisluvuilla tai merkkijonoilla. AppletTein pelistäni myös applet-version eli sovelman. Tämä toteutui melko helposti muuttamalla Elainpeli -luokkaa sekä kuvien lataustapaa. Elainpelin pääohjelmametodi vaihtui init() -metodiksi ja luokka periytyy tässä tapauksessa JFramen sijasta JAppletista. Koodiin unohtui kommentoida se, että kuvien latausmetodi on kurkattu kuudennen Java-harjoituksen malliratkaisusta, joten se tunnustus tulee nyt tässä. Kokemukset projektistaSuunnitteluSuuren kokonaisuuden hallinta huolestutti minua projektia aloittaessani, sillä en ole kovin hyvä suunnittelemaan asioita tarkasti etukäteen. Itselleni sopii paremmin "suunnittele samalla kun teet" -tyyli, jolloin on helppo huomata, mikä toimii ja mikä ei. Suunnittelu oli varmasti yksi hankalimmista asioista projektissa, sillä kokonaisuus tuntui liian isolta enkä tiennyt mistä aloittaa. Lopulta tein karkean suunnitelman ennen koodauksen aloittamista, ja suunnittelin yksityiskohtia työn edetessä. Omasta mielestäni ratkaisu toimi varsin hyvin, sillä projekti eteni vauhdikkaasti eikä kovin suuria ongelmia ilmennyt missään vaiheessa. Kuten jo aiemmin sanoin, jos nyt tekisin projektini uusiksi, miettisin kahteen kertaan, onko järkevää laittaa pelilogiikkaa Pelipaneeliin, jolla on osuutta myös graafiseen toteutukseen. Ratkaisu on nyt kuitenkin jo tehty, ja se toimii, joten seison ratkaisuni takana. Muita kovin huonoja teknisiä ratkaisuja en ole pelissäni havainnut. ToteutusProjektin toteutus sujui todella hyvin, ja olin yllättynyt etenkin siitä, miten nopeasti työskentely loppujen lopuksi sujui. Hankalinta ohjelmassani oli ehdottomasti algoritmien keksiminen ja toteutus. Olen kuitenkin tyytyväinen omiin algoritmeihini, jotka ovat alusta loppuun itse tehtyjä. Ratkaisuni ovat hyvin käytännönläheisiä ja loogisia, ja onkin mahdollista, että samat metodit olisi saanut tehtyä tyylikkäämmin tai vähemmällä rivimäärällä. Ohjelmani metodit kuitenkin kuvastavat hyvin omaa koodaustyyliäni, ja siksi olen niihin joka tapauksessa tyytyväinen. OngelmatVarsinaisia pahoja bugeja ei matkani varrelle osunut, mutta poistaValmiitRivit -metodin outo satunnainen toimimattomuus jäi vaivaamaan. Kovasta yrityksestä huolimatta bugin syytä ei koskaan löytynyt. Tämä oli kuitenkin ainoa koodin kannalta todella haastava tapaus pelissäni, sillä muiden bugien syy oli lähinnä huolimattomuus, ja ne löytyivät helposti. Toinen tosissaan päänvaivaa aiheuttanut osa pelissäni oli pelin grafiikka. Koska oma kuvankäsittelytaitoni ei olisi riittänyt mitenkään kuvien piirtämiseen tietokoneella, jäi ainoaksi vaihtoehdoksi piirtää eläinkuvat käsin. Paperilla kaikki näyttikin hyvältä, mutta kun kuvat pienennettiin PSP:llä oikean kokoisiksi, eläinten ääriviivoista tuli katkonaisia ja epäselviä. Tämä ongelma ratkesi lopulta niinkin käytännönläheisesti kuin TextMarker -tussilla, joka teki viivoista tarpeeksi paksuja ja samanvärisiä, sekä kuvankäsittelyohjelman maalipurkilla, jolla reunoista tehtiin mustaakin mustempia. Lopulta kuvat näyttivät siltä kuin pitikin, mutta aikaa grafiikkaan kului ehdottomasti enemmän kuin mihinkään yksittäiseen pelin osaan. Lisäksi pelistäni jäi puuttumaan muutama ominaisuus, jotka olisin halunnut toteuttaa. Mielessäni oli erikoisruutu, jota klikkaamalla yhden satunnaisesti valitun eläinlajin kaikki edustajat olisivat kadonneet ruudukosta. Tämän toteuttaminen kävi itselleni kuitenkin jäljellä olleen ajan puitteissa mahdottomaksi, sillä koska jo vaaka- ja pystyrivien poistaminen vaati melko työläät metodit, joiden keksimiseen kului tunti jos toinenkin, olisi yksittäisten ruutujen poistaminen sieltä täältä vienyt aikaa varmasti paljon enemmän. Peliäni applettina kokeilleet olisivat toivoneet myös vihjeominaisuutta, joka antaisi pelaajalle vinkin siitä, mitä ruutua on vielä mahdollisuus siirtää rivin aikaansaamiseksi. Itselleni ei valitettavasti ollut tullut tällainen ominaisuus edes mieleen suunnitelmaa tehdessäni, joten viimeisellä viikolla tulleelle idealle ei riittänyt enää aikaa tarpeeksi, mikä jäi hieman harmittamaan. Oma työskentelyOmassa työskentelyssäni suurimmat puutteet olivat - aivan kuten aavistelinkin - suunnittelussa. Olen enemmän käytännön kuin teorian ihminen, ja siksi monen tunnin suunnittelu ilman mitään konkreettista on lähes mahdoton ajatus. Lisäksi ajankäyttösuunnitelmani meni melko pieleen joulun, matikan välikokeen ja java-tenttien takia. Olen kuitenkin tyytyväinen siihen päättäväisyyteen ja itsekuriin, jota osoitin silloin kun projektia vihdoin ehdin koodaamaan, sillä on vaikeaa saada työskenneltyä itse kunnolla silloin, kun muut lomailevat ja tekevät kaikkea kivaa. Koodatessa katosi monesti ajantaju, enkä milloinkaan pistänyt päivää pakettiin, ennen kuin väsäämäni osio oli valmis ja toimi. Olen myös iloinen siitä, että opin käyttämään Paint Shop Pro:ta edes jossain määrin, sillä tähän asti kuvankäsittelyohjelmat ovat olleet itselleni lähinnä vihollisia. Lisäksi appletin onnistunut toteutus lämmitti mieltä, sillä testaus helpottui huomattavasti ulkopuolisen avun myötä, ja nyt kun projekti on vihdoin valmis, voin esitellä neljän viikon kovan työn tulosta helposti myös kavereilleni. AjankäyttöAlkuperäinen ajankäyttösuunnitelmani petti pahasti, mutta näin jälkikäteen voin sanoa, että se oli alunperin todella ylimitoitettu. Projektisuunnitelmassani ilmoitin ajankäyttöarviokseni 90 tuntia, joka oli tarkoitettu kokonaan koodaamiseen, sillä en ottanut huomioon, että myös suunnittelu ja loppuraportin kirjoitus lasketaan projektin ajankäyttöön. Koodaamiseen käytetty aika oli siitäkin huolimatta paljon vähemmän kuin mitä olisin kuvitellut, sillä ohjelmakoodin tekemiseen meni yhteensä aikaa noin 45 tuntia. Pelini kuitenkin toimii ja sisältää myös lisäominaisuuksia, kuten toisen pelimoodin, joten en mielestäni ole mennyt siitä mistä aita on matalin. YhteenvetoPelini pohja on hyvin paljon samanlainen kuin miinapelissä, mutta silti opin uusia asioita sekä syvensin aiempaa tietämystäni. Monet vanhat ja ennestään tutut asiat aukenivat vasta projektin aikana kunnolla. Esimerkkinä voisi mainita vaikkapa asettelijoiden ja kuvien käytön sekä sisäkkäisten paneelien hyödyn, ja myös API:a tuli luettua eri tavalla kuin ennen. Ennen kaikkea opin kuitenkin hallitsemaan suurta kokonaisuutta, kehittämään itse vaativiakin algoritmejä, sekä muokkaamaan valmiista sovelluksesta toimivan sovelman. Lisäksi stressinsietokykyni on varmasti vahvistunut tämän projektin myötä enemmän kuin koskaan. Projektia aloittaessa haaste tuntui valtavalta ja melkein mahdottomalta, mutta oman projektini lopputulos yllätti ainakin itseni positiivisesti. En olisi uskonut, että saan neljässä viikossa aikaan näin toimivan ja ennen kaikkea visuaalisesti hienon pelin, jota myös muut kuin minä jaksavat pelata. Projektin tekeminen oli erittäin raskas, mutta samalla todella opettavainen ja palkitseva kokemus. Näin jälkikäteen ajateltuna se on ainoa oikea päätös Studio1 -kurssille, joka on ollut samaan aikaan haastava, jännittävä ja hauska. Tämä kurssi ja etenkin projekti on myös syy, miksi alunperin hain infolle, sillä mielestäni kuulosti uskomattoman hienolta, että joku, joka ei osannut vielä syksyllä koodata lainkaan, oli tehnyt oman sudoku -tietokonepelin joululomalla, ja halusin kokea saman itse. Niin kuin sananlaskukin sen sanoo: sitä saa mitä tilaa, ja tällä kurssilla saa kyllä koko rahan edestä. P.S. Koska aika tunnetusti kultaa muistot, ja onnistuminen häivyttää mielestä turhautumisen ja koetun tuskan, on ehkä hyvä nähdä totuudesta myös se toinen puoli, ja katsastaa projektipäiväkirjani. |
Portfolion etusivu Esseet ja käsitekartat Johdanto Tehtävät OLO-sessiot Johdanto Tapaukset 0-5 Tapaukset 6-10 Ohjelmointi Johdanto Java 1 Java 2 Java 3 Java 4 Java 5 Java 6 Projekti Aihe-ehdotus Projektisuunnitelma Loppuraportti Projektipäiväkirja Kokeile Eläinpeliä! Kurssipalaute |