Universitetet i Tromsø Obligatorisk øving 3 INF 1400 Thomas Holden
Innledning Rapporten beskriver implementasjonen av en Mayhem klone. Spillet har støtte for to spillere, gravitasjon, score (antall drap / antall drept / antall gjenværende liv), refuling når man har brukt opp drivstoffet og aksellerasjon. Oppgaven stilte krav om gravitasjon, dette kravet er gjennomført med hjelp av en planet som roterer på midten av skjermen. Om spilleren kommer for nært planeten vil gravitasjonskreftene begynne å påvirke spilleren. Gravitasjonskraften er ikke fysisk korrekt, dvs, den følger ikke de fysiske lovene (9,8 ms)^2 men spillerene får en følelse av gravitasjon likevel. Implementasjonen er gjort i Linux, ved hjelp av programmeringsspråket Python 2.7. På denne datamaskinen heter pyton binæren «python2» (Ikke python, som er vanlig på noen av de store linux distrobusjonene), dermed må du endre env variabelet i game.py om du ønsker å kjøre spillet uten bruk av python / python2 kommandoen. Mere om dette i readme.txt Implementasjonen er gjort på Universitetet i Tromsø, som en obligatorisk innlevering i INF-1400. Planleggingsfasen Det måtte noe planlegging til for å få til denne implementasjonen. Blant annet var det viktig å følge kravet oppgaven stilte om å arve pygame.sprite.sprite for alle de synlige objektene på skjermen. Dette innkluderer planeten, to romskip og to «bensin stasjoner» / pads. Teksten øverst i spillvinduet er også surface objekter, men av typen pygame.font.font, og arver dermed ikke fra sprites klassen, men i fra fonts klassen. Et annet krav oppgaven stilte var at alt av oppdateringer (forflytning / gravitasjon osv) skulle gjøres fra objektenes update funksjon. Dette var for å lære om prinsippene bak polymorfisme. Det vil si å overskrive annen tilarvet funksjonalitet. I tillegg var det viktig å tenke ut hvordan klassene skulle henge sammen. Skulle man arve noe, eller skulle man heller bare lage objektene separat? Løsningen her baserte seg på å arve det som var kravene i oppgaven, og i tillegg til dette lage flere andre objekter. Blant annet, Score, Fuel og Ammo, som vil bli beskrevet senere i rapporten. UML diagram UML diagrammet skal gjøre det enklere å visualisere seg hvordan alt henger sammen. Klassen game, er hovedklassen. Herifra blir pygame startet, displaymode satt og en screen konfigurert. Innstillingene blir hentet fra config.py som var et av de andre kravene oppgaven stilte. Game klassen har en metode, createworld() som initialiserer de andre objektene som behøves for at spillet skal kjøre. Disse er følgende: Player, Planet og Fuelpads. Alle disse objektene er sprites, som blir lagt til i grupper slik at de kan oppdateres og tegnes på en enklere måte (også et oppgave krav).
Font objekter brukes til å skrive tekst til skjermen. Metoden getsurface returnerer en surface som kan blittes til skjermen. Dette skjer i game klassen, ved hjelp av screen.blit. Dette bildet viser hvilken filer programmet består av, og hvilken del av programmet som importerer deler av programmet. For eksempel, importerer game filene player, planets, font med flere. Konfigurasjonsfilen leses av flere deler av programmet, men ikke av alle. Grunnen til dette er at noen deler er avhengige av predefinerte konstanter, mens andre derimot, ikke er det. UML-en Vedlagt denne rapporten ligger det en fil, uml.png som innholder uml diagrammet. Bildet var for stort til å få med i rapporten, men her følger en beskrivelse av diagrammet. Game klassen instansierer objekter av typen player, planets og fuelpads. Player klassen instansierer ammo, fuel og score. Alle klassene som innholder kordinater benytter seg av Vector2D objekter for å regne ut de nye posisjonene. Det kommer ikke frem av UML-en, men player og planets arver fra pygame.sprite.sprite, og font arver fra pygame.font.font
Implementasjon Game Spillets hovedklasse, og har i oppgave å sette opp PyGame, en screen og instansiere resten av objektene som er nødvendig for at spillet skal kjøre. Game klassen består av følgende metoder: Createworld Collision GameStatus dokill setalive stats handleevents Createworld Metoden kjøres fra init, og har i oppgave å instansiere to player objekter, to fuelpadobjekter, og en planet (som obstacle). Alle disse objektene blir plassert i sprites grupper. Spillerne blir lagt til i en gruppe kalt players, planeten i en gruppe kalt planets osv. Disse gruppene blir oppdatert, og tegnet i fra while løkken som kjøres i init metoden til game. Collision Metoden tester for kollisjon mellom planeter og spaceships, og ammo og spaceships. Planeter og spaceship kollisjoner blir funnet om pygame.sprite.collide_rect returnerer True. Hvis returverdien er sann vil dokill bli kjørt på objektet som skal drepes. Dette innebærer et kall til game.dokill og et annet til self.dokill. Disse metodene resetter objektet, og gjør slik at neste runde kan starte. For kulene, er det litt annerledes. Her blir skuddene fra player1 og player2 lagt til i en bullets.sprite gruppe. Alle kulene innholder en «ident», slik at det er mulig å sjekke hvem som skjøt kulene, på denne måten er det mulig å oppdatere score, og i tillegg gjøre slik at det ikke er mulig å drepe seg selv. Når en av spillerne blir drept vil self.dokill og game.dokill kjøres på objektet som er drept, i tillegg vil self.reset kjøres på begge objektene (for å resette fuel for eksempel), self.score.incrementdeath vil kjøres for å telle antall dødsfall, og self.score.decrementlive for å telle ned på liven til spilleren som døde. I tillegg vil self.score.incrementkill kjøres på objektet som sto igjen i livet. Game status
Metoden holder styr på spillets status. Hvis noen av spillerne er døde gjør metoden det mulig å restarte spillet gjennom å lytte på en pygame.key event. Metoden skriver også «Game over» til skjermen om en av spillerne går tom for liv. DoKill Sjekker om den får rett argument ved hjelp av isinstance(who, Player). Om alt stemmer vil bullets gruppen bli tømt, og spilleren som skal drepes fjernes fra gruppen slik at den ikke lengre tegnes på skjermen. På denne måten vil det se ut som at spilleren dør. SetAlive Legger til spilleren i sprites gruppen slik at den kan bli tegnet på skjermen. Teller også ned på antall liv på spilleren. Stats Skriver statistikk til skjermen gjennom bruk av Font klassen (Arver fra pygame.font.font). Denne klassen innholder en metode som gjør det mulig å hente ut et pygame surface, som senere blir blittet til skjermen. Koordinatene står spesifisert i config.py Player Player klassen instansierer et ammo, et score og et fuel objekt. Disse skal holde styr på score, skudd og drivstoff forbruket til player objektet. Posisjonen til player blir gitt ved å benytte et Vector2D objekt, dette gjøres for enkelthetsskyld. Player klassen innholder metoder for reset, gravitasjon (rundt planet, og fuelpad), rotasjon, thrust, skyting, refuling, kill/alive, set status, og update. Update metoden kaller på thrust, rotasjon og skudd når det er nødvendig. Set status benyttes av dokill, og setalive metodene. Ammo Ammo objekter blir instansiert og lagt til i ei liste for hver gang player skyter. Et ammo objekt representerer et skudd og innholder en ident, en retning og en hastighet. Ammo objektet har kun en metode, update som legger til hastighet for hver iterasjon av main loopen. Fuel Fuel objektet blir instansiert fra player klassens init funksjon og holder styr på brennstofforbruket til player. Består av metoder for å brenne fuel, fylle opp drivstoff og sjekke hvor mye som er igjen på tanken.
Score Objektet holder styr på poengene til player, dvs, antall drap, antall død og gjenværende liv. En spiller begynner med tre liv og mister liv, enten ved hjelp av krasj i planet, eller som følge av skudd. Score består av metoder for å dekrementere liv og inkrementering av drap og død. I tillegg betsår objeket av metoder for å hente ut gjenværende liv, antall dødsfall og drap. Font Font klassen arver pygame.font.font og består av fontsize, fontname og text. Text er teksten som skal rendres, og fontsize er tekststørrelse og fontname er navnet på fonten som benyttes. Består av en metode, getsurface, som returnerer en pygame surface som kan blittes til skjermen. Planets Planets arver fra pygame.sprite.sprite, og er et objekt som brukes for å illustrere planeter på skjermen. I nåværende kodeform er det kun en planet, men det er mulig å legge til flere. Klassen fungerer på samme måte som player hva angår rotasjon, men ellers er den relativt funksjonsløs. Kollisjonstesting gjøres fra game.collision Fuelpads Arver fra pygame.sprite.sprite, og illustrere en fuelpad på skjermen. Denne klassen innholder kun data om selve posisjonen, fargen og lengden / bredden av objektet. Selve refulingen skjer automatisk når romskipet er innenfor en viss radius. Dette blir detektert fra player objektet. Cprofiler Python koden er kjørt gjennom cprofiler, men det var ingenting spessielt som stakk seg ut som tregt kjørende i denne koden. Dermed, er det ikke noen forbedringer å diskutere. Det som tok lengst tid var pygame.font.font. init () metoden, og det er ikke noe å gjøre med denne. Må kjøre init for at klassen skal fungere. I filen cprofiler.txt ligger output av profileringstesten. Problemstillinger Det oppsto egentlig ingen spesielle problemstillinger under programmeringen av denne obligatoriske øvingen. Det eneste var at det måtte en del planlegging til for å finne ut hvordan implementeringen skulle være. For å følge alle kravene etc. Dette var nok det vanskeligste i denne oppgaven. Tegning av UML diagram ble gjort automatisk ved bruk av pylint, tok noe tid å skjønne seg på hvordan dette
verktøyet fungerte, men etter dette gikk det greit. Konklusjon Koden fungerer slik som den skal, følger de kravene som er stilt i oppgaveteksten og kjører ikke med noen spesielle flaskehalser. Som tidligere nevnt, er det pygame.font.font. init () som bruker lengst tid under kjøring.