Göm menyn

TDDE10 Objektorienterad programmering i Java

Textbaserat Spel - OOP i java

Tips innan du startar labben

Laborationens bakgrund och återkoppling från tidigare års studenter

Detta är en stor lab. Vi vet att studenter historiskt lagt väldigt mycket tid på denna laboration: både före redovisning och efter med åtskilliga stora kompletteringar.

Det är delvis för att den pedagogiska grundprincipen för laborationen är att man får utforska vad som kan göras och sedan får återkoppling av assistent. Vi vill att ni skall komma in i ett nytt objektorienterat tänk.

Vi erbjuder också ett stabilt tips: Om man inte gör detta på ett objektorienterat sätt, så tar det mycket längre tid, eftersom varje tillägg i koden medför många förändringar på många ställen. Om man istället gör snygga klasser, med bra inkapsling och som utnyttjar arv och polymorfi till sin fördel så har man igen detta grundarbete senare, och tillägg/förändringar i koden går mycket snabbt att implementera. Det är detta som är den stora poängen men laborationen.

Alltså, om ni kommer till ett läge där ni känner "GUUU va mycke det är! Aja, vi köttar på och lägger in fem if-satser till här" så är ni troligtvis inte rätt ute. Ta då ett par steg tillbaka, reflektera över hur ni delar upp problemet i klasser och vilken klass som får anvsara för vad. Ibland behöver man ta ett par steg tillbaka för att sedan kunna köra vidare frammåt. Detta kallas med ett finare ord för "refaktorering".

Andra tips:

  • Läs igenom hela uppgiften innan ni börjar. Försök att få ett tydlig och klar bild vad ni ska göra och skapa er en idé om hur ni kan åstadkomma detta.
  • Rita! Rita hur ni vill att er karta ska se ut och vilka platser som hänger ihop. Skapa bilder att referera till senare.
  • Välj ett eget tema på uppgiften. Är temat roligt för er kommer det bli roligare att implementera det senare.

Inledning

Börja självklart med att läsa igenom hela laborationshandledningen så att du får en översikt över laborationens struktur och omfång och vad som ska göras och hur det ska redovisas.

Laborationen går ut på att lära sig grunderna för objektorienterad programmering, samt motsvarande språkkonstruktioner i Java.

Laborationen består av olika steg där du får öva på bl.a. följande begrepp inom OOP:

Följande begrepp inom OOP introduceras:

  • Klasser och instanser
  • Konstruktorer
  • Arv
  • Polymorfism
  • Instansvariabler och klassvariabler (static-variabler)
  • Metoder och klassmetoder (static-metoder)
  • Inkapsling och "information hiding" (skydda implementationen)
  • Skyddsnivåerna private, protected, public
  • Ansvar, (d.v.s. vilken klass skall göra vad?)

Problembeskrivning

Året är 1989 och företaget PCGamez har anställt dig för att skapa ett äventyrsspel. Vad detta innebär (kom ihåg att detta är 1989) är ett datorspel där en spelare manövrerar runt i en påhittad värld, träffar på saker, människor, monster och andra underliga ting, via ett textuellt gränssnitt. Spelet går ut på att försöka ta sig till en specifik plats och/eller utföra en särskild handling och/eller ha ett visst (antal) föremål. Några mer kända sådana äventyrsspelen är Adventure och Zork.

Till din hjälp har du från PCGamez fått ett körexempel, ett antal krav på spelet och ett UML-diagram (klassdiagram) som visar ett förslag på några klasser som behövs och hur de kan förhålla sig till varandra. För att du inte skall behöva göra allt på en gång är laborationen uppdelad i några mindre delar. Även klassdiagrammet är uppdelat så att du inte behöver titta på allt på en gång. Det fullständiga diagrammet finns alltså i del C. Kravlistan är uppdelad på laborationens olika delar. De krav som är relevanta för varje del står där. Här är några generella krav (och tips) som gäller för alla delar:

