Bookmark and Share

Svårighetsgrad
Svårighetsgrad
Betyg (6 röster)
BetygBetygBetygBetygBetyg
 

Multiplayer Tanks - Del 2

Grafik

Till vårt projekt behövs lite schysst grafik. Till en början behövs grafik till 2 Tanks, ett skott, någon form av mätare, en enkel bana samt en animerad explosion. För att bättre illustrera grafiken så har vi här förstorat den. Nedanför hittar ni en länk där ni kan ladda hem grafiken.

bild

bild

Implementation - steg ett

Vi kommer nu som hastigast att skapa ett fungerande spel som om det vore ett spel utan möjlighet till nätverksspel för att i ett senare skede lägga till den funktionen. Vi kommer enbart att titta närmare på sådant som ej behandlats i tidigare artiklar vilket gör att det går lite snabbt fram ifall man aldrig jobbat med OOP eller XNA. Kollisionshantering kommer i del 3 och därefter nätverksstödet.

Vi börjar med att skapa ett projekt som vi döper till MultiPlayerTanks. Vi packar upp grafiken i content-mappen till vårt projekt och lägger till grafiken i vår content-manager.

bild

Vi har valt att använda en enkel mark-textur som grafik till vår "bana". Själva banan kan hanteras på ett betydligt bättre sätt, exempelvis skapa ett tile-baserat system för grafik och speciella föremål/hinder på banan. I detta fall nöjer vi oss dock med en enkel (relativt stor) textur vars enda syfte är att ge en känsla av rörelse vid förflyttning.

Vi börjar med att skapa de olika klasserna. Exempelvis genom att högerklicka på vårt projekt i content-manager och välja Add - Class..

bild

Vi börjar med att skapa klassen GameObj.

Notera att vi använder något som kallas för Property (plural properties) som introducerades med C# 3.0 och är ett extremt smidigt och flexibelt sätt att läsa, skriva och beräkna värdet för privata medlemmar (eng. fields) (en medlem är en klassvariabel). En property är egentligen en form av metod som kallas accessor. Alla som sysslat lite med OOP vet att det blir en himla massa set- och get-metoder för att läsa, ändra och beräkna privata medlemmar. Detta blir alltså betydligt lättare och mer överskådligt med properties. Mer information hittar ni på webbplatsen för MSDN

De properties som alla objekt har är Position, Gfx och Angle. Sedan lägger vi till en Draw-metod så att objektet kan rita ut sig på skärmen. Draw-metoden tar 2 in-parametrar. Dels en SpriteBatch som vi skickar med när vi anropar metoden så att objektet kan rita ut sig själv och dels en Vector2 som används för att korrigera var objektet ritas ut i förhållande till vår spelare .

Alla objekt kommer att ha en "riktig" världsposition men när vi ritar ut grafiken så vill vi att vår spelare skall vara centrerad i mitten på skärmen hela tiden. Detta gör att vi behöver rita ut vissa objekt med en viss förskjutning (offset) som kompenserar för detta. Dock skall vissa objekt som mätare och text ritas ut på normalt vis. En alternativ lösning på detta problemet är att flytta "kameran" (viewport) som bestämmer vad som skall visas (lite enkelt uttryckt). Eller använda den omvandlingsmatris som finns. Det i min mening enklaste sättet för att åstadkomma detta i vårt projekt är att skicka en vektor till utritningsmetoden som korrigerar detta.

Själva utritningen sker med ett anrop till spriteBatch.Draw som tar 10 inparametrar och som beskrivits i projektet Asteroids här på csharpskolan. Vi använder bl a objektets medlem Angle för att kunna rotera grafiken som skall ritas ut samt centrera den.

Klassen MovingGameObj används för alla objekt som skall kunna röra på sig och ärver från klassen GameObj. Vi har properties för hastiget, Speed och riktning, Direction. Vi kommer än så länge att använda direction för riktning (som en normerad vektor med längden 1) och tillsammans med hastighetskonstanten (speed) bestämma förflyttningen av objektet.

De flesta medlemmar och metoder till klassen Tank är ganska självklara men vi kommer förtydliga dem och dess funktion för säkerhetsskull. Klassen ärver från klassen MovingGameObj.

