Loppuraportti Studio1:n ohjelmointiprojektista


Ohjelma: virheenetsintäpeli (eräältä kutsumanimeltään ”Find-it”)





Juho Kokkola

80326R










Yleistä


Tekemäni ohjelma on peli, jossa pelaajan tehtävänä on löytää kahden kuvan väliset eroavaisuudet. Eroavaisuuksia on viisi kappaletta per kuvapari, joista jokainen on oma levelinsä. Pelaajan on tarkoitus kerätä mahdollisimman paljon pisteitä, ennen kuin etsintäaika loppuu.


Käynnistettäessä ohjelma ruudulle ilmestyy ensin lyhyellä ohjetekstillä varustettu ikkuna, josta pelaaja voi halutessaan joko aloittaa pelin tai lukea kattavammat ohjeet. Itse peli aukeaa suurempaan ikkunaan. Peli-ikkuna sisältää ylhäällä valikon, keskellä kaksi kuvapaneelia vierekkäin sekä alapaneelin, josta jäljellä olevan etsintäajan lisäksi pelaaja saa tietoa senhetkisestä levelistä, löydettyjen virheiden lukumäärästä ja pisteistä. Pelaaja klikkaa hiirellä havaitsemansa eroavaisuuden kummasta kuvasta tahansa, ja saa ilmoituksen osuiko oikeaan vai ei. Oikeasta klikkauksesta kuuluu suljinääni-ääniefekti ja alue kehystyy kumpaankin kuvaan, muuten peli herjaa summeriäänellä, ja etsintäaika vähenee viisi sekuntia. Kun pelaaja on löytänyt kuvista kaikki viisi virhettä, peli ilmoittaa siirtymisestä seuraavalle tasolle, jonka jälkeen kuvapari vaihtuu uuteen. Etsintäaika tasoa kohden lyhenee, mitä pidemmälle pelissä edetään. Levelistä numero 16 eteenpäin aika pysyy vakiona (15 sekuntia), joten pelin läpi pelaaminen ei periaatteessa ole mahdotonta. Eri kuvapareja on 30 kpl, joista pelin eri levelit arvotaan. Jos (ja kun) aika loppuu kesken, peli loppuu. Tällöin paljastetaan löytämättä jääneet tason virheet sekä pyydetään pelaajaa kirjoittamaan nimensä dialogiin. Lopuksi näytetään tuloslista (10 parasta pistemäärää), jonka alapuolelta voi valita aloittaako uuden pelin vai lopettaako pelaamisen.







Tekninen toteutus


Ohjelmointikielenä on Java, jota olen kirjoittanut Eclipse Platform – ohjelmointiohjelmalla (version 3.4.1). Pelin käyttöliittymän toteutukseen olen käyttänyt Javan valmista Swing-komponenttikirjastoa.


Ohjelman luokkakaavio:



Ohjelma käynnistyy, kun luokka Alkuframe ajetaan. Alkuframe-olio on nimensä mukaisesti pieni ruudulle ilmestyvä frame, josta lyhyen ohjeistustekstin siivittämänä voi päättää klikkaako nappia, josta aukeavat kattavammat peliohjeet omaan frameensa, vai nappia, joka käynnistää itse pelin. Toteutuksessa olen käyttänyt Swing-komponenteista JFramea, JTextPanea ja JButtonia. Nappeja kuuntelee ActionListeneristä periytyvä nappienkuuntelija-luokka.


Pelaajan aloittaessa pelin alkuframe sulkeutuu, ja ruudulle aukeaa varsinainen peli-ikkuna. Peli-luokka luo tämän graafisen käyttöliittymän ja pitää sitä yllä. Toteutus perustuu niin ikään Swing-komponentteihin, kuten JMenu, JPanel ja alapaneelin JLabelit, mutta oleellisempia ovat kahteen keskellä olevaan JPaneliin sijoitettavat kuvat, eli Paneeli-luokan instanssit. Juuri Paneeli-luokka sisältää pelin pelilogiikan. Peli-luokka toimii jossain määrin tiedonvälittäjänä Paneeli-luokalle, koska peli sisältää kaksi eri Paneeli-oliota. Lisäksi se pitää yllä pelaajalle pelin alareunassa näkyviä tietoja (kuten etsintäaika ja pisteet). Peli-luokka hoitaa myös tulostiedoston luonnin (ohjelman sijaintikansioon) ja vastaa, että tulosrivi luodaan ja tallennetaan Tulosrivi- ja Tulostaulukko-luokkien toimesta oikeaan aikaan oikeassa paikassa ja että tulokset näytetään pelin lopuksi.