Generella Krav:

  • Alla klasser skall ha konstruktorer som tar lämpliga parametrar (data som man inte borde få skapa objektet utan).
  • Alla klasser bör ha sina instansvariabler privat. Det behövs goda själ för att t.ex. sätta instansvariabler till public eller protected. Om data behöver kommas åt eller ändras utifrån klassen bör detta göras via s.k. getters resp. setters.
  • Deklarera variabler så lokalt som möjligt. Variabler som bara behöver finnas inuti en metod skall deklareras där, inte vara instans-/klassvariabler.
  • När det gäller arv och klasshirarkier, fundera noga över vad som är gemensamt för subklasserna och lägg detta i superklassen. Missar man detta så får man ofta kodduplicering.
  • Stora if- eller switch-satser indikerar ofta att man inte tänker på ett objektorienterat sätt. Skapa istället individuella klasser för det som skall variera och uppnå olika beteende m.h.a. polymorfi. Därmed är det inte förbjudet att använda if-satser, de kommer att behövas på många ställen.
  • Fundera på vad som behöver vara en egen klass och vad som endast kan vara instanser av en generell klass. Om du får många klasser som är exakt lika, bortsätt från vilka data de lagrar så kan du ju använda en gemensam klass, som du instansierar med olika data.
  • Om en klass börjar bli för stor så kan det vara för att den har för mycket ansvar. Dela upp i mindre klasser och låt dem ansvara för mindre delar.
  • Vissa klasser skall vara abstrakta, vilka då? Vad innebär det? Varför just dessa klasser?
  • Tänk över huruvida en metod skall vara en klassmetod (d.v.s static) eller inte. Metoder som inte behöver en instans av klassen de befinner sig i görs med fördel till klassmetoder. (Detta är dock inte normalfallet i objektorienterad programmering, då man nästan alltid "befinner sig i" ett objekt.)
  • Tänk på huruvida en variabel skall vara en klassvariabel (d.v.s static) eller inte. Tillhör den ett specifikt objekt, eller hela klassen?

Del A - Grundläggande funktionalitet (Game, Player, Location, o.s.v.)

Första delen av UML-diagrammet:


Tänk på att detta är ett förslag från PCGamez. Du kanske inte behöver alla egenskaper hos dessa klasser och det kommer förmodligen att behövas många fler metoder och variabler/parametrar än vad som ritats ut ovan (och eventuellt även fler klasser).

I denna första del av laborationen skall du implementera klasserna Game, Player, Location (och dess subklasser). Efter att denna del är klar skall man kunna spela spelet. Man kommer dock endast att kunna gå mellan olika platser.

Viktigt! Välj ett tema för spelet som passar er labbgrupp. Om det är roligt att arbeta med laborationen går det lättare att lära sig, och snabbare att komma framåt.

Exempel på hur spelet kan se ut när man spelar det:

(Det som matats in av användaren är i fet och kursiverad stil.)
      Welcome to the adventure game!  What is your
      name? Nathalie

      Hello Nathalie, welcome to this magical world of wonder! You can move
      around by typing north/south/west/east. You will have to learn more
      commands as you play the game! (Hint: there is a command "help").

      You are standing in the middle of a plain. It seems to go on for miles
      and miles. You cannot remember how you got here and you have nothing
      but the clothes on you. Way out in the distance to the north you see
      some mountains. Over to the east you see some trees. The sun is pale
      and a cool breeze caresses your skin.  There is a road leading north.
      There is a road leading east.  What do you want to
      do? east

      You wander into what seems to be an old forest of oak trees. Something
      unnerves you as you tip toe forward, trying not to stir whatever is
      resting here. Suddenly you are in a small clearing and there is a big
      boar in front of you! The boar is blocking the path.  There is a road
      leading west.  What do you want to do? west

      You are out on the plain again.  There is a road leading north.  There
      is a road leading east.  What do you want to do?
    

Kravlista

Funktionella krav (krav på funktionalitet):
  • Det skall finnas åtminstone fem platser som världen består av. Ha åtminstone en av vardera typen som visas i klassdiagrammet, men du får skapa egna subklasser.
  • Spelaren skall ha ett namn som får väljas i början av spelet.
  • Se till att det finns ett kommando "help" som ger spelaren lite anvisningar om hur man spelar.
  • Spelaren skall hålla reda på sin nuvarande plats och kunna gå mellan olika platser (m.h.a. kommando "north"/"west"/"south"/"east").
  • Spelet skall inte krascha om man försöker gå åt ett otillåtet håll.
  • Alla platser (Location-objekt) har en längre och en kortare beskrivning. Den längre beskrivningen används bara första gången platsen besöks.
  • Det skall finnas två olika typer av platser, rum och utomhusplatser. Skillnaden är att rum beskriver kopplingen till andra platser som dörrar medan utomhusplatser kallar sina kopplingar för vägar. Dessutom har utomhusplatser väder som kan ändras under spelets gång.
