Skip to content

Monogame del 1

Monogame är ett .Net bibliotek för c# med stöd för dom flesta populära plattformarna idag. Så som Windows, mac, linux, Android, Ios, och även ett flertal konsoler. Jag kommer bara rikta mig till Windows men det är nog inte några större problem att porta över koden till någon av dom andra operativsystemen.

Jag kommer inte gå in på hur du installerar Visual Studio. Det finns en uppsjö andra guider som tar upp det förfarandet. Googla på på How to install Visual Studio så kommer du garanterat hitta en uppsjö guider. För stunden så är det Visual Studio Community 2019 som gäller.

När det är klart så kan vi installera Monogame, det görs numera genom något som heter Nugets. Starta Visual studio och klicka på "continue without code" i fönstret som öppnas. Efter det så öppnas Visual Studio i sin helhet utan att ha skapat någon kod. Där klickar vi på Extensions uppe i menyraden och sedan Manage Extensions. Då öppnas ett nytt fönster och uppe i högra hörnet av fönstret så finns en liten sökruta som vi skriver monogame i och trycker enter.

Ett extension som heter monogame project templates dyker upp och på den klickar vi install.
När det är klart kan vi stänga det fönstret och sedan klicka på File-> New->Project...



Så öppnas ett nytt fönster. I den listan letar vi upp Monogame Windows Project och trycker Next. Här får vi namnge vårat projekt, jag väljer att döpa mitt till GameTutorial och sedan klickar vi på Create. På så vis så skapas det två basklasser och lite annat åt oss så att vi snabbt kan komma igång och skriva spel.

När vi trycker på F5 så kommer ett blått fönster att öppnas vilket innebär att allt fungerar som det ska.
Låt oss grotta ner oss i den här koden lite.


Två klasser är skapta i vårat projekt, en heter Program.cs och ser ut så här

  1.  
  2. using System;
  3.  
  4. namespace GameTutorial
  5. {
  6. #if WINDOWS || LINUX
  7.     /// <summary>
  8.     /// The main class.
  9.     /// </summary>
  10.     public static class Program
  11.     {
  12.         /// <summary>
  13.         /// The main entry point for the application.
  14.         /// </summary>
  15.         [STAThread]
  16.         static void Main()
  17.         {
  18.             using (var game = new GameTutorialGame())
  19.                 game.Run();
  20.         }
  21.     }
  22. #endif
  23. }
  24.  


  1.  
  2. static void Main()
  3.  

Här börjar vårat program genom att skapa en ny klass av typen GameTutorialGame. Den här typen är genererad utifrån vad vi döpte vårat projekt. Sedan körs game.Run() och vårat program kör igång.
Det är inte så mycket att tala om i den här klassen så vi hoppar över till den dåligt döpta klassen GameTutorialGame klassen.
Men först så tänkte jag att vi gör en lite lite ändring här i program.cs.

  1.  
  2. using System;
  3.  
  4. namespace GameTutorial
  5. {
  6. #if WINDOWS || LINUX
  7.     /// <summary>
  8.     /// The main class.
  9.     /// </summary>
  10.     public static class Program
  11.     {
  12.         /// <summary>
  13.         /// The main entry point for the application.
  14.         /// </summary>
  15.         [STAThread]
  16.         static void Main()
  17.         {
  18.             using (var game = new GameTutorialGame())
  19.             {
  20.                 game.IsFixedTimeStep = false;
  21.                 game.Run();
  22.             }
  23.         }
  24.     }
  25. #endif
  26. }
  27.  


Den raden måste till för att vi ska kunna forcera våran main loop att snurra fortare än 60 frames per sekund. Det är möjligtvis inget som rekomenderas, men det kan vara bra medans man bygger för att testa prestanda och så att allt flyter på i rätt takt. Men vi kommer till det senare. Nu hoppar vi över till Game klassen eller GameTutorialGame som den heter för mig.

Den här klassen har betydligt mer kod i sig, uppemot 100 rader. Men mycket kod är kommentarer, jag kan rekomendera att läsa igenom alla kommentarer och när du är klar med det så tar vi och markerar all kod och trycker delete. Sådär... Då kan vi börja bygga upp klassen igen och gå igenom dom funktioner som fins. Vilka vi behöver kommer vi till senare och vilka som är viktiga.