Tank() Konstruktorn för klassen används för att tilldela vissa medlemsvariabler och ett startvärde
Enemy Denna bool används för att avgöra vilka Tanks som är fiende och vilken tank som är vår spelares. Detta har betydelse vid utritning av tanksen.
MaxSpeed Används för att bestämma maxhastigheten för förflyttningen.
ShotPower Används för att bestämma hur långt skottet ska förflytta sig när man avfyrat ett skott
WeaponType Används inte i nuläget men kommer antagligen att behövas ifall vi vil kunna byta vapen i framtiden. Variabeltypen enum lämpar sig eventuellt bättre.
ShotFired Denna bool används för att bestämma ifall ett skott har avfyrats (ska avfyras)
Life Lagrar livet för vår tank
Kills Lagrar antalet dödade tanks som vår spelare/tank har på sitt samvete
prevKS Detta är en medlemsvariabel av typen protected och ingen property eftersom den enbart används inom klassen. Används för att hålla koll på föregående tangentbordsstatus
Respawn() Denna metod används för att återställa en tank, exempelvis när en tank har "dött". Vi slumpar ut en ny position och återställer livet mm.
Update() I denna metod samlar vi all uppdateringslogik för vår tank. Som inparameter skickar vi gameTime som är den förflutna tiden i spelet vilket vi eventuellt behöver använda. Vi läser av tangentbordet och gör enkla algoritmer för att accelerera och bromsa vår tank. Vi använder piltangenterna för att svänga och gasa/bromsa. För att skjuta används SPACE-tangenten och vi när vi håller den inne så "laddas" vår ShotPower och skottet avlossas först när vi släpper SPACE-tangenten igen. Sist men inte minst så uppdateras positionen.

Klassen Shot ärver från MovingGameObj och är ganska enkel. Vi har en property Power som används för att avgöra hur långt skottet ska flyga. Metoden Update() används för att uppdatera skottet. I detta fall minska Power och ändra Positionen.

Klassen Meter ärver från klassen GameObj eftersom mätaren inte behöver flytta på sig. Konstruktorn Meter() används för att sätta värdet på property'n Value till 0. Value är värdet på det som mätaren skall visa och används i metoden Draw() som används istället för den ärvda metoden från GameObj i och med override. För att rita ut (grafiken till) mätaren så används en Rectangle vars startposition (övre vänstra hörn) är detsamma som mätarens position och "slutposition" (nedre högra hörnet) är höjden av grafikens höjd och bredden är Value. Detta gör att vår mätare skalas beroende på värdet. En Rectangle anges med heltal därför måste vi typkonvertera (eng. typecast) till int där så behövs.

Klassen Explosion används för animerade explosioner (hör och häpna). Konstruktorn Explosion() används för att ge viktiga medlemmar ett värde när en ny instans av klassen Explosion skapas. Hela klassen och animeringen av explosionen fungerar på liknande sätt som de animationer som beskrivits i tidigare artiklar (exempelvis denna.

Time Förfluten tid i milliesekunder
Frame Aktuell bildruta
AnimationSpeed Fördröjningen (i ms) mellan varje bildruta
Active Används för att bestämma ifall explosionen är aktiv eller inte
Update() Denna metod uppdaterar explosionen genom att kolla hur mycket tid som förflutit sedan förra uppdateringen och ändra bildruta ifall mer tid än AnimationSpeed har förflutit. Efter 16 bildrutor så sätts Ative till false
Draw() Denna metoden skriver över den ärvda Draw-metoden och används för att rita ut rätt bildruta från grafikfilen för explosionen (där alla bildrutor är i samma bild). Precis som i tidigare exempel på animationer så är bilden som innehåller bildrutorna 256x256 pixlar och delas in i 16 olika bildrutor.

Lägg till en SpriteFont

Vi vill kunna skriva ut lite saker på skärmen och därför måste vi lägga till en spritefont (som beskrivits i tidigare artiklar).

bild

Enda skillnaden nu är att vi ändrar lite i inställningarna för fonten (xml-filen SpriteFont1.sritefont). Vi ändrar style från Regular till Bold.

bild

Game1.cs bör inte innehålla några lustigheter. Det som kan vara värt att kommentera är avsaknaden av inparametrar till konstruktorn för de olika objekten när nya instanser av klasserna skapas. Istället så anges värden på propertis direkt inom {}-parenteserna.

Vi skapar ett enkelt spel utan några "gamestates" och än så länge ingen kollisionshantering (kommer i del 3). För att få en enkel skugg-effekt på utskriften så skrivs varje text ut två gånger. Först i svart och sedan något förskjutet i vitt och på så sätt åstadkommer man den effekten. Alla objekt (utom mätarna) ritas ut i förhållande till vår spelares tank eftersom den skall vara centrerad.

Vår bana är en enkel GameObj som bara ritas ut. Ska vi göra något av banan så bör vi i ett senare skede skapa en egen klass för banor. Dessa kan med fördel vara tile-baserade för att förenkla ban-byggandet och optimera det hela lite.

När allt är klart bör vi ha något i stil med detta (se bild nedanför).

bild

Viktiga begrepp

  • Property
  • medlemmar
  • fields

Kommentarer

3 inlägg