Icke-funktionella krav (krav på kodens uppbyggnad):
  • Klassen Game skall skapa världen i sin konstruktor.
  • Game-objektet skall hålla reda på spelaren, och ha en samling med alla spelets Location-objekt.
  • Klassen Game skall ha en metod run som innehåller själva spel-loopen.
  • Alla platser kan ha upp till fyra kopplingar till andra platser.
  • Platser skall ha en metod describeYourself låter platsobjekten själva bestämma hur de skall presenteras. Denna metod kan överskuggas i alla objekt som ärver från klassen Location.
  • Platser, Game och Spelaren har även metoden doCommand som låter objekten själva bestämma vad som händer då användaren använder ett visst kommando. Tänk noga över vilka parametrar dessa metoder kan behöva, det kommer inte räcka med bara det som står i diagrammet.
  • Det skall vara mycket enkelt att för någon som inte är insatt i resten av koden att lägga till nya platser och till och med nya typer av platser, (t.ex. Rum med dolda dörrar). Optimalt sett bör man bara behöva ärva från rätt klass och sedan göra ett tillägg i den befintliga koden. Om ni behöver ändra i många filer (ofta i stora if-satser) för att lägga till ny funktionalitet tyder detta på att ni inte har löst det på ett objektorienterat sätt.

Kodskelett

Nedan finns ett kodskelett att utgå ifrån. Ni kommer att behöva göra ändringar så att det bättre passar just ert spel, och ni kommer garanterat att behöva lägga till kod, men det bör underlätta för er så att ni kommer igång snabbare. Ni kan kopiera koden och klistra in den. Obs! Det saknas klasser för att kunna kompilera och testköra. Tanken är inte att detta kodskelett ska vara körbart, utan bara att ge er en start att utgå ifrån. Main.java

 public class Main {
	public static void main(String[] args) {
	Game g = new Game();
	g.run();
	}
	}
    

Game.java

import java.util.*;

public class Game {
    private Scanner keyboard; 
    private ArrayList<Location> locations;
    private Player player;
    
    public Game() {
	    keyboard = new Scanner(System.in);
	    locations = new ArrayList<>();
	    locations.add(new Location("Starting location"));
    }

    public void run() {
	String name;
	    
        System.out.println("Welcome to the adventure game!\nWhat is your name?");
	name = keyboard.nextLine();
	player = new Player(name, locations.get(0));
	System.out.println("Hello " + name + ", welcome to this magical world of wonder!"
	+ " You can move around by typing north/south/west/east."
	+ " You will have to learn more commands as you play the game!"
	+ " (Hint: there is a command \"help\").");
	
	while (true) {
            String command;
	    
	    player.getLocation().describeYourself();
	    System.out.println("What do you want to do?");
	    command = keyboard.nextLine();
	    player.doCommand(command);
	}	    
    }
}
    

TIPS: I klassen String finns en trevlig metod split(), som man kan använda för att dela upp en sträng i separata ord. Detta kan t.ex. vara trevligt att göra med det indata som man får från användaren. För mer information, se dokumentationen för String.

Del B - Föremål (Item, Tool, WearableItem)

Du skall nu lägga till föremål i spelet. Föremålen skall kunna ligga på olika platser och plockas upp, och användas av spelaren. Vissa föremål kan användas aktivt, d.v.s. de tillåter spelaren att utföra någon specifik handling (t.ex. med en spade kan man gräva). Andra föremål har en passiv effekt (t.ex. en viss dörr kan man bara gå igenom om man har nyckeln). Försök att (så mycket det går) låta föremålen själva (de klasserna) bestämma hur de skall fungera, så blir spelet så flexibelt som möjligt för senare tillägg.

