Introduction
This page is going to go through the process of creating a basic console game where you move a character around the console screen to collect points which attribute to your score. It will go through the basics of a game loop, more advanced key management and screen manipulation. Some of you may remember that this was set as an extension exercise around week eight or nine.
A game loop
The simple idea behind a game loop is to keep the game processing until we want to exit it. This can easily be achieved with a while loop and a boolean variable. There are three basic concepts that each game does inside this loop, and these are: updating from inputs, general game updating and drawing the output.
// Initialize variables bool gameRunning = true; while(gameRunning) { // Process the game data here // 1. Update inputs // 2. Update game logic // 3. Draw new updates }
Updating Input
When you call the Console.ReadKey() method you'll notice that your program stops and waits for input to be pressed, which is not what we want. Once we have the game working we want it to continue updating other objects in the game, even if the player is not moving.
The following section of code goes directly into the game loop and it takes advantage of the C# property Console.KeyAvailable. This property returns true when the user has pressed a key and nothing has processed it. If this is true, then the next Console.ReadKey() that we do will not wait for input. We can take advantage of this and process keys only if they are available without pausing the game at any point.
Another problem we have is when you press a key it appears on the console screen. We don't want a bunch of jargon on the screen while were playing it so we need to hide it by setting the intercept variable of Console.ReadKey() to true. This means that we take full control of what output is displayed from this key press. Why not try making a reversed keyboard using this method? It's certainly pointless fun but it can help you clearly understand it.
// Initialize variables bool gameRunning = true; ConsoleKeyInfo userKey; while(gameRunning) { // Begin with processing input if(Console.KeyAvailable) { // We have input, process accordingly userKey = Console.ReadKey(true); switch(userKey.Key) { case ConsoleKey.Escape: // Exit the game when pressed gameRunning = false; break; } } }
Moving a player around
Now we have a game that runs until we exit it or press escape we can focus on getting our player on the screen. The default console window size is 80x25 and begins at zero when we reference it. We need to create two variables to store the X and Y of the character and also begin processing the Up, Down, Left and right keys. While we do this we'll need to check the player won't go off the screen.
// Initialize variables bool gameRunning = true; ConsoleKeyInfo userKey; int locationX = 0; int locationY = 0; while(gameRunning) { // Begin with processing input if(Console.KeyAvailable) { // We have input, process accordingly userKey = Console.ReadKey(true); switch(userKey.Key) { case ConsoleKey.LeftArrow: // See if we can move left if (locationX > 0) { // Move ourself left locationX = locationX - 1; } break; case ConsoleKey.RightArrow: // See if we can move right if (locationX < 78) { // Read the System Caret section for // more information on why you should // use 78 instead of the 79 here. locationX = locationX + 1; } break; case ConsoleKey.UpArrow: // See if we can move up if (locationY > 0) { // Move ourself up locationY = locationY - 1; } break; case ConsoleKey.DownArrow: // See if we can move down if (locationY < 24) { // Move ourself down locationY = locationY + 1; } break; case ConsoleKey.Escape: // Exit the game when pressed gameRunning = false; break; } } }
The Caret
There is something that is often overlooked and causes some considerable bugs but it is relatively simple to understand. When you type in the console you will notice the system caret indicating where your next character will be placed. This means that when we come to typing our player on the screen at a later point we'll actually be typing two characters.
I am the system caret, and I look like this: _
In Microsoft Word I look like this: |
You will usually find me blinking.
If you havn't guessed yet, this means that when you move the character to the very edge of the screen the system caret wraps around to the line below it. At the moment this won't concern you until you start drawing levels behind the player as the caret will erase it. If you move the player into the bottom right hand corner of the screen you'll notice the screen moves downwards to compensate the system caret. To stop this, simply deny the player from moving into the last column of the screen.
Drawing the player
Yes, I'm aware we skipped a whole section, updating game logic, however there is no logic in this game to be updated. So, I'm skipping to drawing the player at the currently set location. We'll first need to clear the screen to remove the last players location then move the caret to the new location and simply write out the character.
// Initialize variables bool gameRunning = true; ConsoleKeyInfo userKey; int locationX = 0; int locationY = 0; while(gameRunning) { // Begin with processing input if(Console.KeyAvailable) { // We have input, process accordingly userKey = Console.ReadKey(true); switch(userKey.Key) { case ConsoleKey.LeftArrow: // See if we can move left if (locationX > 0) { // Move ourself left locationX = locationX - 1; } break; case ConsoleKey.RightArrow: // See if we can move right if (locationX < 78) { // Read the System Caret section for // more information on why you should // use 78 instead of the 79 here. locationX = locationX + 1; } break; case ConsoleKey.UpArrow: // See if we can move up if (locationY > 0) { // Move ourself up locationY = locationY - 1; } break; case ConsoleKey.DownArrow: // See if we can move down if (locationY < 24) { // Move ourself down locationY = locationY + 1; } break; case ConsoleKey.Escape: // Exit the game when pressed gameExit = true; break; } } // Draw the player Console.Clear(); Console.SetCursorPosition(locationX, locationY); Console.Write("@"); }
Final Considerations
Well, now that you have a character moving around at your own free will you might want to consider why the game flickers.I'll leave you with a tiny peice of information: when you call Console.Clear() the screen must go black for the user to see before anything esle can be drawn, thus causing the flicker. Is there another way to remove the old player without causing a flicker?
If you want to take the game further try loading levels from a file and displaying them on the screen? If you're going to take this challenge up then you should be aware that redrawing the level every single iteration of the game loop is horribly inefficient and you will spend half the game waiting for the level to be shown. A solution to this is to draw the level once and manipulate it to your needs when you need to.
After I created levels and had them working properly I moved into adding small scriptable sections underneath the map files that allowed me to create highly primitive AI and interactivity. If you need help with any of the tutorial or extending your games functionality please don't hesitate to ask someone.
Nice Job :)
just a few comments.
bug in the
while(gameExit = false)
should be double equals (==)... It's neater to use (!gameExit) instead. Although, it's even neater to phrase your boolean in the positive light e.g.:
while(gameRunning)
In order to get the flashing to stop you need to stop using the Clear() method. You need to write your characters to an off screen buffer and each time to draw, you should call Console.Write() just once with a string that contains every single character.
Something to thing about :)
Cheers
John
19/12 - I've updated it to include the more positive game loop condition and fixed
some repeated comments. Thanks for the feedback ^.^
[[/code]]
[[/code]]