Vi börjar med att lägga till namespace för klassen, så att klassen tillhör rätt namespace. En namespace är som en virtuell mapp, alla klasser som skapas och använder sig av samma namespace kommer att hitta varandra. Men om du skapar en klass med ett annat namespace så måste du referera till den klassen genom att ange namespace antingen via deklareringen eller med using statement längst upp i klassen.
eftersom det här är en basklass som ligger längst ner i namespace hierarkin så får den här namespacet samma namn som projektet. så i mitt fall GameTutorial.
Så nu har vi följande kod.


  1.  
  2. namespace GameTutorial
  3. {
  4.  
  5. }
  6.  


Fint, glöm inte hak-klammrarna. Då är vi redo att skapa klassen, det gör vi genom att skriva


  1.  
  2. namespace GameTutorial
  3. {
  4.     public class GameTutorialGame
  5.     {
  6.     }
  7. }
  8.  


Att klassen deklareras som publik gör att man kan anropa klassen från ett annat projekt, det är inget vi kommer göra i den här guiden, men det kan vara fint att känna till.
Men nu blir Visual Studio lite sur och kastar några fel från Program klassen som vi tittade på tidigare. Det är för att våran klass måste ärva lite funktioner från en basklass som heter Game. För att fixa det så lägger vi till : Game efter vårat klassnamn. Men för att det ska funka så måste vi lägga till vårat första using statement längst upp i klass filen.

Så nu ser våran fil ut så här och vi borde vara fria från felmeddelanden.

  1.  
  2. using Microsoft.Xna.Framework;
  3.  
  4. namespace GameTutorial
  5. {
  6.     public class GameTutorialGame : Game
  7.     {
  8.     }
  9. }
  10.  

när vi skriver using Microsoft.Xna.Framework; så talar vi om för klassen att den behöver använda kod ifrån det här kodbiblioteket. Det här är kod som på ett eller annat sätt ligger utanför vårat projekt och det måste finnas externa referenser till det för att fungera. Det är ett lite mer avancerat ämne så vi kan tala om det senare. Vi litar på att Visual Studio gör sitt jobb och lägger till dom referenser som behövs.

Vi har inga felmeddelanden, men om vi provar att köra programmet så kommer det att krascha. Så vi måste skriva lite till innan vi kan köra programmet igen.

Nu är det dax för oss att lägga till en klass konstuktor. Det är den första funktionen som anropas när en klass skapas med "new" Om du mins i program klassen så stog det

  1.  
  2. using (var game = new GameTutorialGame())
  3.  

när man skriver new GameTutorialGame() så kommer lämplig konstruktor att anropas.
I det här fallet en konstruktor utan argument, så då vet vi hur våran konstruktor måste se ut.

  1.  
  2. public GameTutorialGame()
  3. {
  4. }
  5.  


Så nu har vi följande kod

  1.  
  2.  
  3. using Microsoft.Xna.Framework;
  4.  
  5. namespace GameTutorial
  6. {
  7.     public class GameTutorialGame : Game
  8.     {
  9.         public GameTutorialGame()
  10.         {
  11.  
  12.         }
  13.     }
  14. }
  15.  



Om vi kör programmet så kraschar det och säger att vi inte har någon "Graphics Device Service" Det löser vi lätt genom att deklarera en variabel som heter graphics av typen GraphicsDeviceManager och den måste vi initiera i konstuktorn av klassen.
Så nu ska koden se ut så här

  1.  
  2. using Microsoft.Xna.Framework;
  3.  
  4. namespace GameTutorial
  5. {
  6.     public class GameTutorialGame : Game
  7.     {
  8.         GraphicsDeviceManager graphics;
  9.  
  10.         public GameTutorialGame()
  11.         {
  12.             graphics = new GraphicsDeviceManager(this);
  13.         }
  14.  
  15.     }
  16. }
  17.  
  18.  


Trycker vi på F5 nu så körs vårat program utan fel och ett svart fönster öppnas. Inte illa med 17 rader kod.

