Design – Meteor Hunt

Inledning

Fördelarna med att använda OOP och att designa strukturen väl är mycket viktigt för att kunna skapa större projekt. En av de absolut viktigaste delarna, och även mest intressanta, är just OOP-designen. I denna artikel presenteras en grundläggande design som passar de flesta sorters spel.

Det är svårt att säga hur mycket tid som ska läggas på kodningen och hur mycket tid som ska läggas på designen av koden men det rekommenderas starkt att du iallafall lägger mer än hälften av tiden på design och struktur. Det går flera gånger snabbare att ändra designen i planeringsstadiet än att behöva skriva om stora mängder kod i efterhand.

bild
Klicka för förstoring.

Spelet "Meteor Hunt" är bara ett enkelt exempel komplett med ljud och koddesign. Du ska skjuta ned meteoriter och samla poäng.

UML

bild

Basklassen

Klassen GameObj utgör vår basklass. Från denna klass bygger vi vidare med subklasserna Shot, Meteor, Player och Explosion.

GameObj innehåller det som alla grafiska och rörliga delar på skärmen i grunden är uppbyggda av. Subklasserna är mer specialiserade och har ett annat beteende.

Varje GameObj har variabler och metod för uppritning. I konstruktorn så ska det skickas med en ContentManager och en SoundBank så att GameObj kan ladda in sin egen grafik och spela upp ljud. Klassen är abstract då metoden LastWishes() är deklarerad som abstract. Varje subklass är då tvingad att implementera denna metod. Metoderna Draw() och Update() är deklarerade virtual så att subklasserna kan ändra beteendet på dessa.

Subklasser

Lösa tankar kring hur klasserna är uppbyggda...

Shot

Detta är skotten som Player skjuter. Det är från klassen Player som objekt av denna sort skapas. När objekt av Shot skaps slumpas även en hastighet med riktning uppåt ±5°.

Som sista önskningar, LastWishes(), så skapas objekt av typen Explosion som läggs i listan för neutrala objekt. Ljudet för explosion spelas också upp. Allt för att skapa en trevlig effekt då skotten träffar något.

Skulle skotten färdas för långt utanför skärmen så blir de automatiskt döda.

Player

Spelaren är såklart skeppet som du styr. Via en overrideUpdate() sköts avläsningen av tangentbordet så att styrningen fungerar. Det finns spärrar så att skeppet inte kan åka utanför skärmen.

Player har några extra medlemsvariabler för att sköta fördröjningen mellan skotten som avlossas. Player ritar även ut livmätaren längst upp till höger.

Meteor

Meteoriterna, liksom skotten, blir automatiskt döda då de färdats för långt utanför skärmen. Detta för att listorna på objekt inte ska blir gigantiska då vi har spelat i flera timmar.

Meteoriterna slumpar sina egna startpositioner samt skapar explosioner och ger poäng som sista ösnkning.

Explosion

Detta är det enda på skärmen som är animerat. Klassen Explosion har egna variabler för att hantera animeringen såsom animTime, animRate och frame.

När animationen har spelats klart så dör objekten.

Lagring och hantering av data

För att hålla reda på alla objekt skapar vi tre listor av typen List:

  • evilObjList. Alla onda objekt, dvs de som skadar goda objekt t.ex. meteoriterna
  • goodObjList. Alla goda objekt, t.ex. skott
  • neutralObjList. Effekter såsom explosionerna.

Alla listor lagrar objekt av typen GameObj. Listorna deklareras som public static så att alla klasser kan nå listorna och lägga till objekt i dem.

Hela Update-delen av spelet handlar nu mest om att gå igenom listorna och se till så att alla objekt blir uppdaterade. Om något objekt inte längre lever, dvs att alive är false, så ska objekten städas bort med RemoveAt(index). Samtidigt som vi bearbetar alla onda objekt så testas även kollision mot både player och alla goda objekt.

Update

        protected override void Update(GameTime gameTime)
        {
            audioEngine.Update();
		
            if (!player.alive)
            {
                KeyboardState ks = Keyboard.GetState();
                if (ks.IsKeyDown(Keys.Enter))
                    Restart();
                return;
            }
            respawnTime += gameTime.ElapsedGameTime.Milliseconds;

            //uppdatera player
            player.Update(gameTime);

            //dags för en ny meteorit
            if (respawnTime > respawnRate)
            {
                respawnTime = 0;
                evilObjList.Add(new Meteor(Content, soundBank));
            }
            
            //uppdatera neutrala objekt
            for(int i=0; i < neutralObjList.Count; i++)
            {
                neutralObjList[i].Update(gameTime);
                if (!neutralObjList[i].alive)
                {
                    neutralObjList.RemoveAt(i);
                    continue;
                }
            }

            //uppdatera goda objekt
            foreach (GameObj g in goodObjList)
            {
                g.Update(gameTime);
            }

            //uppdatera & rensa onda objekt
            for (int i = 0; i < evilObjList.Count; i++)
            {
                //uppdatera onda objekt
                evilObjList[i].Update(gameTime);

                //testa mot player
                evilObjList[i].CheckCollision(player);

                //gå igenom alla goda för att testa kollision
                for (int j = 0; j < goodObjList.Count; j++)
                {
                    //testa kollision
                    evilObjList[i].CheckCollision(goodObjList[j]);

                    //rensa bort döda objekt
                    if (!goodObjList[j].alive)
                    {
                        goodObjList.RemoveAt(j);
                        continue;
                    }
                }

                //rensa bort döda objekt
                if (!evilObjList[i].alive)
                {
                    evilObjList.RemoveAt(i);
                    continue;
                }
            }

            base.Update(gameTime);
        }

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *

Scroll to top