Paneeli-luokka periytyy JPanel-luokasta. Kuvien toimintaperiaate pelissä on, että kuvat piirretään Paneeli-olioon Paneeli-luokassa, jonka jälkeen ne lisätään Peli-luokan JPanel-komponentteihin. Paneeli-luokan konstruktorissa sen attribuuteiksi alustetaan listat pelin kuvapareista sekä niiden yksilöllisistä virhealueista. Kuvaparien varsinainen tallennus tapahtuu Kuvat-luokassa, ja virhealueet puolestaan tallennetaan Lootat-luokassa. Paneeli-luokka vastaa pelin tapahtumista. Se kuuntelee hiiren klikkauksia, ja riippuen siitä osuuko pelaaja eroavaisuuteen vai ei, kehystää se kuvista virhealueita tai soittaa summeri-ääntä. Luokan paintComponent()-metodi on tahdistettu attribuuttien arvoilla siten, että kumpikin Paneeli-olio tietää koko ajan, mitä itseensä piirtää (kuvat ovat toisiaan vastaavia, ja samat virhealueet ovat kehystettynä, jos niitä on löydetty). Paneeli-luokka huolehtii myös esimerkiksi virheen poistamisesta listasta, kun se kerran on löydetty, sekä kuvaparin poistamisesta, kun taso on suoritettu. Samaa kuvaparia ei siis pelissä esiinny kahdesti, eikä löydetty virhe enää ole virhe. Paneeli-luokka lisäksi laittaa Peli-luokan päivittämään pelaajan tietoon jäljellä olevan levelin numeron, löydetyt virheet, jäljellä olevan ajan sekä pistemäärän.


Tiiviisti:


Pelissä käytettävät kuvat ja äänet ovat omassa paketissaan nimeltä ”kuvatYms”. Se löytyy src-kansiosta yhdessä .java-tiedostojen kanssa.





Työskentely ja sen tarjoamat elämykset


Olen suunnitelmaani tyytyväinen; ei se mikään spesifiyden huippu ollut, mutta riittävät raamit olivat kasassa kokonaisuuden hahmottamiseen ja sitä kautta koodauksen aloittamiseen. Oleellisesti suunnitelma kuitenkin muuttui virheen tallennus- ja löytölogiikassa. Sokobanista tutun peliruudukon (JButton/vastaava) sijaan, käytin Rectangle-luokkaa. Vihiä tällaisen luokan olemassaolosta sain, kun kyselin, miten voisi ”näkymättömän” ruudukon asettaa kuvan alle/päälle. Lopputuloksena keskustelutuokiosta käteen jäi kyseinen luokka, josta rupesin ottamaan selvää. Rectangle sattui ilokseni olemaan kuin tällaista peliä varten luotu. Virheiden tallentaminen, löytyminen ja merkitseminen hoituivat yhden ja saman luokan avulla, vieläpä fiksusti. Kun hiirellä klikataan kuvaa, riittää tutkia, onko getMousePosition()-metodilla saatu piste kyseessä olevan kuvaparin jonkin virhealueen sisällä. Ruudukon kanssa sähläämiseltä siis onneksi vältyttiin. Muuten suunnitelma piti hyvin paikkansa. Muutamia luokkia loin lisää, kun päätin toteuttaa kuvien ja virhealueiden manuaalisen tallentamisen omina erillisinä kokonaisuuksinaan. Lisäksi tulosten tallentamiseen loin luokat Tulosrivi ja Tulostaulukko.


Koodausrupeama oli kokonaisuutena varsin seesteinen. Oli vain muutamia asioita, jotka aiheuttivat päänvaivaa. Tosin silloin yleensä asian setvimiseen kuluikin reilusti tunteja. Ensimmäinen kompastuskivistä oli kuvien näkyviin saaminen. Olin luonut graafiset raamit ja halusin pari testikuvaa näkymään paneeleihini. Tämä ei luonnollisestikaan meinannut millään onnistua, johtuen siitä yksinkertaisesta syystä, että kuvat olivat väärässä kansiossa, tai oikeammin, niihin oli viitattu väärin koodista. Tajusin asian vasta, kun vilkaisin, miten olin asian tehnyt Sokoban-pelissäni. Kuvien siirrolla asia siis ratkaistiin ja homma alkoi pelittää. Toinen oikeastaan vielä suurempi harmin lähde oli Paneeli-luokan paintComponent()-metodi. Siitä riitti tuskaa niin alussa, kuin vielä loppupuolellakin. Swing:hän tunnetusti toteuttaa kyseisen metodin ”kun ehtii”. Eli tilanteessa, jossa saman luokan kahta instanssia tulisi piirtää omilla tavoillaan, eikä koodia sitten suoritetakaan siinä järjestyksessä, kun on kirjoitettu, voi hieman ns. nakertaa. Välillä mietin, olisiko sittenkin pitänyt tehdä kaksi luokkaa, vasen ja oikea paneeli. Pitäydyin lopulta yhdessä luokassa, koska kyseessä on kuitenkin hyvin samanlaiset oliot. Toinen bugi asian tiimoilta tuli tilanteessa, jossa ikkuna esim. piilotetaan kesken pelin tai menu-valikko avataan. Tällöin Swing kutsui uudelleen kyseistä metodia ja homma meni metsään. Ratkaisu vaati, että osa piirtologiikasta siirretään piirtometodin ulkopuolelle. Tässä ei sinänsä ollut hankaluuksia, mutta turhalta työltä olisi vältytty, jos olisi jotenkin tiennyt tästä ominaisuudesta etukäteen.


