- Kursmaterial
- Planering
- Arbete
- Kunskapsdokument
- Tutorials
- Andra kurser
- Om Kursolle
2. Moment02 - Objektorientering
I detta moment skall vi kika på objektorientering, OOP (object-oriented programming), och hur vi kan använda denna teknik för att bygga tydligare och mer generella applikationer. Vi kommer lära oss att bygga klasser och skapa objekt vilket är grunden inom den objektorienterade programmeringen.
Objektorientering är en viktig del av de objektorienterade programmeringsspråken. Det är ett sätt att försöka anpassa programmeringen mot den riktiga
världen där helt andra datatyper finns än just de datatyper som finns inbyggt i programmeringsspråket. När vi skall sätta samman information om någon person eller en bil så räcker det inte att bara förlita sig på bara de enkla variabler som finns i en applikation. Det är då vi vill använda oss av objekt.
Momentet börjar med en teorigenomgång frikopplad från något specifikt programmeringsspråk innan vi kikar på hur OOP fungerar i programmeringsspråket C#. Detta gör vi genom att följa en tutorial och bygga en klass och lär oss använda denna i en applikation. Slutligen så börjar vi med den tutorial som vi skall jobba med i stora delar av kursen. Den tutorialen där vi bygger ett spel i C#.
2.1 Teori
Vi börjar med en allmän genomgång av OOP. Denna presentation är bortkopplad från något specifikt programmeringsspråk. Senare i momentet kommer vi kika på hur vi jobbar med objektorientering specifikt i C#.
2.2 OOP & C# genom två tutorials
För att träna på OOP så skall totalt fyra tutorials gås igenom, två i detta moment och två i moment03.
Hur tutorials hänger ihop
2.2.1 Objektorienteringens grunder
För att lära dig objektorientering i C# så har jag byggt en tutorial där du skall skapa klassen Car och sedan jobba med denna klass genom objektorienteringens grunder.
Uppgift m02u01
Genomför tutorial ovan och redovisa sedan ditt arbete enligt instruktionerna under punkten Redovisning
nedan.
2.2.2 Att lagra objekt i en samling
Dags för nästa tutorial, denna gången bygger vi vidare med klassen Car och nu skall vi bygga en applikation kring våra bilar så att de går att lagra i en lista, lägga till, ta bort och tömma listan. Klicka här för att jobba med tutorialen.
Uppgift m02u02
Genomför tutorial ovan och lämna sedan in ditt projekt i Classroom. Packa hela projektet så att jag kan köra det på min dator.
2.3 Speltutorial
Vi kommer att arbeta med ett läromedel, skrivet av Krister Trangius, som innehåller en bra tutorial i hur man utvecklar spel. Även om boken är skriven för en äldre kursplan (före HT-17) finns det mycket vi kan dra nytta av.
2.3.1 Spelets grundstruktur
Introduktion till spelutveckling och hur våra projekt ser ut finns i boken men jag kommer här gå igenom det viktigaste. Numreringen härifrån kommer följa bokens kapitelnumrering.
Kapitel 5. Komma igång med XNA och MonoGame
MonoGame är ett ramverk för att skapa spel för olika plattformar. Det finns flera liknande ramverk som underlättar för dig att skapa spel där, Godot, Unity och Unreal kan nämnas bland de större.
Tidigare användes Microsoft XNA, men det utvecklas inte längre. MonoGame är ett communitydrivet ramverk som i stor utsträckning är API-kompatibelt med XNA, vilket gör att många XNA-projekt kan portas till MonoGame (ibland med mindre justeringar).
Vi kommer utveckla våra spel i Visual Studio och med hjälp av MonoGames hemsida så behöver du installera och konfigurera Visual Studio något. Börja med att gå till Setting up your OS for development on Windows och följ sedan alla steg beroende på vilken IDE du använder. Våren 2025 har de ändrat lite i guiden men det borde fungera, annars löser vi de problem som uppstår.
Kapitel 6. Introduktion till spelutveckling
När du skapar ett nytt spelprojekt i MonoGame så kommer du att få lite grundläggande kod som behövs för att spelet skall fungera. I den här koden så finns det några förskapade metoder som vi behöver bekanta oss med.
Kapitel 6.1 Att skapa ett första projekt
Har du inte redan skapat ditt första projekt hittar du guiden ovan.
Kapitel 6.2 Spel-loopen
Spel styrs oftast av en spel-loop: varje varv uppdaterar vi spelets logik (Update) och ritar en ny bild (Draw). Eftersom Update() körs många gånger per sekund hinner vi läsa input ofta och uppdatera positioner, kollisionslogik och spelregler kontinuerligt.
Inom spelprogrammering så kan inte spelet endast vänta på att vi skall göra något, då skulle ju spelet stå helt still tills vi väljer att flytta vår figur. Det skulle ju bli tråkiga spel om inget hände under tiden.
Därför så finns det en spel-loop (game loop) som ser till att spelet hela tiden fortsätter. Denna spel-loop har en viss frekvens då den uppdaterar vårt spel. I MonoGame uppdateras spelet vanligtvis cirka 60 gånger per sekund så det som vi egentligen ser är 60 utritade stillbilder per sekund, det mäts i fps (frames per second). Hastigheten på dessa bilder gör att vi ser det som ett rörligt spel.
Kapitel 6.3 Metoden Main()
I metoden skapas ett objekt av typen Game1 och sedan anropas metoden .
Run() startar spel-loopen. Som standard försöker MonoGame köra med ett fast tidssteg (ofta 60 uppdateringar per sekund), men den faktiska bildfrekvensen kan variera beroende på inställningar och prestanda. Varför objektet game skapas på detta sätt beskrivs kort i boken för den som är intresserad. För kommande arbete är det inte så viktigt.
Kapitel 6.4 Livscykeln för ett spel i MonoGame
Metoden är viktig för spelet men det är samtidigt ingen metod som vi kommer skriva kod i. Metoden har som uppgift att se till att spelet loopas 60 gånger per sekund, att alla kontroller görs och att spelet ritas ut som det är tänkt. Vi kan se denna metoden som vår spelmotor som sköter allt som vi förväntar oss skall fungera.
Det intressanta är att Run() i sin tur anropar fem andra metoder som vi kan påverka och som kommer driva spelet framåt.
Metoderna och används för att sätta upp variabler och allt vi behöver förbereda inför spelet. Ibland väljer en utvecklare att bara använda den ena av dessa två metoder och ibland vill man använda bägge, det spelar egentligen ingen större roll för oss i våra mindre projekt.
och är själva spel-loopen, och dessa metoder kommer köras 60 ggr var per sekund så länge spelet pågår.
är metoden som rensar upp efter oss när spelet är slut. Denna metod finns inte när du skapar ett nytt projekt då den inte används lika ofta längre, du kan däremot skapa den om du vill.
Kapitel 6.4.1 Klassen Game1
Ändrad namngivning i koden
MonoGame har ändrat sättet som de skapar sina variabelnamn. I boken heter medlemsvariabeln för spelets SpriteBatch till medan i den autogenererade koden döper numera variabeln till . Det gör ingen skillnad, men antingen döper du om variablerna till bokens standard från början eller så kommer du ihåg att du behöver namnge allt i boken enligt den nya namngivningen.
I klassen Game1 finns det två variabler och datatyper som vi behöver hålla koll på.
GraphicsDeviceManager graphics; SpriteBatch spriteBatch;
är en klass som tar hand om kopplingen mellan spelet och grafikkortet.
är klassen som vi använder oss av för att skriva ut bilder i spelet.
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
I konstruktorn så kopplas objektet till grafikkortet.
talar om vart vi kommer lägga de filer som vi vill använda i vårt spel, tex bilder och ljud.
Ändringar i koden
I klassen så har det skapats en ny medlem, , som borde vara självförklarande.
Kapitel 6.4.2 Initialize()
I metoden initierar vi de objekt som vi kommer behöva under spelet. Metoden ligger utanför spel-loopen så denna metod kommer endast köras en gång.
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
anropar metoden från den ärvda klassen . Här skriver vi vår egna kod före anropet till basklassens metod. Mer om arv kommer i nästa moment.
Kapitel 6.4.3 LoadContent()
I metoden så laddas content (innehåll) in, här tänker vi oss att bilder, ljud, fonter och annat innehåll skall läsas in. Denna metod ingår heller inte i spel-loopen så den körs också en gång. Det är lite flytande vad som skall göras i denna metod och vad som skall/kan göras i .
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//TODO: use this.Content to load your game content here
}
Här skapas objektet som använder för att kunna rita ut sprites (bilder) på skärmen.
Kapitel 6.4.4 Update()
Metoden är den första metoden som körs inne i spel-loopen, här kommer alla händelser fångas upp och alla beräkningar att göras. Eftersom denna körs 60 gånger per sekund så finns det 60 möjligheter per sekund att läsa av en tangent som trycks ner, en musrörelse eller en kollision mellan två objekt i vårt spel. Sedan måste alla beräkningar göras så att objekten flyttar på sig, något händer när två objekt kolliderar som påverkar dessa och andra objekt.
protected override void Update(GameTime gameTime)
{
// For Mobile devices, this logic will close the Game when the Back button is pressed
// Exit() is obsolete on iOS
#if !__IOS__ && !__TVOS__
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
#endif
// TODO: Add your update logic here
base.Update(gameTime);
}
Koden här är ett exempel på hur den metoden kan se ut. Den kod som finns där för tillfället är kod som avbryter spelet på flera olika sätt, beroende på vilken plattform som spelet skall köras på.
Här anropar vi även den ärvda metoden när vi har gjort våra specifika anrop. Detta fungerar på samma sätt som i .
Ändringar i koden
I den automatgenererade koden så har hela blocket med stängknappen i IOS och TVOS tagits bort.
Kapitel: 6.4.5 Draw()
Metoden är den andra metoden som körs inne i spel-loopen och här kommer nu alla bilder att ritas ut så att vi ser att det har hänt något i spelet. Varje gång körs så levereras en stillbild över hur det ser ut precis vid det tillfälle då bilden ritas. Även denna metod kommer köras 60ggr/s vilket ger oss upplevelsen att spelet rör sig
.
Det är när spelet inte hinner göra alla beräkningar eller rita ut allt som skall ritas ut innan spel-loopens nästa varv börjar som vi upplever att spelet laggar. Datorn hinner helt enkelt inte med allt som skall göras. Då får vi ge mer hårdvaruresurser, skriva effektivare kod eller helt enkelt ta bort delar av spelets detaljer.
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
//TODO: Add your drawing code here
base.Draw(gameTime);
}
anropas här på samma sätt som tidigare.
Kapitel 6.4.6 UnloadContent()
Metoden ligger utanför spel-loopen och körs först när spelet avslutas. När spelet stängs avslutas processen och operativsystemet frigör minnet. Men under körning (och i större projekt) är det viktigt att stänga filer/streams och frigöra resurser som implementerar IDisposable (t.ex. vissa grafik- och nätverksresurser).
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
Ändringar i koden
I den automatgenererade koden så skapas inte denna metoden alls.
Kapitel 6.5 Att rita ut på skärmen.
Bilder som används inom spelprogrammering kallas en sprite
. Det finns en historik kring hur man använde sprite på ett speciellt sätt tidigare då våra datorer hade mindre kapacitet. Då var det väldigt viktigt att sprites var i exakta storlekar och så optimerade i storlek som det var möjligt. Ofta byggdes större bilder upp av flera mindre sprites för att optimera datorns arbete. Idag har datorerna en helt annan kapacitet och då behöver vi inte trixa med bilder på samma sätt. Namnet sprite har dock blivit kvar. Med det sagt så bör vi ändå se till att våra bilder har rätt storlek när den läses in i spelet, vilket är bra att tänka på när/om du kommer skapa egna sprites.
2.3.2 Arbete med speltutorial
Nu är det dags att skapa projektet SpaceShooter
och börja med bokens tutorial. När projektet är skapat så börjar du med att gå till kunskapsdokument - hjälp till boken som är stöd till boken. Här kommer saker som inte längre stämmer med hur boken gör det att fångas upp. Den första delen i kunskapsdokumentet, “6.5 Lägg till en sprite” visar hur du måste göra för att få det att fungera. I övrigt börjar du på kapitel 6.5.1 i boken och jobbar dig sedan igenom kapitel 6 i boken.
Uppgift
När du är klar med kapitel 6 så har du säkerligen en ganska så rörig kod, gå igenom koden, snygga till, kommentera de saker du har gjort medan du kommer ihåg vad du har gjort.
Spara en kopia som innehåller allt som fungerar så här långt så kan du jobba vidare med en kopia i nästa kapitel. Skulle något bli väldigt fel i nästa del så kan du enkelt börja om från ett fungerande projekt istället för att behöva felsöka allt för länge.