Iomed att vi ärver våran klass från Game så finns det ett antal funktioner som vi kan skriva över, eller "överrida" ( override)
om vi skriver "protected override" så kommer Visual Studio visa ett antal funktioner som kan överridas, låt oss lägga till allihopa.

Jag har även lagg till en rad i varje funktion som skriver ut till terminalen varje gång funktionen anropas.
På så vis kan vi få lite klarhet vad respektive funktion gör och när den anropas.

Koden ser nu ut så här:


  1.  
  2. using Microsoft.Xna.Framework;
  3. using System;
  4.  
  5. namespace GameTutorial
  6. {
  7.  
  8.     public class GameTutorialGame : Game
  9.     {
  10.         GraphicsDeviceManager graphics;
  11.  
  12.         public GameTutorialGame()
  13.         {
  14.             graphics = new GraphicsDeviceManager(this);
  15.             Console.WriteLine("Konstuktor");
  16.         }
  17.  
  18.         protected override void Initialize()
  19.         {
  20.             Console.WriteLine("Initialize");
  21.             base.Initialize();
  22.         }
  23.  
  24.         protected override void LoadContent()
  25.         {
  26.             Console.WriteLine("LoadContent");
  27.             base.LoadContent();
  28.         }
  29.  
  30.         protected override void UnloadContent()
  31.         {
  32.             Console.WriteLine("UnloadContent");
  33.             base.UnloadContent();
  34.         }
  35.  
  36.         protected override void OnActivated(EventArgs args)
  37.         {
  38.             Console.WriteLine("OnActivated");
  39.             base.OnActivated(args);
  40.         }
  41.  
  42.         protected override void OnDeactivated(EventArgs args)
  43.         {
  44.             Console.WriteLine("OnDeactivated");
  45.             base.OnDeactivated(args);
  46.         }
  47.  
  48.         protected override void OnExiting(EventArgs args)
  49.         {
  50.             Console.WriteLine("OnExiting");
  51.             base.OnExiting(args);
  52.         }
  53.  
  54.         protected override void Dispose(bool disposing)
  55.         {
  56.             Console.WriteLine("Dispose");
  57.             base.Dispose(disposing);
  58.         }
  59.  
  60.         protected override void BeginRun()
  61.         {
  62.             Console.WriteLine("BeginRun");
  63.             base.BeginRun();
  64.         }
  65.  
  66.         protected override void Update(GameTime gameTime)
  67.         {
  68.             Console.WriteLine("Update");
  69.             base.Update(gameTime);
  70.         }
  71.  
  72.         protected override void EndRun()
  73.         {
  74.             Console.WriteLine("EndRun");
  75.             base.EndRun();
  76.         }
  77.  
  78.         protected override bool BeginDraw()
  79.         {
  80.             Console.WriteLine("BeginDraw");
  81.             return base.BeginDraw();
  82.         }
  83.  
  84.         protected override void Draw(GameTime gameTime)
  85.         {
  86.             Console.WriteLine("Draw");
  87.             base.Draw(gameTime);
  88.         }
  89.  
  90.         protected override void EndDraw()
  91.         {
  92.             Console.WriteLine("EndDraw");
  93.             base.EndDraw();
  94.         }
  95.     }
  96. }
  97.  
  98.  

Det börjar bli mycket nu, Console raderna kan vi ta bort senare men om vi kör programmet nu så kan vi se vilka funktioner som blivit anropade och i vilken ordning. Tryck på stopp knappen ganska omgående efter att du startat programmet annars kommer vissa funktioner spamma ut information och du kommer få svårt att se vad som händer i början.

Vi ser iallafall att följande funktioner anroppas i tur och ordning.
Konstuktor
OnActivated
Initialize
LoadContent
BeginRun
Update
Update
BeginDraw
Draw
EndDraw


Sedan körs update, begindraw Draw, endDraw i en oändlig loop.
Om vi lägger till följande kod utan någon närmare förklaring för tillfället