Körexempel

      What do you want to do? items

      You have the following items: shovel (2.1 kg) elven_robe (3 kg) torch
      (1 kg) What do you want to do? wear elven_robe

      You put on the elven_robe.  What do you want to do? dig

      You dig a hole in the ground, but you find nothing here.  What do you
      want to do? look

      You are out on the plain again.  There is a road leading north.  There
      is a road leading east.  What do you want to do? north

      You come to a cave in the mountains. Your torch shines red light on
      the stone walls. You can now see that there is a path at the back of
      the cave, but you have no idea where it leads. There is a bone from a
      large animal on the floor.  There is a door leading west.  What do you
      want to do? take bone

      You picked up the bone.  What do you want to do?
    

Kravlista

Funktionella krav:
  • WearableItems kan man ta på sig och ger oftast någon effekt (mer hälsa, skydd av något slag, vissa NPC:er beter sig annorlunda). Man skall kunna ta på sig saker med kommandot "wear".
  • Normalt sett skall alla föremål som finns på en plats skrivas ut när spelaren besöker den platsen.
  • För att användaren skall kunna veta vilka föremål den har ska den kunna skriva "items", då skall spelet lista dem, och vad man har på sig för tillfället.
  • När det nu börjar bli lite mer att göra för spelaren så kan det vara bra för den att ha ett sätt att åter få reda på vad som fanns på platsen (vägar/dörrar, saker i rummet), lägg därför till kommandot "look" som skriver ut dessa saker igen.
  • När man vill plocka upp ett föremål som ligger i ett rum skall man använda kommandot "take".
  • Det skall finnas minst fem föremål i spelet. Vi rekommenderar shovel (som man kan gräva och hitta saker med), elven_robe (som man kan ta på sig), och torch (som lyser upp rum som annars är mörka). Hitta på två helt egna föremål också.
  • Du kanske kommer fram till att du behöver ändra lite i tidigare klasser. Hur ser du till att framtida tillägg kommer kräva minimal (eller ingen) förändring i den nuvarande koden?
Icke-Funktionella krav:
  • Klassen Item skall vara en superklass som alla andra föremål ärver ifrån. Den innehåller gemensamma attribut för alla föremål (namn, vikt och pris).
  • Item skall ha en metod doCommand som definierar vad föremålet kan göra (eller tillåta att spelaren gör). Tänk noga över vilka parametrar den metoden behöver.
  • Det skall finnas minst två subklasser till Item: Tool och WearableItem. (Weapon implementeras i del C).
  • Tool är klassen (eller snarare superklassen) för diverse verktyg, t.ex. spaden. Tools definierar själva vilket kommando man behöver skriva för att använda dem i metoden use. Det är därför inte konstigt om alla Tools blir egna klasser, som ärver av klassen Tool.
  • WearableItem är klassen (eller snarare superklassen) för diverse kläder, t.ex. elven_robe. WearableItems definierar själva vad som händer när man anropar metoden putOn(). Det är därför inte konstigt om alla föremål blir egna klasser, som ärver av klassen WearbleItem.

Tänk på att detta är ett förslag från PCGamez (för del A och del B). Det kommer förmodligen att behövas många fler metoder och variabler/parametrar än vad som ritats ut ovan (och fler klasser också).

Del C (Frivillig men bonusgivande) - Varelser (NPC, Monster, Person, Weapon)

Ditt spel är fortfarande ganska tråkigt. För att råda bot på detta skall fler features läggas till. Du skall nu lägga till varelser. Visa varelser kommer kunna hjälpa spelaren att komma vidare i spelet, andra är där som utmaningar som spelaren måste överkomma. I vissa fall kan spelaren behöva använda eventuella vapen som den har samlat på sig sedan tidigare.
  You are in the oak woods, in the clearing.  There is a big boar in
  front of you! The boar is blocking the path.  There is a road leading
  west.  What do you want to do? kill boar

  The boar charges straight at you! Luckily you have your spear, you
  would not have survived the attack without it. Now that the boar is
  gone you see another path in front of you to the east.  What do you
  want to do? east

  You arrive at a small hut at the edge of the forest. In front of the
  hut is a small old man sitting on the ground. He appears to be begging
  as he has a bowl in front of him, with a few coins therein.  There is
  a road leading west.  There is a road leading south.  There is a road
  leading north.  What do you want to do? give coins

  The man's bushy eyebrows are raised and you can see the wisdom in his
  aged eyes. He tells you "Nathalie, seek the altar on the mountain
  summit, and place this gem thereupon. You will then find your true
  purpose!".  You got strange_gem from beggar.

  What do you want to do?

