16. Labyrinth
For the background labyrinth, we might simply draw an image. But in classic platform and arcade games, it was more usual to use small repetitive images called "tiles". If we draw a grid over the screen of many classic games, we will find these tiles:

We can imitate this by using a two dimensional array. In a first approach, we might use a 1 for a piece of wall and a 0 for a blank space:
byte [,] map = { {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, {1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1}, {1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,1,1,0,1,0,1,1,1,0,1,0,1,1,0,1}, {1,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,1}, {1,1,1,1,0,1,1,0,0,0,1,1,0,1,1,1,1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,1}, {1,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,1}, {1,0,1,1,0,1,0,1,1,1,0,1,0,1,1,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,1,1,0,1,0,1,1,1,0,1,0,1,1,0,1}, {1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} };
And we would need a new image for those "pieces of wall":
Image wallImage = new Image("wall.bmp");
Which we would draw in a repetitive sequence, analyzing the array:
// Background map for(int row=0; row<15; row++) { for(int column=0; column<17; column++) { if (map[row,column] == 1) SdlHardware.DrawHiddenImage(wallImage, column*32, row*32); } }
Now the whole source would be:
/* First mini-graphics-game skeleton Version D: background map for the labyrinth */ using System; public class Game02d { public static void Main() { bool fullScreen = false; SdlHardware.Init(800, 600, 24, fullScreen); Image dotImage = new Image("dot.bmp"); Image enemyImage = new Image("ghostGreen.bmp"); Image pacImage = new Image("pac01r.bmp"); Image wallImage = new Image("wall.bmp"); int x=40, y=12; int pacSpeed = 6; int amountOfDots = 100; int[] xDot = new int[amountOfDots]; int[] yDot = new int[amountOfDots]; bool[] visible = new bool[amountOfDots]; Random randomNumberGenerator = new Random(); for(int i=0; i<amountOfDots; i++) { xDot[i] = randomNumberGenerator.Next(0,760); yDot[i] = randomNumberGenerator.Next(40,560); visible[i] = true; } int amountOfEnemies = 4; float[] xEnemy = { 150, 400, 500, 600 }; float[] yEnemy = { 100, 200, 300, 400 }; float[] incrXEnemy = { 5f, 3f, 6f, 4.5f }; byte [,] map = { {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, {1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1}, {1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,1,1,0,1,0,1,1,1,0,1,0,1,1,0,1}, {1,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,1}, {1,1,1,1,0,1,1,0,0,0,1,1,0,1,1,1,1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,1}, {1,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,1}, {1,0,1,1,0,1,0,1,1,1,0,1,0,1,1,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,1,1,0,1,0,1,1,1,0,1,0,1,1,0,1}, {1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} }; int score = 0; bool gameFinished = false; // Game Loop while( ! gameFinished ) { // Draw SdlHardware.ClearScreen(); //Console.Write("Score: {0}",score); // Background map for(int row=0; row<15; row++) { for(int column=0; column<17; column++) { if (map[row,column] == 1) SdlHardware.DrawHiddenImage(wallImage, column*32, row*32); } } for(int i=0; i<amountOfDots; i++) { if (visible[i]) SdlHardware.DrawHiddenImage(dotImage, xDot[i], yDot[i]); } SdlHardware.DrawHiddenImage(pacImage, x, y); for(int i=0; i<amountOfEnemies; i++) SdlHardware.DrawHiddenImage(enemyImage, (int)xEnemy[i], (int)yEnemy[i]); SdlHardware.ShowHiddenScreen(); // Read keys and calculate new position if (SdlHardware.KeyPressed(SdlHardware.KEY_RIGHT)) x+=pacSpeed; if (SdlHardware.KeyPressed(SdlHardware.KEY_LEFT)) x-=pacSpeed; if (SdlHardware.KeyPressed(SdlHardware.KEY_DOWN)) y+=pacSpeed; if (SdlHardware.KeyPressed(SdlHardware.KEY_UP)) y-=pacSpeed; if (SdlHardware.KeyPressed(SdlHardware.KEY_ESC)) gameFinished = true; // Move enemies and environment for(int i=0; i<amountOfEnemies; i++) { xEnemy[i] += incrXEnemy[i]; if ((xEnemy[i] < 1) || (xEnemy[i] > 760)) incrXEnemy[i] = -incrXEnemy[i]; } // Collisions, lose energy or lives, etc for(int i=0; i<amountOfDots; i++) if ( visible[i] && (x > xDot[i] - 32) && (x < xDot[i] + 32) && (y > yDot[i] - 32) && (y < yDot[i] + 32) ) { score += 10; visible[i] = false; } // Pause till next fotogram SdlHardware.Pause(40); } } }
Which would look this way:

And as we "eat" dots, the labyrinth will become more visible:

If we have many different kinds of tiles, it might be easier to
"see" the labyrinth when we have a look at our source code, if we use
"chars" instead of numbers to define the map:
char [,] map = { {'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-'}, {'-','.','.','.','.','.','.','.','-','.','.','.','.','.','.','.','-'}, {'-','.','-','-','.','-','-','.','-','.','-','-','.','-','-','.','-'}, {'-','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','-'}, {'-','.','-','-','.','-','.','-','-','-','.','-','.','-','-','.','-'}, {'-','.','.','.','.','-','.','.','-','.','.','-','.','.','.','.','-'}, {'-','-','-','-','.','-','-','.','.','.','-','-','.','-','-','-','-'}, {'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.'}, {'-','-','-','-','.','-','.','-','.','-','.','-','.','-','-','-','-'}, {'-','.','.','.','.','-','.','-','.','-','.','-','.','.','.','.','-'}, {'-','.','-','-','.','-','.','-','-','-','.','-','.','-','-','.','-'}, {'-','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','-'}, {'-','.','-','-','.','-','.','-','-','-','.','-','.','-','-','.','-'}, {'-','.','.','.','.','-','.','.','.','.','.','-','.','.','.','.','-'}, {'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-'} }; ... for(int row=0; row<15; row++) { for(int column=0; column<17; column++) { if (map[row,column] == '-') SdlHardware.DrawHiddenImage(wallImage, column*32, row*32); } }
And even easier if we use an array of strings:
string [] map = { "-----------------", "-.......-.......-", "-.--.--.-.--.--.-", "-...............-", "-.--.-.---.-.--.-", "-....-..-..-....-", "----.--...--.----", ".................", "----.-.-.-.-.----", "-....-.-.-.-....-", "-.--.-.---.-.--.-", "-...............-", "-.--.-.---.-.--.-", "-....-.....-....-", "-----------------" }; ... for(int row=0; row<15; row++) { for(int column=0; column<17; column++) { if (map[row][column] == '-') SdlHardware.DrawHiddenImage(wallImage, column*32, row*32); } }
(Note that we now use "map[row][column]" to access each position, instead of "map[row,column]" as it is not a two-dimensional array any more).
There is a new image ("wall.bmp") in our project, so you can
download again the whole project, including images, DLL files and the
auxiliary "SdlHardware.cs" file. It is in the "Google Code" page for this
project. This version is called "SdlMuncher004.zip":
code.google.com/p/sdl-muncher/