Skip to content

Monogame del 5, [Content pipeline]

Förra gången laddade vi in bilder till våra texturer. Det sättet fungerar såklart alldeles utmärkt men det finns ett väldigt mycket bättre sätt. Med monogame så följder det med ett verktyg för att packa med vad vi kallar för assets. En såkallad assetsmanager som heter MGCB Editor. I ditt solutions fönster så hittar du hela ditt projekt samt en mapp som heter Content.
I den mappen finns det en fil som heter samma som ditt projekt och avslutas med Content. I mitt fall heter den MonogameTutorialContent.mgcb.



Det ska egentligen funka att dubbelklicka på den filen för att öppna editorn, men för mig funkar det inte alls utan jag måste högerklicka på filen och välja "open with". Väl där måste jag sedan välja mgcb-editor-wpf och sedan ok. och först då så får jag upp editorn som ser ut så här...



Här kan vi välja att lägga till alla sina assets som man kan komma att behöva i spelet eller programmet framöver. Man kan ladda in ljudfiler, bildfiler, fontbeskrivningsfiler, shader filer med mer. Vi komma idag bara ladda in samma bild som i förra avsnittet för att se hur det funkar. Det här blir mer en guide över själva editorn och inte så mycket kod.

Vi börjar med menyerna eftersom knapparna bara är genvägar till det som finns i menyn.
Vi börjar med File...
Det är inte mycket som används här. New och open förklarar sig själva men är bara intressant om du öppnar editorn utan projekt. Det bryr vi oss inte om. Import skulle kunna vara intressant om du har kommit över några gamla XNA assets från något gammalt projekt. annars kan vi skippa det också.

Vi hoppar vidare till Edit...
här har vi en meny som heter Add och under den finns några intressanta undermenyer

New Item... Här lägger man till font beskrivningar och shaders bland annat. Det kommer vi till när vi ska rita text och skriva shaders.

New Folder skapar en ny mapp i strukturen, det kan vara bra för att organisera sina assets lite bättre. En mapp för texturer, en för ljud, en för fonter och en för shaders till exempel.

Existing Item. Här kan vi lägga in befintliga filer från våran dator. Här letar vi upp och lägger in våran bild som vi har från förra avsnittet.

Det kommer upp en fråga om man vill kopiera filen till sitt projekt Jag brukar göra så, annars skapas bara en länk till filen. Men det kan sluta med att veta vart alla original filer ligger sparade. Kopierar man så hamnar alla filer som används med i projektet.

View innehåller bara lite inställningar för vad och i vilken ordning saker ska visas i editorn. Inget viktigt.

Build däremot måste göras efter varje gång man lagt till nya assets till projektet. annars kommer man inte hitta filerna när man väl försöker ladda in filerna i koden. Så tryck på Build -> Build.

Rebuild bygger bara om dom assets som har ändrat på sig. Ifall du gjort någon ändring i någon bild så kommer bara den filen att byggas om.

Clean kommer ta bort alla byggda filer och bygga om allt från början. Jag har aldrig behövt göra det så jag kan inte ge något bra exempel på när det skulle behövas göras.

Slutligen ar vi debug mode som verkar vara lite buggig eller experimentell. Den kraschar för mig och jag har aldrig fått ut nått vettigt av att bocka i den.

Skulle det vara något problem i bygget "som vi kommer märka när vi kommer till shaders" så kommer det stå i loggen när vi bygger. Det borde inte kunna bli några fel när vi bygger en bildfil bara. Men det kan vara bra att veta.

Nu ser iallafall min Editor ut så här.



Då kan vi stänga fönstret och hoppa in i konstruktorn. Där lägger vi till raden Content.RootDirectory = "Content";
Med det så talar vi om vart programmet ska hitta våra assets filer någonstans.
  1.  
  2.         public GameTutorialGame()
  3.         {
  4.             graphics = new GraphicsDeviceManager(this);
  5.             Content.RootDirectory = "Content";
  6.         }
  7.  


i initialize funktionen så tar vi bort den gamla koden för att ladda in filer
  1.  
  2.         protected override void Initialize()
  3.         {
  4.             spriteBatch = new SpriteBatch(GraphicsDevice);
  5.  
  6.            
  7.             base.Initialize();
  8.         }
  9.  


och nu hoppar vi in i LoadContent() funktionen skriver vi in