if (Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit();

i Update(GameTime gameTime)
så kan vi avsluta programmet på ett korrekt sätt och se vad som händer när vi avslutar. Vi tar bort Consol -rader som vi redan sett anropas.
Den raden kräver att vi ladar in ett nytt namespace så om inte visula studio redan gjort det automatiskt så lägger vi till using Microsoft.Xna.Framework.Input; längst upp.

  1.  
  2. using Microsoft.Xna.Framework;
  3. using Microsoft.Xna.Framework.Input;
  4. using System;
  5.  
  6. namespace GameTutorial
  7. {
  8.  
  9.     public class GameTutorialGame : Game
  10.     {
  11.         GraphicsDeviceManager graphics;
  12.  
  13.         public GameTutorialGame()
  14.         {
  15.             graphics = new GraphicsDeviceManager(this);
  16.             Console.WriteLine("Konstuktor");
  17.         }
  18.  
  19.         protected override void Initialize()
  20.         {
  21.             Console.WriteLine("Initialize");
  22.             base.Initialize();
  23.         }
  24.  
  25.         protected override void LoadContent()
  26.         {
  27.             Console.WriteLine("LoadContent");
  28.             base.LoadContent();
  29.         }
  30.  
  31.         protected override void UnloadContent()
  32.         {
  33.             Console.WriteLine("UnloadContent");
  34.             base.UnloadContent();
  35.         }
  36.  
  37.         protected override void OnActivated(EventArgs args)
  38.         {
  39.             Console.WriteLine("OnActivated");
  40.             base.OnActivated(args);
  41.         }
  42.  
  43.         protected override void OnDeactivated(EventArgs args)
  44.         {
  45.             Console.WriteLine("OnDeactivated");
  46.             base.OnDeactivated(args);
  47.         }
  48.  
  49.         protected override void OnExiting(EventArgs args)
  50.         {
  51.             Console.WriteLine("OnExiting");
  52.             base.OnExiting(args);
  53.         }
  54.  
  55.         protected override void Dispose(bool disposing)
  56.         {
  57.             Console.WriteLine("Dispose");
  58.             base.Dispose(disposing);
  59.         }
  60.  
  61.         protected override void BeginRun()
  62.         {
  63.             Console.WriteLine("BeginRun");
  64.             base.BeginRun();
  65.         }
  66.  
  67.         protected override void Update(GameTime gameTime)
  68.         {
  69.             if (Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit();
  70.             base.Update(gameTime);
  71.         }
  72.  
  73.         protected override void EndRun()
  74.         {
  75.             Console.WriteLine("EndRun");
  76.             base.EndRun();
  77.         }
  78.  
  79.         protected override bool BeginDraw()
  80.         {
  81.             return base.BeginDraw();
  82.         }
  83.  
  84.         protected override void Draw(GameTime gameTime)
  85.         {
  86.             base.Draw(gameTime);
  87.         }
  88.  
  89.         protected override void EndDraw()
  90.         {
  91.             base.EndDraw();
  92.         }
  93.     }
  94. }
  95.  
  96.  


Kör vi nu så får vi följande resultat
Konstuktor
OnActivated
Initialize
LoadContent
BeginRun

> Loopen startas här och följande funktioner körs tills programmet avslutas
Update
BeginDraw
Draw
EndDraw

> Här trycker vi på escape

OnDeactivated
EndRun
OnExiting
UnloadContent
Dispose


OnActivate och OnDeactivate anroppas var gång fönstret tappar och/eller återfår focus. Det kan vara bra att hålla reda på framöver ifall användaren tabbar ut från programmet, eller av annan aledning förlorar fokus från spelet, så man kan forcera in spelet i pause läge.
Det börjar bli ett jäkla längt inlägg nu så jag tror jag stannar här så kanske vi kan komma till lite mer roliga saker i nästa inlägg. Förhoppningsvis har vi lite koll på hur monogame anropar vårat spel. Inästa inlägg så ska vi se till att rita nånting i fönstret och kanske ta lite mer konfigurering av monogame.

Tack för visat intresse.

Trackbacks

No Trackbacks

Comments

Display comments as Linear | Threaded

No comments

Add Comment

You can use [geshi lang=lang_name [,ln={y|n}]][/geshi] tags to embed source code snippets.
Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Form options