Kravlista

Funktionella krav:
  • Normalt sett skall spelet alltid skriva ut vilka NPC:er som finns på en plats då spelaren besöker platsen.
  • Det skall finnas minst tre NPC:er i spelet. Vi rekommenderar vildsvinet (boar), beggar och en egenpåhittad NPC till spelet.
Icke-Funktionella krav:
  • Klassen NPC skall vara en superklass som alla andra varelser ärver ifrån. Den innehåller gemensamma attribut för alla föremål (namn, hälsa och föremål).
  • Det skall åtminstone finnas två typer av NPC:er: Monster och Person.
  • Monsterklassen skall ha en metod attack som bestämmer vad som händer då spelaren attackerar monstret (eller tvärt om).
  • Personklassen har istället metoden interactWith som bestämmer vad som händer då spelaren interagerar med personen.
  • Lägg till klassen weapon, och instansen spear, så att spelaren har en chans mot vildsvinet.

Tänk på att detta är ett förslag från PCGamez (för del A-C). Det kommer förmodligen att behövas många fler metoder och variabler/parametrar än vad som ritats ut ovan. NPC står för non-player character och representerar varelser som inte kontrolleras av spelaren.

Del D (Frivillig men bonusgivande) - Finishing Touches

Ditt spel är nu så pass bra att du nästan kan sälja det och tjäna storkovan, åtminstone tills Doom kommer ut. Det som behövs är ett par "finishing touches".

För att få Del D:s bonus krävs det att ni lagt in tre eller fler av de förslag som finns i listan nedan. Har ni egna liknande ideér på saker som bör ge bonus måste ni få dessa godkända av Emma eller Magnus innan ni gör dem.

I er Game-klass ska också en javadoc-kommentar skapas som beskriver vilka tre ni valt och i vilken klass de finns.

Förslag på "finishing touches"

  • En häftig feature är att även låta NPC:erna få agera i spelet. Lägg till i spelets huvudloop ett steg där en metod hos NPC:erna anropas där de själva kan bestämma vad de skall göra.
  • En annan typ av NPC man kan lägga till är en som rör sig mellan spelets platser. Detta kan antingen vara genom en interaktion med spelaren eller så kan den röra sig av sig själv.
  • Skapa en eller fler hemliga platser som man endast kan ta sig till genom en hemlig dörr eller genom att låsa upp den genom att spela spelet.
  • Gör så att man kan sälja och köpa items i utbyte mot guld. Detta kan vara genom en försäljnings-NPC eller något annat smart ni kommer på.
  • Uppgradera ert inventory så att det är vikt-känsligt. Man kan bara ha med sig items upp till en specifik vikt och går vikten över detta måste man kasta ett item för att plocka upp ett annat.
  • Uppgradera vädret så att detta förändras under spelets gång.
  • Uppgradera rummens storlek så att viss funktionalitet enbart kan ske i vissa storlekar på rum. Detta kan vara att man till exempel bara kan träna svärdfäktning i stora rum, eller bara sova i små rum.
  • Uppgradera items så att de endast kan användas ett antal gånger.
  • Lägg till funktionalitet i Player. Detta kan vara utseende som påverkar interaktioner med NPC:er, eller kanske att spelaren har ett rykte som kan förändras under spelets gång.
  • Lägg till så att man kan vinna eller förlora spelet. Det kan t.ex. vara att ta sig till en specifik plats (som förmodligen inte är helt trivial att ta sig till), eller att man skall få tag i något visst föremål, eller besegra ett visst monster, eller en kombination av ovanstående. På liknande sätt kanske man förlorar om dör mot monster. Vilken klass bör ansvara för att hålla reda på vad spelaren har utfört?

Sidansvarig: Magnus Nielsen
Senast uppdaterad: 2024-01-10