enBild = Content.Load("Assets\\Graphics\\bild");
  1.  
  2.         protected override void LoadContent()
  3.         {
  4.             enBild = Content.Load<Texture2D>("Assets\\Graphics\\bild");
  5.             base.LoadContent();
  6.         }
  7.  


Glöm int att man måste använda sig av dubbla backslashar eller skriva @ först innan strängen
(@"Assets\Graphics\bild")

Om allt är i sin ordning så borde programmet gå att köra. kontrollera sökvägarna ordentligt om du inte får det att funka.
Det är på det här viset vi kommer ladda in våra assets framöver.
Nästa gång ska vi utforska Spritebatchen lite mer. Det kommer bli ett lite roligare avsnitt än vad det här kanske var. på återseende.

Monogame del 4, [Ladda texturer från disk]

Välkommen till del 4.
Det finns mycket att skriva om vad det gäller texturer och hur dom fungerar och vad dom används till. Texturer har en uppsjö olika användningsområden varav ett är att presentera en bild på skärmen. Texturen eller "bilden" är egentligen en buffer eller en blobba minne bestående av en eller fler pixlar. Vi kan säga att det är en lång array av pixlar. Arrayens längd eller minnets storlek är lika med bilden bredd * höjd. Så är bilden 100 pixlar bred och 100 pixlar hög så kommer arrayen ha en längd på 10000 element. Vardera element i arrayen kan variera, men så länge så säger vi att vardera elemten är av typen Color. Så ska tar vi och expanderar det i ett eget avsnitt om bara färger. Men jag vill komma lite längre i den här serien först.

Nu tar vi och börjar om dagens ämne. att ladda texturer från fil från disk.

Innan vi börjar så behöver vi ha en bild att ladda, lämpligen av formatet .png men .bmp och .jpg fungerar också bra för dagens ändamål. Men .png är filformatet vi kommer använda oss av framöver. Jag bjuder på en bild här om du inte kan hitta något eller rita en egen bild just nu.


Spara filen i samma mapp som din exe fil ligger, som i mitt fall är det i följande mapp,
C:\Users\...\source\repos\GameTutorial\GameTutorial\bin\Windows\AnyCPU\Debug\
Det kan såklart skilja sig ifrån hur det ser ut hos dig, men den sista delen efter GameTutorial\ borde inte skilja sig.

Där sparar vi filen och sedan börjar vi skriva lite kod.

Först så deklarerar vi en variabel högst upp i våran klass så här:
  1.  
  2.         Texture2D enBild;
  3.  


Sen tänkte jag att vi skulle öppna upp en ny funktion som Monogame anropar efter Initialize(). Som ni kanske mins från tidigare inlägg så anropas sedan LoadContent() och det är i den funktionen Monogame vill att vi ska ladda in saker till vårat program. Så vi lägger till den funktionen i vårat program

  1.  
  2.         protected override void LoadContent()
  3.         {
  4.             base.LoadContent();
  5.         }
  6.  


Och med följande kod så laddar vi in bilden som en textur och sparar den i våran textur variabel som vi döpte till enBild.

  1.  
  2.             try
  3.             {
  4.                 using (FileStream fs = new FileStream("bild.png", FileMode.Open))
  5.                 {
  6.                     enBild = Texture2D.FromStream(GraphicsDevice, fs);
  7.                 }
  8.             }
  9.             catch (FileNotFoundException e)
  10.             {
  11.                 throw e;
  12.             }
  13.  


Snabbt förklarat vad varje del i koden gör så kopslar vi in koden i ett "try" block ifall någonting går snett så bakar koden ut ur try blocket och kastar en exception på vad som gick fel.
sedan använder vi oss av ett using statement på filströmmen så vi är säkra på att filströmmen kommer stängas när vi har laddat klart filen eller om något går fel. här anger vi ockås vad filen heter och i vilken mapp den ligger. Nu är ingen mapp anged och då antas filen ligga i samma mapp som den körbara filen. "exe filen".

Inga konstigheter hoppas jag. Det här är ingen kors i C#, utan mer ett utforskande och förklarande av monogame. Så är det oklarheter angånde koden så får ni fråga så ska jag förklara eller kanske ännu bättre googla på det så kommer du säkerligen finna svar snabbare.