Olen tyytyväinen siihen, että vaikka deadlineen on vielä 24 tuntia, on homma loppuraportin loppuosaa vaille valmis. Viime päivinä ei ole edes tarvinnut ihmeemmin kuumottaa, koska toimiva ohjelma on ollut kasassa jo jonkin aikaa. Hyvä ratkaisu siis oli aloittaa tarpeeksi ajoissa (jo ennen joulua) ja tehdä rauhassa tasaisesti. Missään vaiheessa ei tullut tunnetta, etten saisi projektia valmiiksi ajoissa. Silti haluaisin parantaa työtehokkuudessa, ja paljon. Monena päivänä, kun on pitänyt koodata tehokkaasti, ei valmiiksi saatu työ vastaa sitä määrää tunteja, mitä koneella on vietetty. Osasyynä tähän on varmasti netissä surffailu, irkkaus tms. ”taukotoiminta”, joka usein venyy luvattoman pitkäksi, eikä se loppujenlopuksi edes saa ajatuksia pois tekemättömästä työstä. Parempi olisi tehdä pienempi tuntimäärä tehokkaasti, ja lähteä sen jälkeen tekemään jotain täysin muuta. Omalta osaltaan taas aikaa veivät bugit yms. koodin toimimattomuus, joka välillä turhauttaa. Asiaan vaikuttaa kuitenkin vain oman osaamisen taso, eikä kukaan loppujen lopuksi kirjoita valmista koodia. Demotilaisuudesta sain vielä vinkin, että kuvien ja virheiden tallennus olisi kannattanut tehdä mieluummin esimerkiksi erilliseen tekstitiedostoon, jonka olisi koodissa scannerilla käynyt läpi, ja tallentanut tiedot tämän perusteella tietorakenteisiin. Ratkaisu olisi ollut selkeämpi ja siistinnyt myös osittain koodia. Ohjelma on kuitenkin toimiva, eikä suuria rakennemuutoksia ole mielestäni enää järkevää lähteä tekemään. Muutin kuitenkin viime hetkellä muutaman asian. Ensinnäkin, olin huomannut, että hiiren kuuntelussa käytettävä mouseClicked()-metodi ei toiminut niin hyvin, kuin olisin toivonut. Hiiren tuli olla ”painallus-vapautus”-tapahtuman ajan täsmälleen samassa pikselissä, muuten klikkausta ei Swingin mukaan tapahtunut. Tämä haittasi pelaamista varsinkin, kun oli kiire. Demossa kuitenkin sain tietää, että miettimäni vaihdos mousePressed()-metodiin oli täysin käypä. En ollut aikaisemmin varma, mitkä kaikki pisteet tämä ottaisi huomioon, jos hiirtä liikuteltaisiin nappi pohjassa. Nyt peli toimii huomattavasti sulavammin ja virheenetsijäguruja palvelevammin. Lisäksi pohdin kauan, laitanko pelin kuviin jonkinasteisen helppousjärjestyksen. Päädyin ratkaisuun, josssa ensimmäiset kuvat ovat arvottuja helppojen kuvien joukosta, seuraavat keskivaikeiden jne. Enemmän peliä pelaavaa voi ruveta alkupään samat toistuvat kuvat ärsyttämään. Tällöin voi muokata Paneeli-luokan yhtä parametria (kohta ja ohjeet on kommentoitu koodiin), ja peli arpoo tasot kaikkien kuvien joukosta.


Aikaa tähän projektiin kaikkine hyvineen kului aika tarkkaan 100 tuntia. Suunnilleen se määrä, johon olin varautunut aloittaessani urakkaa. Monta päivää / osaa päivästä on tullut istuttua koneen ääressä. Työskentely oli jokseenkin sitä, mitä odotinkin: tasaista duunailua Eclipsen äärellä, välillä turhautuneena, välillä onnistumisesta tyytyväisenä.





Yhteenveto


Olen kaikin puolin tyytyväinen projektin lopputulokseen. Mielestäni hyvän idean saatuani, pystyin todella myös toteuttamaan sen, vieläpä juuri niin kuin suunnitelmissa toivoin. Ohjelma ei sinänsä ole mikään kovin monimutkainen, avaruuden mallinnuslogiikkaa vaativa tuotos, mutta mielestäni kuitenkin hyvä ja toimiva peli. Studio1-kurssin ohjelmointiprojektin tunnusmerkit täyttyvät, ja sehän lienee pääasia.


Koodatessa opin luottamaan syksyllä saamiin tietoihin ja taitoihin javan saralla, mutta myös etsimään lisää tietoa ratkaisujeni tueksi. Projekti opetti minulle paljon. Uusina asioina opin muun muassa grafiikan piirtämisen Graphics-luokan avulla, Rectangle-olioiden luonnin ja käytön sekä HSB-colorin kolmen parametrin määrittämisen niin, että todella sain haluamani värisävyn.


Tärkeimpänä kokemuksena pidän sitä, että opitun asian pohjalta pystyin tuottamaan itsenäisesti toimivan ohjelman. Vielä runsas neljä kuukautta sitten en olisi ymmärtänyt yhtään ainoaa riviä nyt kirjoittamastani koodista.





Linkit: