TDDE10 m.fl. Objektorienterad programmering i Java Föreläsning 9 Erik Nilsson, Institutionen för Datavetenskap, LiU På denna föreläsning: Projekt Omfattning, Upplägg Kravspecifikation Tips & Trix Spel-loop Buffra bilder Kollisionsdetektering 1
Projekt OOA workshop Upplägg Fö v7 Börja skriv KS KS godkänd Fö Redovisning v9 v8 Deadline KS Börja koda Handeldning vid behov! v10 Deadline Reflektion 2
Projekt (2) Omfattning Ett arkadspel skall göras! 1 Spelare mot spelet Grafiskt gränssnitt som styrs med tangentbord och mus. Flera banor, fiender, powerups o.s.v. Meny, med bl.a. highscore. (d.v.s. poäng) 3
Projekt (3) Resurser Grupper om 2 (samma som laborationerna) Handledare finns att tillgå Knappt 3 veckor kalendertid. Två föreläsningar, (Krav, Tips, Versionshantering) En lektion (OOA-workshop) Projektpass i datorsalarna (ej handledare). GitLab Projektet är i stort sett helt självständigt arbete. 4
Projekt(4) Exempel 5 Robotron, Missle Command, m.fl.
Projekt (5) Övriga krav (icke-funktionella) Skall vara skrivet i Java. Får använda annat grafiskt lib än Swing, men då kan man inte förvänta sig hjälp med det. Skall versionshanteras med git/gitlab Handledaren skall bjudas in till projektet. Åtminstone 2 moduler (paket). Lämpligt att dela på grafik och logik. Javadoc skall finnas på klasser (endast kort beskrivning av vad klassen representerar krävs). 6
Projekt (6) Redovisning Godkänd kravspec. Godkänd muntlig demo vid redovisningstillfället. Kraven uppfyllda. Koden OK. Godkänt reflektionsdokument (1 per grupp). 2-3 A4 om ert lärande under projektet. Översiktligt klassdiagram. 7
Projekt (7) Tävling! Vi utser årets projektarbete. Deltagare har chans att komma med i Hall of Fame, d.v.s. en gloriös poster. För att delta, skicka in: Era namn (eller alias) En screenshot från spelet En kort beskrivning av spelet. 8
Kravspecifikation Vad som skall ingå: Er vision, d.v.s. en löpande text om hur spelet skall vara. Ca 1 A4 text. En eller flera bilder (skisser) över hur spelet ser ut när man spelar, med förklarande texter. Helt OK att rita detta för hand. En tabell över konkreta, funktionella krav. Ca 15-25 st är rimligt. Skickas som PDF till handledaren senast kl 17.00 tisdagen den 20:e februari Handledaren ger er svar senast nästa dag. När kravspecen är godkänd så kan ni börja koda! 9
Kravspecifikation (2) Hur är en kravspec för mjukvara? Fokus för detta projekt. Beskriver funktionaliteten. Vad programmet skall göra. Beskriver gränssnitt(en). Hur programmet interagerar med omvärlden (människor såväl som andra system). Beskriver andra attribut, t.ex. Andra begränsningar för systemet, t.ex. Portabilitet, hållbarhet, säkerhet, o.s.v. Implementationsspråk, resursbegränsning, o.s.v. Går inte in på design eller implementationsdetaljer! 10 IEEE Std 830-1998: Recommended Practice for System Requirements Specifications
Kravspecifikation (3) Vilka krav behövs? Kraven arbetas fram tillsammans med kund och andra intressenter. Ofta används use-cases och scenarion även här, för att kunna identifiera systemets viktiga delar. I detta fall agerar ni både kund och utvecklare. Ni får alltså själva komma fram till vilka krav som behövs! Tips: Använd er vision och era skisser. Se även mini-kraven på kurshemsidan. Vi (handledare, examinator) finns med som yttre intressenter. 11 IEEE Std 830-1998: Recommended Practice for System Requirements Specifications
Kravspecifikation (4) Hur skriver man ett krav? Det finns vissa karaktäristika som är viktiga för krav. Ett krav skall vara: Korrekt Icke tvetydigt Om kravet inte skall gälla Ett krav får inte ha flera för programmet så är tolkningar. det inte korrekt. Termer kan behöva definieras i en ordlista. Konsistent Rankat Ett krav får inte motsäga ett annat krav, eller andra delar av kravspecen (t.ex visionen, skissen). Vissa krav är viktigare än andra. Alla krav bör ha något mått så att man vet vad som bör prioriteras. IEEE Std 830-1998: Recommended Practice for System Requirements Specifications Verifierbart Det måste gå att avgöra om kravet är uppfyllt. Spårbart Vi måste kunna referera till ett krav. Själva texten i ett krav kan ju ändras. Görs lättast med nummer. Icke redundant 12 Om ett krav överlappar med ett annat kan detta leda till fel senare...
Kravspecifikation (5) Det skall finnas åtta olika former i spelet. Spelaren skall kunna rotera en form genom att trycka på en tangent. Spelet skall visa upp spelarens nuvarande poäng. Ev. redun- Spelet skall visa vilken av de sju formerna som kommer härnäst. dant Formerna skall dyka upp i spelets underkant och röra sig nedåt. När en rad är komplett så skall den försvinna och spelaren får poäng för den. Former kan roteras med mellanslag så länge de inte har landat. Spelets grafiska design skall vara snygg och användarvänlig. Ej konsistent Tvetydigt Vad är komplett? Vad är en rad? Ej verifierbart Ej spårbara Vad är snyggt? Vad är användarvänligt? Ej korrekt IEEE Std 830-1998: Recommended Practice for System Requirements Specifications Ej rankade 13
Kravspecifikation (6) Minst innebär viktigast ID Beskrivning. sju olika former i spelet. 1 Det skall finnas åtta Spelaren skall kunna rotera en form genom att 2 trycka på en tangent. 3 Spelet skall visa upp spelarens nuvarande poäng. Spelet skall visa vilken av de sju formerna som Ev. redun4 kommer härnäst. dant Formerna skall dyka upp i spelets underkant överkant och 5 röra sig nedåt. När en rad är komplett så skall den försvinna och Se definitionerna för rad och 6 spelaren får poäng för den. komplett i ordlistan. Former kan roteras med mellanslag så länge de 7 inte har landat. Spelets grafiska design skall vara snygg och 8 Varje form skall ha en egen färg. användarvänlig. Prio 1 1 1 Ej konsistent 2 1 Tvetydigt Vad är komplett? Vad är en rad? 1 1 2 Ej verifierbart Ej spårbara Vad är snyggt? Vad är användarvänligt? Ej korrekt IEEE Std 830-1998: Recommended Practice for System Requirements Specifications Ej rankade 14
Kravspecifikation (6) Minst innebär viktigast ID Beskrivning. sju olika former i spelet. 1 Det skall finnas åtta Spelaren skall kunna rotera en form genom att 2 trycka på en tangent. 3 4 5 6 7 8 Spelet skall visa upp spelarens nuvarande poäng. Spelet skall visa vilken av de sju formerna som kommer härnäst. Formerna skall dyka upp i spelets underkant överkant och röra sig nedåt. När en rad är komplett så skall den försvinna och Se definitionerna för rad och spelaren får poäng för den. komplett i ordlistan. Former kan roteras med mellanslag så länge de inte har landat. Spelets grafiska design skall vara snygg och Varje form skall ha en egen färg. användarvänlig. Prio 1 1 1 2 1 1 1 2 15 IEEE Std 830-1998: Recommended Practice for System Requirements Specifications
Kravspecifikation (7) Övriga tips Låt åtminstone 3 av era krav vara lägre prioriterade så att ni har några extra saker att göra om ni blir klara tidigt. Om ni inte hinner med dessa så är det inte hela världen. Ni behöver inte fler än 3 prio-nivåer. Välj något välkänt spel och gör er egen variant av det. Ni hinner troligtvis inte att både designa ett nytt spel och implementera det. 16
Tips! Ritprogrammet (lab 3) är ju nästan ett spel... Ni kan säkert återanvända en del kod, eller åtminstone idéer från lab 3. Även här: jobba objektorienterat! Ni kommer behöva ändra mycket i koden medans ni jobbar. Använd det ni har lärt er! D.v.s. klasshirarkier, polymorfi, abstrakta klasser, inkapsling. 17
Spel-Loop De allra flesta spel har någon form av spel-loop. Generell form: while (speletejslut) { hanteraindata(); // t.ex. mus, tangentbord uppdateramodell(); // Alla objekt uppdateras ritautallt(); // Uppdateringarna visas för // spelaren väntaenkortstund(20ms); 18
Spel-Loop (2) Med swing behövs ev. inte denna, då indata kan hanteras av lyssnare och händelser. De allra flesta spel har någon form av spel-loop. Generell form: while (speletejslut) { hanteraindata(); uppdateramodell(); ritautallt(); väntaenkortstund(20ms); T.ex. Thread.sleep(20); Modellen går igenom alla sina objekt och talar om för dem att uppdatera sig själva. Ex: for (ModelObject mo : modelobjects) { mo.updateyourself(this); T.ex. repaint() eller paintimmediately() på GUIt Tips: Anropa Toolkit.getDefaultToolkit().sync(); direkt efter utritning om spelet verkar hacka. (Kan dock bero på 19 annat också).
Spel-Loop (3) Thread.sleep() kan variera ett par ms. Uppdatering av model, kan också ta variabel tid. Egentligen bör man mäta hur lång tid varje del i spel-loopen tar. Kan göras med System.currentTimeMillis() Därefter räknar man ut hur lång tid man skall vänta. Uppdatering av modellen bör då bero av tiden som gått sedan föregående uppdatering. 20
Spel-Loop (4) En annan approach är att låta uppdateringen också vara en händelse. I Swing finns en timer-klass just för detta ändamål! Timer t = new Timer(20, new ActionListener() { public void actionperformed(actionevent e) { uppdateramodell(); ritautallt(); Toolkit.getDefaultToolkit().sync(); ); t.start(); 21
Kort om trådar... Tänk på att inte blockera trådar. Låt händelser löpa ut. Ex: this.addmouselistener(new MouseAdapter(){ @Override public void mouseclicked(mouseevent arg0) { if (clickedonstartbutton(arg0)) { while (!gameover) { // do things Här blockeras händelsetråden och andra ); trådar får inte en chans att göra sitt. Medför att programmet verkar ha hängt sig...
Kort om trådar... Ett till exempel (denna gång i utritningstråden): @Override protected void paintcomponent(graphics g) { while (! wearedonehere) { // do stuff Här blockeras utritningstråden och andra trådar får inte en chans att göra sitt. Medför att programmet verkar ha hängt sig... Alltså: lägg spel-loopen I huvudprogrammet. Låt t.ex. er modell hålla reda på vilket tillstånd spelet är i
Buffra bilder Ni kommer säkert att vilja ha bilder, s.k. sprites i ert spel. Enklast är det nog att låta objekten själva rita ut dessa bilder: public void paintyourself(graphics g) { BufferedImage im = ImageIO.read( new File( sprites/bad.png )); g.drawimage(x, y, im, null); Men vad händer när vi har många objekt? Alla dessa skall läsa filen varje gång spelet skall ritas ut. Även en liten bild kan då ta tid. 24
Buffra bilder (2) Lösning 1: Spara bilden som en klassvariabel. private static BufferedImage image = null; private static BufferedImage getimage() { if (image == null) { ImageIO.read(new File( sprites/bad.png )); return image; public void paintyourself(graphics g) { g.drawimage(x, y, getimage(), null); 25
Buffra bilder (3) Lösning 2: Spara alla bilder i en separat manager-klass. Den klassen ansvararför att bilder läses in enn gång. Lämpligtvis sparar klassen undan dem i en map. public void paintyourself(graphics g) { g.drawimage(x, y, SpriteManager.getImage( bad.png ), null); 26
Buffra bilder (4) Andra tips om sprites Graphics-objektet kan rita ut bara en del av en bild. Kan vara praktiskt att använda sprite maps. Animationer kan vara en sprite map, där man tickar fram mellan bilderna. Graphics kan hantera transparens i bilder. 27
Kollisioner Ni kommer troligtvis behöva en del klasser för att sköta geometri i ert program. T.ex., hur vet vi om skeppet kolliderat med asteroiden? Finns många bra klasser i java.awt och java.awt.geom T.ex. Point2D.Double, för att hantera punkter i planet. Dimension, för att hantera storlek (bredd, höjd) Rectangle, för att representera en rektangel. Line2D, för att representera en linje. 28
Kollisioner (2) Lösning 1: Se objekten som rektanglar och kolla om dessa överlappar. rect2 rect1 Rectangle rect1 = new Rectangle(ship.getX(), ship.gety(), ship.getwidth(), ship.getheight()); Rectangle rect2 = new Rectangle(rock.getX(), rock.gety(), rock.getwidth(), rock.getheight()); if (rect1.intersects(rect2)) { // objekten har kolliderat! 29
Kollisioner (3) Finns även funktion för att se om en sträcka beskär en rektangel. rect2 (Xb, Yb) (Xa, Ya) Rectangle rect2 = new Rectangle(rock.getX(), rock.gety(), rock.getwidth(), rock.getheight()); Line2D line = new Line2D(Xa, Ya, Xb, Yb); if (rect1.intersects(line)) { // linjen beskär rektangeln. 30
Concurrent Modification Antag att vi i modellen (GameModel) har följande någonstans i en metod: for (GameObject obj : allobjects) { obj.doyourstuff(this); Och att vi även har denna metod i GameModel: public List<GameObject> getallobjects() { return allobjects; Vad händer om vi i doyourstuff gör detta: gamemodel.getallobjects().remove(...) 31
Concurrent Modification (2) Vi ändrar ju på en lista medans vi itererar igenom den Vägar runt detta: Kopiera över alla objekt till en annan lista, iterera över den men ta bort i orginalet. Markera objekt för borttagning. Samla objekt som skall tas bort I en skräplista, och ta bort dem efter. I båda de senare två fallen, ta bort elementen efter for-eachloopen. 32