Nu är vi iallfall redo att rita bilden och det gör vi på samma sätt som vi gjort tidigare i Draw() mellan spritebatch.begin() och end()...


  1.  
  2.         protected override void Draw(GameTime gameTime)
  3.         {
  4.             GraphicsDevice.Clear(Color.Black);
  5.             spriteBatch.Begin();
  6.            
  7.             spriteBatch.Draw(enBild, new Vector2(100,100), Color.White);
  8.  
  9.             spriteBatch.End();
  10.  
  11.             base.Draw(gameTime);
  12.         }
  13.  



Om allt är i sin ordning så borde ni få upp ett fönster som ser ut så här.

Jag lämnar lite kodsnuttar som ni kan testa och fundera över som vanligt. Tack för den här gången...

  1.  
  2.             spriteBatch.Draw(enBild, new Rectangle(100, 100, 50, 50), Color.White);
  3.  


  1.  
  2.             spriteBatch.Draw(enBild, new Vector2(0, 0), Color.Gray);
  3.  


  1.  
  2.             spriteBatch.Draw(enBild, new Rectangle(100, 100, 50, 50), Color.Yellow);
  3.             spriteBatch.Draw(enBild, new Rectangle(200, 100, 50, 50), Color.Red);
  4.             spriteBatch.Draw(enBild, new Rectangle(100, 200, 50, 50), Color.Green);
  5.             spriteBatch.Draw(enBild, new Rectangle(200, 200, 50, 50), Color.Blue);
  6.  


  1.  
  2.             spriteBatch.Draw(enBild, GraphicsDevice.Viewport.Bounds, Color.White);
  3.  

Monogame del 3, [Mer om texturer]

Välkommen till del tre i min lilla Monogame tutorial på svenska.

Vi testade ju på lite texturer i förra delen och jag lämnade er med lite olika exempel ni kunde laborera med. Det sista exemplet var ltie spännade då det trots att vi bara angett blå och röd i våran textur,


Det är egentligen en väldig massa saker som händer och jag tänker idag bara lyfta på locket lite för att vi ska få en grundläggande förståelse för vad det är som händer.
När vi skapar vår textur som vi kallar för papper så anger vi att den ska vara 2 pixlar lång och 1 pixel hög, totalt kommer våra textur bestå av två pixlar den ena anger vi att den ska vara röd och den andra blå. Det är allt vi gör när vi skapar våran textur.

När vi sedan ritar texturen med följande kod
  1.  
  2.             spriteBatch.Begin();
  3.             spriteBatch.Draw(papper, new Rectangle(10, 10, 100, 100), Color.White);
  4.             spriteBatch.End();
  5.  

Så vill vi att våran textur ska täcka upp en yta som är placerad 10 pixlar ut från höger och 10 pixlar ner från överkant på fönstret. Och sen vill vi att den ska fylla upp 100 pixlar i bredd och 100 pixlar i höjd. Det blir ju lite omöjligt när våran textur som vi ska rita bara är 2 pixlar bred och 1 pixel hög.

När grafikkortet ska rita upp en textur på det här viset så kan den använda sig av lite olika tekniker, men den vanligaste tekniken kallas på engelska för bilinear texturefiltering. Krångligt ord måhända men det den gör är att räkna om dom där 100 pixlarna i bredd och 100 pixlarna i höjd till två värden mellan 0 och 1, dessa två koordinater kallar man för U samt V. Man kan säga vart procentuellt i ytan där man ritar är man sedan gör man samma sak för den textur man ska rita ifrån. I det här fallet "papper"

Så första pixeln som ritas är 0% vertikalt in i rektangeln och 0 % horisontellt det blir UV kordinaterna 0U och 0V, man kan skriva ihop det i vektor form och få 0.0 ,0.0. Vektorer är lite av ett ämne för sig som är lite mer matematiskt. 0% in i våran textur så har vi en pixel som är röd, eftersom texturen bara är en pixel hör så spelar det ingen roll vad det vertikal värdet är så vi kan utesluta det, det kommer ändå bara att bli samma som det horisontella värdet. nästa pixel är 1% in och 1 % in är fortfarande röd, inte fören vi kommer till 50 procent in i texturen så ändrar det si och blir blå.

