Juhitav animatsioon Joonistuskäsud, lõimed, mälupilt, heli Taustateave Joonistamine Javas on püütud joonistusvahendid teha sihtpinnast sõltumatuks, nii et sama programmilõigu abil võib joonistada nii ekraanile, mällu kui printerisse ning sobiva draiveri abil ka muude vahendite abil. Programmi poolt saadetakse joonistatavale pinnale teateid graafilise konteksti abil, milleks on enamasti klassi Graphics või Graphics2D oskustega isend. Kontekstile antakse üldisi käsklusi joone, ringi, teksti jm. joonistamise kohta, viimase ülesandeks on tulemus kanda üle sihtpinnale. Kas tegemist on mälupildi joonistamisel massiivi väärtuste arvutamisega või põletuspulga liigutamisel plotteris, sellele ei pea programm tähelepanu pöörama. Programmi kirjutaja isegi ei pea teadma, millise sihtseadmeni tema käskude mõju lõpuks ulatub. Et iga lõigu või punkti joonistamisel ei peaks eraldi kaasa lisama joonistamisel vajalikke parameetreid, hoitakse neid graafilise konteksti juures ning koos joonistuskäsklustega edastatakse sihtseadmele. Graphics hoiab eneses joonistusvärvi, -piirkonda ning nihet, Graphics2D juures aga tulevad juurde joone laius, pildi keere, kalle, suurendus, joonte ühendus. Samuti võib joonistusvärviks valida mustri või värviülemineku. Sellisel juhul arvutades mälus asuvale pildile joonistatavat ringi iga punkti puhul vaadatakse, milline koht mustrist vastab joone alla jäävale kohale sihtpinnal. Välja paistva graafikakomponendi joonistuskäsud koondatakse üldiselt meetodisse nimega paint. Meetodile antakse parameetrina graafiline kontekst, mis oma väljundi suunab pinnale, kus kasutaja arvab komponenti asuvat, üldjuhul ekraanile. Nii õnnestub lihtsustada komponenti näitava kesta tööd. Igal korral kui leitakse, et komponendi sisu on kaduma läinud või vajab lihtsalt uuendamist, kutsutakse taas välja paint, mis komponendi arvates sobiva oleku taastab. Kui komponendi omanik vaatab, et uuendamist vajab vaid osa komponendi pinnast, võidakse ressursside kokkuhoiu huvides saata paint-meetodisse pügatud (vähendatud, clip) joonistusalaga Graphics, mis kannab sihtpinnale edasi vaid nende joonistuskäskude tulemused, mis jäävad konteineri (komponendi omaniku) arvates uuendamist vajavasse piirkonda. Nõnda võib mõnikord veebilehe kerimisel avastada, et konteiner on uuendamist vajavat osa valesti hinnanud ning valgeks jäetakse ka osa komponendi pinnast, kus peaks midagi muud leiduma. Soovides kogu komponendi pinda uuendada, tuleb anda käsklus repaint(). See käsk käivitab pinnauuenduse eraldi lõimes. Ta ootab kõigepealt, et virtuaalmasinal jaguks piisavalt ressursse joonistamiseks. Edasi kutsutakse välja komponendi meetod update parameetrina graafiline kontekst komponendi asukohaseadme pinnale joonistamiseks, mis klassist java.awt.Component päritud vaikimisi oskuste alusel kõigepealt katab komponendi pinna taustavärviga ning seejärel kutsub välja meetodi paint, kus kirjeldatud käskude abil peaks kompomendi arvatav pinnavälimus kasutajani jõudma. Nõndamoodi saab hoolitseda, et vanast välimusest uude juhuslike jupikeste näol rudimente ei jääks, sest kui vana pind ühtlaselt taustavärvi plaadiga katta, siis ei tohi ju põhja midagi alla silma häirima jääda. Samas võib suurema pinna uuendamisel tekkida ekraanile vilgatus, kui platsi tühjendamise ning uue sisu joonistamise vahele piisavalt suur vahe jääb, et inimsilm seda märkab. Lahenduseks leitakse, et tuleb komponendi kohal näidatav pilt eelnevalt valmis arvutada ning siis ühekorraga või rida-realt ekraanile joonistada. Nõnda saab teha, kui pilt koostatakse mällu ning paint-meetodi sisuks oleks vaid selle pildi ekraanile manamine, update aga loobuks taustavärviga katmisest (taustavärv oleks ekraanile joonistatava pildi põhjaks) ning kutsuks kohe välja paint-i. update't annab muuta ülekatte abil: kui kirjutatada public void update(Graphics g){ paint(g) } siis jääbki taustaga katmine ära ning kogu töö usaldatakse paint'ile. Sellist joonistusviisi nimetatakse topeltpuhverduseks ning Swingi juures võib sama skeemi joonistamise juures ühe lisaparameetriga sisse lülitada. Ekraanile joonistada saab ka mujal kui paint-meetodis, kuid sellisel juhul konteineri poolt kutsutud pinnauuenduse korral läheb kõik muu kaduma, mis paint'is kirjas pole. Sellegipoolest on mõnikord mugav kohe hiirevajutuse peale küsida pinna graafiline kontekst ning sealtkaudu soovitud ringike ekraanile manada, mitte asuda andmeid muutujates säilitama ning paint'i kaudu ekraanile tooma. Tõsisemate programmide juures tuleks aga valida viimane tee ning pea alati on siiski vaja ka omal teada, mida ja kus peaks ekraanile ilmuma. Ka liikumise puhul on mõistlik jätta pinna uuendamine paint-meetodi hooleks, kuigi vahel on mugavam otse leitud uute andmete järgi ka pilt ekraanile joonistada. Taust liikumise ajal mälus. Järgnevas näites koos kasutaja hiire lohistamisega liigub ekraanil ring. Liikumise taustaks on eelnevalt hiire lahtilaskmise kohas taustapildile joonistatud ringid ning iga uue lahtilaskmisega tekib taustapildile üks ring juurde. Joonistusmeetod paint viib ekraanile vaid taustapildi, liikumismulje jätab mouseDragged, kus alla joonistatakse taust ning peale hiire parajatist asukohta tähistav ring. Et taustapilt oleks kõikjalt Liigu2 isendist kätte saadav, selleks on deklareeritud Image pilt; klassi algusse. Pilt saab enesele väärtuse ning võimaluse andmeid sisestada aga alles joonistusmeetodis public void paint(Graphics g){ if(pilt==null)looPilt(); g.drawImage(pilt, 0, 0, this); } , sest varem pole kindlustatud, et createImage suudaks Liigu2 komponendi omadustele vastava pildi luua. Koos pildi loomisega küsitakse ka selle graafiline kontekst, et edaspidi oleks hea lihtne mälupildi peale andmeid saata. void looPilt(){ pilt=createImage(getSize().width, getSize().height); piltg=pilt.getGraphics(); } Java uuemates versioonides on võimalik pilt ka varem luua BufferedImage abil. Hiire sündmustele reageerimiseks on rakendile külge pandud kuular nii hiirevajutuste (MouseListener) kui hiire liikumise (MouseMotionListener) tarvis. public Liigu2(){ addMouseMotionListener(this); addMouseListener(new Liigu2kuular(this)); } Liikumise ja vedamise tarvis kaks meetodit on realiseeritud Liigu2 enese sees, vajutuste püüdmiseks on aga loodud eraldi adapterklass, kust hiirenupu lahtilaskmise peale mälupildile ring joonistatakse. import java.awt.*; import java.awt.event.*; import java.applet.Applet; public class Liigu2 extends Applet implements MouseMotionListener{ Image pilt; Graphics piltg; public Liigu2(){ addMouseMotionListener(this); addMouseListener(new Liigu2kuular(this)); } public void paint(Graphics g){ if(pilt==null)looPilt(); g.drawImage(pilt, 0, 0, this); } void looPilt(){ pilt=createImage(getSize().width, getSize().height); piltg=pilt.getGraphics(); } public void mouseMoved(MouseEvent e){} public void mouseDragged(MouseEvent e){ Graphics g=this.getGraphics(); g.drawImage(pilt, 0, 0, this); g.drawOval(e.getX()-5, e.getY()-5, 10, 10); } public static void main(String argumendid[]){ Frame f=new Frame(); f.add(new Liigu2()); f.setSize(200, 200); f.setVisible(true); } } Et hiire vajutamise teateid püüdev suudaks Liigu2 juurde kuuluvale pildile midagi joonistada, selleks peab siin olema ligipääs ekraanile joonistatava pildi graafilisele kontekstile. Üheks võimaluseks on lisada kuularile osuti, mille kaudu sihtisendile ligi pääseda. Selle muutuja nimeks on siin peremees. Objektorienteeritud programmeerimises on viisakas isendimuutujatele anda väärtusi meetodite kaudu, siin saadakse andmed kohale konstruktori kaudu. Kui üleval kirjutati kuulari lisamiseks addMouseListener(new Liigu2kuular(this)); , siis see this Liigu2kuulari loomisel tähendabki osutit Liigu2 eksemplarile. class Liigu2kuular extends MouseAdapter{ Liigu2 peremees; public Liigu2kuular(Liigu2 l2){ peremees=l2; } public void mouseReleased(MouseEvent e){ peremees.piltg.drawOval( e.getX()-5, e.getY()-5, 10, 10); } } Lõim liikumisel Järgnevas näites on lõime alamklass Pall2. Igal pallil on koordinaadid x ja y ning iga sammuga muutuvad need dx ja dy võrra. Iga pall liigub omaette lõimes, s.t. programmi muudest osadest sõltumatult. Pall alandab enese prioriteeti, s.t. et protsessori tööaja jagamisel loeb ta end keskmisest vähem tähtsamaks. Prioriteedi alandamine on vajalik selleks, et pallide liigutamine ei hakkaks märkimisväärselt aeglustama teisi lõimi, näiteks ekraanile joonistamist. Muidu võib juhtuda, et suure hulga pallide puhul jäävad ülejäänud tegevused suhteliselt unarusse. Käsk yield tähendab, et lõim annab oma tööjärje üle järgmisele ning asub ise järjekorda uuesti protsessori aega ootama. Raami alamklass Loim2 loob enesele viis palli isendit ning laseb nad liikuma. Loim2 ise realiseerib liidest Runnable, s.t., et tema run-meetodit on samuti võimalik panna tööle eraldi lõimes. Selle lõime ülesandeks on joonistada iga natukese aja tagant uus ekraanipilt vastavalt pallide uutele koordinaatidele. import java.awt.*; public class Loim2 extends Frame implements Runnable{ Pall2[] pallid; public Loim2(){ pallid=new Pall2[5]; for(int i=0; iparem)dx=-Math.abs(dx); if(y>all)dy=-Math.abs(dy); if(xmustripikkus)nihe=nihe-mustripikkus; g.drawImage(taust, 0, nihe-mustripikkus, this); } public void update(Graphics g){ paint(g); } public void start(){ veel=true; new Thread(this).start(); } public void run(){ while(veel){ repaint(); try{Thread.sleep(paus); }catch(Exception e){} } } public void stop(){ veel=false; } Image laePilt(String failinimi){ try{ return getImage(getCodeBase(), failinimi); }catch(Exception e){} return Toolkit.getDefaultToolkit(). getImage(failinimi); } public static void main(String argumendid[]){ Frame f=new Frame("Pildiraam"); Pildike1 ap=new Pildike1(); f.add(ap); f.setSize(300, 300); f.setVisible(true); ap.start(); } } Taust koos lilledega Nagu iga keerukama ülesande kallale tuleb asuda üksikute osade kaupa, nii ka siin on järgmiseks ette võetud lillede paigutamine taustale. Ning hoolitsetud, et lilled suhteliselt ühtlase tihedusega, kuid samas juhuslikult pinnale jaotuksid ning pinna suhtes paigal püsides inimese eest läbi liiguksid. Juurde on tulnud muutujad lilledega seotud andmete hoidmiseks. Lille kõrguse järgi saab arvestada millal ta ekraanile paistma hakkab, millal kaob ning hiljem ka leida, kas lill mõne muu alaga kattuma juhtub. Reaalarvuline lillelisamistõenäosus näitab, mitu lille luuakse keskmiselt iga sammu korral. Et väärtuseks on praegu 0.03*samm, tähendab, et iga kõrguse ekraanipunkti kohta tuleb keskmiselt 0.03 lille, ehk siis ligikaudu üks lill iga 33 punkti kohta. Lilli endeid hoitakse Vector'is; kollektsioonis, mille elemente võib vabalt lisada ja eemaldada ilma, et peaks ise muret tundma mälu eraldamise või vabastamise pärast. Aega arvutatakse ekraanipunktides. Et liikumine on ühtlase kiirusega, siis leidub ka kulunud ajaga võrdeline seos. Samahästi võiks muutujat nimetada kogunihkeks, ehk maapinna liikumise kogu punktide arvuks. Lillede haldamise tarbeks on loodud mitu alamprogrammi: lisamise, eemaldamise ja joonistamise tarbeks. Lillelisamistõenäosus näitas, mitu lille tuleb ühe joonistustsükli eel lisada. Kui see väärtus on näiteks 2,7, siis kaks lille lisatakse kindlasti, kolmas aga tõenäosusega 0,7 ehk 70%. Muutuja lt liigub tõenäosuse algsest väärtusest kuni nullini. Kuni lt väärtus on suurem kui 1, lisatakse lill kindlasti, sest Math.random()-i väljastatu on vahemikus 0<=x<1. Jääb aga lt väärtus alla ühe, siis sõltub Math.random'i tegevusest, kas tuleb veel lill või mitte. Lill luuakse uue objektina, millele jäetakse meelde tema x-koordinaat (muutumatu) ning aeg ehk maapinna liikumise teepikkus ekraanipunktides lille lisamise ajaks. Selle järgi on võimalik pärast arvutada, kus lill ekraanil peaks asuma, sest kõik lilled luuakse vaikimisi ülaserva. X-koordinaat leitakse juhuslikult pea kogu nähtava laiuse ulatuses. Kümme punkti võetakse laiusest x-i leidmisel maha, et ei tekiks lille, mis ekraanil sugugi näha poleks. Meeldejäetud x tähistab lille vasakut serva. void lisaUusiLilli(){ for(double lt=lillelisamistoenaosus; lt>0; lt=lt-1){ if(Math.random()korgus+lillekorgus){ lillekesed.removeElementAt(i); i--; } } } Joonistamine läks pikemaks. Ehkki paint näeb välja ilus lühike, on tema ülesannet täitma tulnud hulk abilisi. public void paint(Graphics g){ koostaPilt(); g.drawImage(pilt, 0, 0, this); } Lisaks taustale tuleb sisse lugeda ka lille pilt. Samuti ei joonistata tausta enam otse ekraanile, vaid pannakse mälus enne taust ja lilled uue pildi peale kokku ning alles seejärel näidatakse tulemus ekraanile. Mälupildi koostamiseks on createImage, mis jällegi rakendi puhul pole enne võimeline käivituma kui esimese paint-i ajal. Loodud pildi käest küsitud piltg jääb globaalsena üle kogu isendi kättesaadavaks, et ei pea muudele joonistavatele alamprogrammidele seda eraldi kätte jagama. Nagu näha, pärast iga sammu eemaldatakse vanad lilled, lisatakse uusi, joonistatakse taust mälupildi põhjaks ning lilled ükshaaval sellele. void koostaPilt(){ if(taust==null)taust=laePilt("rohetaust320x480.gif"); if(lill==null)lill=laePilt("lill1.gif"); if(pilt==null){ pilt=createImage(laius, korgus); piltg=pilt.getGraphics(); } nihe=nihe+samm; aeg=aeg+samm; if(nihe>mustripikkus)nihe=nihe-mustripikkus; eemaldaVanadLilled(); lisaUusiLilli(); piltg.drawImage(taust, 0, nihe-mustripikkus, this); joonistaLilled(); } Lillede joonistamisel käiakse lihtsalt läbi kõik vektoris olevad lilled ning paigutatakse nende andmete järgi lillepilt taustapildile. Arvutus aeg-lille algaeg näitab lille asukoha ekraanil. Lisaks võetakse maha veel lillekõrgus, et lill asuks nähtavasse alasse sisenema oma alumise poolega ja mitte ei tekiks järsku tervikuna inimese vaatevälja. void joonistaLilled(){ for(int i=0; imustripikkus)nihe=nihe-mustripikkus; eemaldaVanadLilled(); lisaUusiLilli(); piltg.drawImage(taust, 0, nihe-mustripikkus, this); joonistaLilled(); } void lisaUusiLilli(){ for(double lt=lillelisamistoenaosus; lt>0; lt=lt-1){ if(Math.random()korgus+lillekorgus){ lillekesed.removeElementAt(i); i--; } } } void joonistaLilled(){ for(int i=0; iputukasamm))putukax-=putukasamm; if((kood==KeyEvent.VK_RIGHT) && (putukaxputukasamm)putukay-=putukasamm; if(kood==KeyEvent.VK_DOWN && putukaymustripikkus)nihe=nihe-mustripikkus; piltg.drawImage(taust, 0, nihe-mustripikkus, this); piltg.drawImage(putukas, putukax, putukay, this); } public void start(){ veel=true; new Thread(this).start(); } public void run(){ while(veel){ repaint(); try{Thread.sleep(paus); }catch(Exception e){} } } public void stop(){ veel=false; } public void keyPressed(KeyEvent e){ int kood=e.getKeyCode(); if((kood==KeyEvent.VK_LEFT) && (putukax>putukasamm))putukax-=putukasamm; if((kood==KeyEvent.VK_RIGHT) && (putukaxputukasamm)putukay-=putukasamm; if(kood==KeyEvent.VK_DOWN && putukaymustripikkus)nihe=nihe-mustripikkus; eemaldaVanadLilled(); lisaUusiLilli(); kontrolliImemisi(); piltg.drawImage(taust, 0, nihe-mustripikkus, this); joonistaLilled(); piltg.drawImage(putukas, putukax, putukay, this); } void lisaUusiLilli(){ for(double lt=lillelisamistoenaosus; lt>0; lt=lt-1){ if(Math.random()korgus+lillekorgus){ lillekesed.removeElementAt(i); i--; } } } void kontrolliImemisi(){ int kauguseruut=imemiskaugus*imemiskaugus; for(int i=0; iputukasamm))putukax-=putukasamm; if((kood==KeyEvent.VK_RIGHT) && (putukaxputukasamm)putukay-=putukasamm; if(kood==KeyEvent.VK_DOWN && putukay=0 && x=0 && ymustripikkus)nihe=nihe-mustripikkus; if(sees(putukax+putukaxkiirus, putukay+putukaykiirus, laius-putukalaius, korgus-putukakorgus)){ putukax+=putukaxkiirus; putukay+=putukaykiirus; } else { putukaxkiirus=putukaykiirus=0; } } Reageering klahvivajutusele on mõnevõrra muutunud. Kui ennist muutus vajutusel putuka asukoht, siis nüüd püütakse muuta ta liikumise kiirust. Näitena näha vasak nooleklahv. Kui putukas liikus ennist vasakule (st. putukaxkiirus oli nullist väiksem) sel juhul klahvile vajutades vasakule liikumise kiirus kasvab kiirusesammu võrra, ehk kiirusest lahutatakse kiirusesammu väärtus. Kui aga putukas juhtus enne paigal seisma või paremale liikuma, siis määratakse uueks kiiruseks -kiirusesamm, ehk sama palju, kui muidu iga vajutamisega kiirust juurde tuleb. Nõnda ei pea kiiruse suuna muutmisel liialt aega kulutama pidurdamisele, vaid võib küllalt järsku teises suunas liikuma hakata, nagu putuka lennu puhul ikka näha võib. public void keyPressed(KeyEvent e){ int kood=e.getKeyCode(); if(kood==KeyEvent.VK_LEFT){ if(putukaxkiirus<0)putukaxkiirus-=kiirusesamm; else putukaxkiirus=-kiirusesamm; } //... } Juurde tuli mõningane statistika, et kasutajal oleks näha, kaugele ta mänguga jõudnud on. Kulunud aja arvutamiseks küsitakse käivitamise ajal eraldi muutujasse alghetke väärtus. Tegemist küllalt suure ja väheütleva arvuga: millisekundeid alates aastast 1970. long alghetk=new Date().getTime(); Kui nüüd aga millalgi uuesti süsteemi käest aeg küsida, siis nende kahe väärtuse vahe ütleb, palju mängu algusest aega kulunud on. void joonistaStatistika(){ String st="Lilli: "+lilli+" Imetud: "+imetud+ " Kadunud: "+kadunud+" Aeg: "+(new Date().getTime()-alghetk)/1000; piltg.setColor(Color.white); piltg.drawString(st, 30, korgus-10); } Rakendusele pandi kaasa heli. Pidevalt korduv linnutaust, et veidigi tekiks mulje niidul lendavast putukast ning märku andev sahin, kui putukas lillest nektari kätte sai. Taustaga on lihtsam. Kui esimest korda joonistama asutakse, siis palutakse muutujasse laadida linnutaust ning käsu loop abil pannakse too korduvalt end ketrama. if(linnutaust==null){ linnutaust=laeKlipp("linnutaust.au"); linnutaust.loop(); } Lille tabamise sahina saaks ka lihtsamal juhul laadida ning käsuga play mängima panna. Kui aga lilli palju ning sahinaheli vähegi pikem, siis kipuvad järjestikustel tabamustel helid kattuma ning üksikud tabamused ei eristu kõrvale ilusti ja loendatavalt. Sahinahelide eristamiseks loodi omaette klass. Lähem kirjeldus näha juba kommentaarides. /** * Lõimeklass, mille abil saab meloodiajuppe mängida. Sünkroniseerimise abil hoolitsetakse, * et mängualguste vahel oleks vähemalt poolesekundiline paus. Luku * (loa)ga sünkroniseeritud plokki pääseb korraga vaid üks lõim. Ülejäänud * peavad selle ees ootama, kuni eelmine on plokist väljunud. */ class Piiksuja extends Thread{ static Object lukk=new Object(); static java.applet.AudioClip sahin; public void run(){ synchronized(lukk){ sahin.play(); try{Thread.sleep(500);}catch(Exception e){} } } } Nagu näha, on Piiksuja nii lukk kui sahin staatilised muutujad. See tähendab, et neile on võimalik ligi pääseda sõltumata klassi eksemplaride arvust. Lukuobjekt luuakse programmi käivitamisel ning sama isend on kättesaadav kõigile klassi isenditele - nõndamoodi saab selle järgi sünkroniseerimisel otsustada, et korraga ei juhtuks mitu sahinat mängima. Piiksuja sahin laetakse koos muude klippidega ja paigutatakse sinna staatilisse muutujasse. if(Piiksuja.sahin==null) Piiksuja.sahin=laeKlipp("sahin.au"); Kui nüüd käivitamise juures jõutakse niikaugele, et on paras aeg sahistada, siis luuakse lõimeklassist Piiksuja uus eksemplar ning palutakse start abil eraldi lõimes ta run käivitada. if(!l.kasTyhi() && (xkaugus*xkaugus+ykaugus*ykaugus)mustripikkus)nihe=nihe-mustripikkus; if(sees(putukax+putukaxkiirus, putukay+putukaykiirus, laius-putukalaius, korgus-putukakorgus)){ putukax+=putukaxkiirus; putukay+=putukaykiirus; } else { putukaxkiirus=putukaykiirus=0; } } void koostaPilt(){ if(taust==null)laeKlipid(); arvutaAsukohad(); eemaldaVanadLilled(); lisaUusiLilli(); kontrolliImemisi(); piltg.drawImage(taust, 0, (int)nihe-mustripikkus, this); joonistaLilled(); joonistaStatistika(); piltg.drawImage(putukas, (int)putukax, (int)putukay, this); } void lisaUusiLilli(){ for(double lt=lillelisamistoenaosus; lt>0; lt=lt-1){ if(Math.random()korgus+lillekorgus){ if(!l.tyhi)kadunud++; lillekesed.removeElementAt(i); i--; } } } void kontrolliImemisi(){ int kauguseruut=imemiskaugus*imemiskaugus; for(int i=0; i=0 && x=0 && y0)putukaxkiirus+=kiirusesamm; else putukaxkiirus=kiirusesamm; } if(kood==KeyEvent.VK_UP){ if(putukaykiirus<0)putukaykiirus-=kiirusesamm; else putukaykiirus=-kiirusesamm; } if(kood==KeyEvent.VK_DOWN){ if(putukaykiirus>0)putukaykiirus+=kiirusesamm; else putukaykiirus=kiirusesamm; } } public void keyReleased(KeyEvent e){} public void keyTyped(KeyEvent e){} Image laePilt(String failinimi){ try{ return getImage(getCodeBase(), failinimi); }catch(Exception e){} return Toolkit.getDefaultToolkit().getImage(failinimi); } AudioClip laeKlipp(String failinimi){ return getAudioClip(getDocumentBase(), failinimi); } } Esimesed lilled Kaks imetud, kaks silmist mööda läinud Tihedaks määratud lilleväli Edasiarendusvõimalused Loodud rakendus küll töötab ning on mõningase näpuharjutusena mängitav, kuid pole ta veel ei terviklik mäng ega saa seda kuigivõrd asjalikult ka mõneks muuks otstarbeks kasutada. Olemasolevat koodi vähemalt osalt mõistes on võimalik küllalt vähese vaeva abil koodi toimimist ja rakenduse väljanägemist täiesti märkimisväärselt muuta. Lihtsaimaks asenduseks ehk pildid ja heliklipid. Kui joonistada või leida muru ja lillede abil miskit muud, siis annab kergesti kokku panna koristaja maanteel või elevandi portselanikaupluses. Üks üliõpilane aga kujundas piltide muutmise abil sellest samast näitest aga näiteks küllaltki verise sõjamängu. Kui näidet mänguks kujundada, siis tõenäoliselt tuleb üsna pea lisada punktidearvutus ja mängu lõpp, ehk ka tasemed. Punkte kannatab kuvada statistikafunktsiooni veidi muutes. Iseseisva rakenduse puhul annab vahetulemusi faili salvestada. Rakendi puhul on turvapiirangute tõttu see veidi keerulisem, kuid kel PHP või muu veebiserveripoolse programmeerimisvahendiga kokkupuuteid ja kasutusvõimalusi, siis on ka see täiesti tehtav. Ilus võistlusmoment tekib, kui ühe putuka asemel lendleb kaks, kummagi juhtimiseks omaette klahvid. Ning samuti võib saabuvaid pilte olla mitut tüüpi. Nii magusaid mesikaid, õiteta okaspuid kui muude vahele ära eksinud kärbsepabereid. Näide ei pea aga mitte ainult mängu aluseks olema. Mõningase muutmise teel saab selle põhjal koostada katseseadme pidurdusteekonna pikkuse või molekulide kaootilise liikumise näitamiseks. Kõige rohkem läheb vaja pealehakkamist. Ülesandeid Sirelasemäng Alustuseks on kasutada mängu põhi Tutvu mänguga Lae lähtekood, pildid ja helilõigud oma arvutisse, kompileeri ja käivita. Otsi Internetist pilte ning kujunda nende abil mäng oma silma järgi. Lisa punktidearvutus. Loo mängule lõpp. Luba samaaegselt liikuda kahel mängijal Klahvidega liigutamine. Nooleklahvidega saab liigutada ekraanil olevat ringi. Ekraanil juhuslikus kohas paikneb rist. Jõudes ringiga selleni, hüppab rist uude juhuslikku kohta. Liigutatavaid ringe on kaks, kummalgi oma klahvid. Risti asemel on pisike pilt. All servas on kirjas, mitu korda kumbki mängija on ristini jõudnud. Tennis Võrguga tenniseväljakul liigub pall (ring) vasakult paremale. Samaaegselt ringi liikumisega saab ekraanil liigutada reketit. Kummalgi serval on liigutatav reket, mille tabamisel pall tagasi põrkab. Loetakse punkte. Ussimäng 10*10 ruudustikul asub vasakul ülaservas neljalüliline uss, keda saab nooleklahviga paremale liigutada. Uss liigub pidevalt, nooleklahvidega saab tema liikumise suunda määrata. Seinani jõudmisel uss seiskub. Ussiga püütakse juhuslikus kohas asuvat kuldmuna. Muna kättesaamisel uss pikeneb kahe lüli võrra ning muna tekib uude kohta. Seina või enese hammustamisel uss lüheneb nelja lüli võrra. Tilgapüüdja All servas saab liigutada kaussi Ekraanil kukub tilk. Selle kaussi püüdmisel saab punkti. Tilku võib korraga kukkuda mitu. Mäng on meeldivalt kujundatud ning töötab märgatavate vigadeta. Nooltega ralli. Noolte abil saab muuta ekraanil liikuva ringi kiirust. Külgmiste nooltega saab muuta liikumise suunda, teiste nooltega kiirust. Korraga võib üle võrgu sõita kaks kasutajat. Aetakse taga juhuslikus kohas paiknevat aaret, mis selleni jõudmisel hüppab uude kohta.