Using C# and the Monogame framework, I remade the first level of Super Mario from the NES. My objective of my Super Mario project was to learn how to make a 2D map game. A popular method of making a 2D map is to make it a text file, loop over the file, and render a sprite depending on the charater of the current index.
Above is the text file that the project reads to make the map. 0's are mapped to nothing, the 'i' character is mapped to the item block sprite, the 'b' character is mapped to the block sprite, and finally the 'g' character is mapped to the ground sprite.
First I made all the variables I would need. StreamReader to read the file, a texture, ints to keep track of rows, columns, tile width, map dimensions and the character ASCII. I also need a list of a class I made called FileHandler. FileHandler keeps track of line number, tile letter, column number, and collision rectangle around the sprite
StreamReader file;
Texture2D tile;
public List<FileHandler> fileHandler;
int row, curCol, tileSide, charNumber;
public int mapWidth;
Then I initialized the values I needed. I tell the program the name of the text file, initialize the list, set the row and column, and finally the dimension of the sprite.
public Map()
{
file = new StreamReader("map.txt");
fileHandler = new List<FileHandler>();
row = 0;
curCol = 0;
tileSide = 32;
}
Next, I needed to load the sprite sheet.
public void Load(ContentManager cm, string spriteName)
{
tile = cm.Load<Texture2D>(spriteName);
}
After than, I wrote the update method. This runs through the file, and determines what character it is on, and adds a tile to the FileHandler list if needed. I keep track of the rows by checking if the character is a line break, if it is, I add 1 to my rows counter variable. If the row it at the end, I multiple the current column by the sprite's width to calculate the mapWidth. I use that later in my Camera class. I also reset the column back to 0.
public void Update(GameTime gameTime)
{
while ((charNumber = file.Peek()) >= 0)
{
int next;
if (charNumber != 13 && charNumber != 10)
{
fileHandler.Add(new FileHandler { LineNumber = row, TileLetter = (char)file.Read(), ColNumber = curCol, CollisonRect = new Rectangle(tileSide*curCol, tileSide*row, 32, 32) });
curCol++;
}
else if (charNumber == 10)
{
row++;
mapWidth = curCol * tileSide;
curCol = 0;
next = file.Read();
}
else
{
next = file.Read();
}
}
}
Finally, I draw the map to the screen. I loop through the list and draw the sprite that corresponds to the letter. 0's are mapped to nothing, the 'i' character is mapped to the item block sprite, the 'b' character is mapped to the block sprite, and finally the 'g' character is mapped to the ground sprite.
public void Draw(SpriteBatch sb)
{
foreach (FileHandler item in fileHandler)
{
if (item.TileLetter == 'g')
{
sb.Draw(tile, item.CollisonRect, new Rectangle(0, 0, tileSide, tileSide), Color.White);
}
else if (item.TileLetter == 'i')
{
sb.Draw(tile, item.CollisonRect, new Rectangle(tileSide, 0, tileSide, tileSide), Color.White);
}
else if (item.TileLetter == 'b')
{
sb.Draw(tile, item.CollisonRect, new Rectangle(tileSide*2, 0, tileSide, tileSide), Color.White);
}
else
{
sb.Draw(tile, item.CollisonRect, new Rectangle(tileSide * 3, 0, tileSide, tileSide), Color.White);
}
}
}