Varje gång som grafikkortet kollar i texturen vad det är för färg i uv koordinaterna så gör den en genomsnitts beräkning som resulterar i att våran bild när den ritas ut blir en färgskala mellan dom färger som finns i texturen. Det här gör att vi kan rita våran textur i vilken storlek som helst och grafikkortet kommer hjälpa oss att skala om bilden så att det passar och ser så bra ut som möjligt. Om vi skulle vilja ha ett mer pixelperfekt resultat så måste vi stänga av den där bilinear filtering och det kommer vi komma till framöver när vi går in djupare i vad spritebachen gör och kan göra.

Det var allt jag ville gå igenom idag men innan vi avslutar så tar vi lite nya exempel på vad vi kan rita med våra texturer.

  1.  
  2.             papper = new Texture2D(GraphicsDevice, 2, 2);
  3.             papper.SetData<Color>(new Color[] { Color.Red, Color.Blue, Color.Green, Color.Yellow });
  4.  

Det här blir en 2 * 2 pixlar stor gradient textur med 4 färger i


  1.  
  2.     public class GameTutorialGame : Game
  3.     {
  4.         GraphicsDeviceManager graphics;
  5.         SpriteBatch spriteBatch;
  6.         Texture2D papper1;
  7.         Texture2D papper2;
  8.         public GameTutorialGame()
  9.         {
  10.             graphics = new GraphicsDeviceManager(this);
  11.            
  12.         }
  13.  
  14.         protected override void Initialize()
  15.         {
  16.             spriteBatch = new SpriteBatch(GraphicsDevice);
  17.             papper1 = new Texture2D(GraphicsDevice, 255, 255);
  18.             Color[] colors = new Color[255 * 255];
  19.             for (int y = 0; y < 255; y++)
  20.             {
  21.                 for (int x = 0; x < 255; x++)
  22.                 {
  23.                     colors[(y * 255) + x] = new Color(x, y, 0);
  24.                 }
  25.             }
  26.             papper1.SetData<Color>(colors);
  27.  
  28.             papper2 = new Texture2D(GraphicsDevice, 2, 2);
  29.             papper2.SetData<Color>(new Color[] { Color.Black, Color.Red, Color.Green, Color.Yellow });
  30.  
  31.             base.Initialize();
  32.         }
  33.  
  34.         protected override void Update(GameTime gameTime)
  35.         {
  36.             if (Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit();
  37.             base.Update(gameTime);
  38.         }
  39.  
  40.         protected override void Draw(GameTime gameTime)
  41.         {
  42.             spriteBatch.Begin();
  43.             spriteBatch.Draw(papper1, new Rectangle(10, 10, 255, 255), Color.White);
  44.             spriteBatch.Draw(papper2, new Rectangle(300, 10, 255, 255), Color.White);
  45.             spriteBatch.End();
  46.  
  47.             base.Draw(gameTime);
  48.         }
  49.     }
  50.  

Nu skapar vi två texturer, papper1 och papper2. I papper1 så sätter vi alla pixlarna manuellt i färgskalan svart till röd till grön till gul. papper två har bara 4 pixlar med samma grundfärger, när vi sedan ritar ut dom bredvid varandra så kan vi jämföra hur bra den där bilinjära filtereffekten är. Tänk på att den bara är 4 pixlar stor men medans den andra är 65025 pixlar stor, men skapar ändå ett ganska bra resultat.



Monogame del 2, [Rita rektanglar]

Välkommen tillbaka.

Från och med nu så ska jag försöka hålla mig till lite kortare avsnitt och ämnen
Idag tänkte jag att vi ska försöka rita något i vårat fönster. Men vi börjar städa upp lite ifrån föregående avsnitt då vi testade ordningen som monogame anropar våran applikation. Vi återkommer till det vartefter som det blir aktuellt att ta upp det.

Vi städar bort alla funktioner utom konstruktorn, Initialize, Update och Draw. Så kvar har vi följande kod:

  1.  
  2. using Microsoft.Xna.Framework;
  3. using Microsoft.Xna.Framework.Input;
  4.  
  5. namespace GameTutorial
  6. {
  7.     public class GameTutorialGame : Game
  8.     {
  9.         GraphicsDeviceManager graphics;
  10.  
  11.         public GameTutorialGame()
  12.         {
  13.             graphics = new GraphicsDeviceManager(this);
  14.         }
  15.  
  16.         protected override void Initialize()
  17.         {
  18.             base.Initialize();
  19.         }
  20.  
  21.         protected override void Update(GameTime gameTime)
  22.         {
  23.             if (Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit();
  24.             base.Update(gameTime);
  25.         }
  26.  
  27.         protected override void Draw(GameTime gameTime)
  28.         {
  29.             base.Draw(gameTime);
  30.         }
  31.     }
  32. }
  33.  


Vårat svarta fönster ska fortfarande köras om vi trycker på F5.


För att kunna rita nånting på skärmen så måste vi ha två saker. En Textur och en Batcher eller SpriteBatcher som det heter i Monogame.

En Textur är ett objekt som närmast kan beskrivas som ett genomskinligt papper. När vi sen kommer rita i fönstret så är det som att vi slänger på fler och fler av dessa Textur-papper. PÅ ett papper har vi ett träd, ett annat ett hus och kanske en bakgrund med lite berg. alla dessa papper bygger tillsammans upp en färdig bild som vi sedan vill visa upp i fönstret.

Och det är med spritebatchen som hjälper oss med just den delen att komponera ihop och lagra alla papper i ordning. För det är något som är ganska tidskrävande för en dator.

Vi börjar med att skapa en textur, (ett genomskinligt papper). Det gör vi genom att deklarera en variabel som vi döper till papper av typen Texture2D, Texture2D ligger i ett nytt namespace som heter Graphics under MonogameFramework. Så vi måste lägga till det namespacet också.
Vi ska prata mer om vad en texture faktiskt egentligen är senare någon gång, papper kanske inte är den bästa liknelsen, men det tar vi då.

När vi deklarerat våran texture utanför konstruktorn så behöver vi i initiera den. Det gör vi i funktionen Initialize genom att skriva:
papper = new Texture2D(GraphisDevice, 1,1); GraphicsDevice är ett objekt som vi får av Monogame, men om vi skriver den här koden i konstruktor istället så har inte graphicsdevice blivit initierad än och vi kommer få ett null exception error på graphicsdevice. Om ett objekt inte initierats så har det värdet null. Vilket innebär att det inte har något värde alls. det är som en tom ballong.

GraphicsDevice måste iallafall vara med för att hjälpa oss bygga upp texturen, 1, 1 säger att texturen ska vara en pixel bred och en pixel hög.

Vi ska nu ha följande kod.

  1.  
  2. using Microsoft.Xna.Framework;
  3. using Microsoft.Xna.Framework.Graphics;
  4. using Microsoft.Xna.Framework.Input;
  5.  
  6. namespace GameTutorial
  7. {
  8.     public class GameTutorialGame : Game
  9.     {
  10.         GraphicsDeviceManager graphics;
  11.         Texture2D papper;
  12.  
  13.         public GameTutorialGame()
  14.         {
  15.             graphics = new GraphicsDeviceManager(this);
  16.         }
  17.  
  18.         protected override void Initialize()
  19.         {
  20.             papper = new Texture2D(GraphicsDevice, 1, 1);
  21.             base.Initialize();
  22.         }
  23.  
  24.         protected override void Update(GameTime gameTime)
  25.         {
  26.             if (Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit();
  27.             base.Update(gameTime);
  28.         }
  29.  
  30.         protected override void Draw(GameTime gameTime)
  31.         {
  32.             base.Draw(gameTime);
  33.         }
  34.     }
  35. }
  36.  


Nu vill vi såklart rita ut vårat nya fina papper i fönstret. Det gör vi i Draw funktionen. Men först behöver vi ett nytt objekt. SpriteBatch som vi talade om tidigare. Det är ett ganska stort och invecklat objekt men vi tar en sak i taget utan att gå alldeles för långt ner på djupet.
Spritebatchen måste vi skapa sälva så vi deklarerar den tillsammans med vårat papper så här

  1.  
  2.         SpriteBatch spriteBatch;
  3.  

och i Initialize initierar vi den genom att skriva
  1.  
  2.         spriteBatch = new SpriteBatch(GraphicsDevice);
  3.  


Toppen, nu är allt klart för att börja rita.
Vi hoppar över till Draw funktionen och lägger till följande kod.

  1.  
  2.             spriteBatch.Begin();
  3.             spriteBatch.Draw(papper, new Rectangle(10,10,100,100), Color.White);
  4.             spriteBatch.End();
  5.  
  6.             base.Draw(gameTime);
  7.  


spriteBatch.Begin(); Säger att vi vill börja rita, talar vi inte omd et så kommer inte spritebatchen tillåta oss att rita nånting.
spriteBatch.Draw(papper, new Rectangle(10,10,100,100), Color.White); säger att vi vill rita vårat papper, vart den ska ritas anger vi med new Rectangel(10,10,100,100) vilket säger att vårat papper börjar 10 pixlar ifrån vänster och 10 pixlar ifrån toppen av vårat fönster. Sen säger den att vårat papper är 100 pixlar bred och 100 pixlar hög.


Rectangel(horisontellposition, vertikal position, bredd, höjd)



Color.White är lite krånglig, men den talar om vilken färg vi vill blanda med. Så den färgen vi skriver här kommer blanda sig med den färg som är på texturen som vi ritar. Det är också något som vi kommer till senare.

Men vad händer då om vi kör vårat program. Det händer ju ingenting..? Samma gamla svarta fönster... Som vi pratade om så är vårat papper genomskinligt och ett genomskinligt papper ska ju såklart vara osynligt att se. För att rita något på vårat papper så måste vi skriva en ny lite invecklad rad kod uppe i Initialize, efter att vi initierat vårat papper.


  1.  
  2. papper.SetData<Color>(new Color[] { Color.White });
  3.  


när vi initierade vårat papper sa vi att det skulle vara 1 pixel brett och 1 pixel högt. totalt består vårat papper av en pixel totalt och den pixeln fyller vi med fägen vit.
papper.SetData anger att vi vill ange färg som data för vad texturen ska inehålla. (För en textur kan vara och inehålla mycket annat än färg) sen anger vi en array av färger med endast en komponent, Vit.

Våran kod i sin helhet ser nu ut så här

  1.  
  2. using Microsoft.Xna.Framework;
  3. using Microsoft.Xna.Framework.Graphics;
  4. using Microsoft.Xna.Framework.Input;
  5. using System;
  6.  
  7. namespace GameTutorial
  8. {
  9.     public class GameTutorialGame : Game
  10.     {
  11.         GraphicsDeviceManager graphics;
  12.         SpriteBatch spriteBatch;
  13.         Texture2D papper;
  14.  
  15.         public GameTutorialGame()
  16.         {
  17.             graphics = new GraphicsDeviceManager(this);
  18.            
  19.         }
  20.  
  21.         protected override void Initialize()
  22.         {
  23.             spriteBatch = new SpriteBatch(GraphicsDevice);
  24.             papper = new Texture2D(GraphicsDevice, 1, 1);
  25.             papper.SetData<Color>(new Color[] { Color.White });
  26.  
  27.             base.Initialize();
  28.         }
  29.  
  30.         protected override void Update(GameTime gameTime)
  31.         {
  32.             if (Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit();
  33.             base.Update(gameTime);
  34.         }
  35.  
  36.         protected override void Draw(GameTime gameTime)
  37.         {
  38.             spriteBatch.Begin();
  39.             spriteBatch.Draw(papper, new Rectangle(10,10,100,100), Color.White);
  40.             spriteBatch.End();
  41.  
  42.             base.Draw(gameTime);
  43.         }
  44.     }
  45. }
  46.  
  47.  


Vi kör igen och se på tusan vår första vita rektangel är ritad...


Det var allt för den här delen, men jag lämnar lite kodändringar som kan vara spännade att testa och leka med för att se vad som händer.

  1.  
  2. papper.SetData<Color>(new Color[] { Color.Blue});
  3.  

  1.  
  2. papper.SetData<Color>(new Color[] { Color.Red});
  3.  

  1.  
  2. spriteBatch.Draw(papper, new Rectangle(200,200,100,100), Color.White);
  3.  

  1.  
  2. spriteBatch.Draw(papper, new Rectangle(0,10,800,10), Color.Orange);
  3.  

  1.  
  2. spriteBatch.Draw(papper, new Rectangle(10,10,100,100), Color.White);
  3. spriteBatch.Draw(papper, new Rectangle(300,10,100,100), Color.White);
  4. spriteBatch.Draw(papper, new Rectangle(150,200,100,100), Color.White);
  5.  

Lite mer avancerat, fundera kring vad som händer här...
  1.  
  2. papper = new Texture2D(GraphicsDevice, 2, 1);
  3. papper.SetData<Color>(new Color[] { Color.Red, Color.Blue });
  4.  

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.

Raspberry PI. Del 1 (Introduktion)

Fösta delen av mitt kodäventyr i Raspberry Pi 3B+

Om du redan tycker dig ha lite koll på kortet så behöver du inte läsa allt detta, längst ner i inlägget beskriver jag dock lite kort hur du installerar Arms gcc kompilator med mer, kika gärna på det om du inte redan har koll på det. Del 2 kommer endast vara en länksamling och först i del 3 så ska vi börja skriva kod.

Jag fick tag på en Raspberry PI3B+ för ett tag sedan och har klurat lite på vad man egentligen ska ha den till, men efter lite googlande så hamnade jag på ett antal olika sidor som berättade hur man skriver sitt eget operativsystem från grunden, efter som hårdvaran är hyfsat lättillgänglig och det fins en del dokumentation så möjliggör Raspberryn att man faktiskt kan skriva sitt eget OS till den. Det här tyckte jag verkade otroligt spännande. Så det är det vi ska göra i den här serien tillsammans. Jag har inga tankar på att det ska vara ett fullskaligt OS utan det ska bli en portabel spelkonsol, men som kan kopplas upp med skärm, tangentbord och mus och sedan utveckla sina egna spel på maskinen. Men det slutgiltiga målet ligger så pass långt borta att jag hinner fila på detaljerna under tiden.

Vad är då en Raspberry PI?

Det är en nästan komplett dator till ytan lika stort som ett kreditkort, vid det här laget så finns det ett antal olika varianter och versioner med den senaste varianten som heter 4B som man faktiskt marknadsför som en desktop maskin som skulle kunna ersätta datorn som folk har hemma. Det är nog dock en överskattning, men det är dock ett spännande koncept. Jag har dock bara en Raspberry PI3B+ Så jag kommer bara gå igenom dess hårdvara och spec. Vill ni veta mer om något av dom andra korten så finns det massvis att läsa både på Raspberry PImen även på wikipedia



Vad finns så på kortet månntro
Denna har då en 4 kärnig 64bitars processor på 1.4 gHz, LAN, WLAN, blåtand, 3.5mm ljudutgång, en HDMI utgång, 4st USB portar, plats för ett mikro SD kort, 40 Pinnars GPIO (general-purpose input/output), CSI kamera port och slutligen en DSI Display port. Den kräver lite effekt så en laddare på minimum 2.5A är bra att ha, även en USB kabel av bra kvalitet som klarar att leverera ström till kortet. Processorn är en Broadcom BCM2837 vilket kan vara bra att lägga på minnet. Jag ska skapa ett inlägg med lite länkar som är bra att ha med sig när man programmerar Rasperry PI'n "bare metal" som är tanken att vi ska göra. Processorn är en del GPU och en del CPU, den är tillverkad endast för Raspberryn och återfinns inte på någon annan plattform, vilket nog är mest dåligt. Men tack vara dess popularitet så finns mycket dokumentation att få tag på. Det finns även en micro usb som används för att spänningssätta kortet och jag tror mig ha sett att den även kan användas som seriekommunikations port tillbaka till datorn. Den kommer även med 2 stycken led lampor, en grön och en röd. Den röda visar heter PWR och indikerar att kortet är spänningssatt. Den gröna är märkt ACT och ska blinka när det sker någon form av aktivitet på kortet, tex när den läser och skriver till lagringsmediet.

Att komma igång
Jag rekommenderar dig att läsa hur du gör för att komma igång på Raspberry PI's Det finns så pass bra guider för det så att jag inte känner att jag behöver gå igenom det här också. När du formaterat sd kortet och fått ner någon av dom rekommenderat operativ systemen som erbjuds så stoppar du det i PI'n och sedan kopplar in mus, tangentbord, skärm och slutligen usb-kabeln i mikro usbporten för att spänningssätta kortet (Viktigt att du gör det sist, annars kommer inte kortet boota som det ska) Därefter kommer kortet boota upp in i linux och du kan göra vad du vill.Vi ska dock inte köra linux eller något annat operativsystem på våran PI så nästan alla filer på kortet kommer vi ta bort, men det kommer jag beskriva när det är dax att börja koda.
Men för att skriva program till PI'n så måste vi ha en kompilator och då gör vi livet enklast för oss om vi väljer att använda den kompilatorn som Arm developer tillhandahåller på sin hemsida. Klicka på GNU-RM och sedan på download. Jag kommer använda mig av windows eftersom dom flesta fortfarande sitter på windows maskiner. Så enklast är att välja att ladda ner installationsfilen och sedan följa installations guiden.

När du installerat programmet kan du testa och se så att det funkar genom att skriva in

arm-none-eabi-gcc --version


i kommandotolken. Tryck på windows tangenten, skriv cmd och tryck enter.
Som svar ska du få något liknande detta:

arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 9-2019-q4-major) 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599]
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Grattis, det var allt vi behövede för att komma igång och skriva program till våran lilla PI.
Jag tänkte nog avsluta här, men hoppas kunna återkomma snart med del 2 och 3 i den här serien.

Ett nytt initiativ

"Bästa sättet att lära in är att lära ut"

Välkommen till gurksallad. Gurksallad är en domän jag släpat på under många år och under lika många år har genomgått olika iterationer och transformationer. Den här gången tänkte jag mig göra nånting lite mer permanent med den och posta lite artiklar angående programmering och kodning. Jag har valt att göra det på svenska för att jag vill att programmering ska bli lite mer lättillgängligt för alla och då framför allt yngre. Med det sagt så glider man lätt in på engelska när man programmerar så det kan hända att allt kommer bli lite svengelska stundvis.

Jag har redan plottat ut några kategorier som jag tänker mig att jag ska skriva om, men det kan kanske komma o ta ett tag innan jag hinner få in nånting i respektive kategori, det kan till och med hända att kategorierna kommer till att ändras.

C, C++, C#, Batch script, Beef language, DirectX, GCC, Monogame, MSVC, Open GL, Raspberry PI.

Vad tusan är det jag tänker mig att jag ska skriva då. Mja, det kommer nog vara lite olika, det kommer vara högt och lågt, mycket handlar om att jag själv har mycket inom programmering som jag vill lära mig utforska och testa, som tex kan jag inenting om DirectX men skulle vilja lära mig allt om det, närmast så fick jag tag på en Raspberry PI 3B+ och den har jag redan storslagna planer för vad jag skulle vilja göra med. Så det kommer nog komma ett inlägg om den ganska snart, det är till och med möjligt att det kommer bli en lång serie artiklar om Raspberryn. Annars kommer mycket handla om olika tekniker som används vid spel programmering, lite matte olika språk och hur man kan tänka sig att tackla olika problem när det kommer till kod och programmering.

Jag kommer dock inte skriva något om web programmering, javascript eller något annat web relaterat. Det finns säkert andra sidor som tycker sånt är roligt. Inte jag... Min förhoppning är att vi kommer vara lite mer hårdvarunära och lågnivå programmera och samtidigt försöka förmedla det roliga och spännande med att programmera. Samtidigt vill jag bygga upp en härlig kunskapsbank för mig och er att kunna komma tillbaka till är man glömt bort hur nånting fungerade eller hur jag valde att lösa ett specifikt problem.

Jag kommer inte vara rädd för att ändra i befintliga inlägg så bli inte förvånad om det händer, men det ska förhoppningsvis bara hända i förhoppning till att det ska bli bättre. Jag vill och hoppas också att jag kommer kunna hålla kommentarerna öppna så att man kan komma med frågor, förbättringar eller förslag. Men kommentarer på internet har alltid varit ett problem, så vi får se om det kommer kunna vara möjligt, annars får jag kanske se mig om efter någon annan lösning. Jag har ingen lust att sitta hela dagarna och moderera och radera spam.

Det var lite gran om den här sidan. Så tack för att du läst ända hit och på återseende.

/David



Test 2

Var vänlig och b0rtse från detta inlägg.

En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text...


En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text...

  1.  
  2. int main(int argc, char **argv)
  3. {
  4.     printf("Hello world!");
  5.     return 0;
  6. }
  7.  


En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text...

Dit nya hem

En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text...

Test

Det här är en av 2 testinlägg, bortse från dessa tack!

En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text...


En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text...

  1.  
  2. int main(int argc, char **argv)
  3. {
  4.     printf("Hello world!");
  5.     return 0;
  6. }
  7.  


En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text... En himla massa matnyttig text...



En himla massa matnyttig text...