The Basics of VB.NET Through Text Game Development
This is the second article in a series covering how to learn VB.NET by developing a text-based game. The last article gave a basic introduction into the development of our application, and this one will pick up where it left off. So if you're ready to keep learning, keep reading!
Before we move on, I'd like to point out a few more things concerning WriteLine. First, although we passed a String (the expected type of a series of characters) as an argument earlier, we can pass just about anything as an argument, and WriteLine will print its textual representation. For example, here we print out an Integer (Visual Basic's version of a thirty-two bit signed integer, or the System.Int32 type—there is also a System.Int16, Short and a System.Int64, Long):
Console.WriteLine(4)
If you place the above line of code into our module and run it, the number four will be printed out. If we pass any type of object, a textual representation of it will be printed out as well. How is this representation determined? The object's ToString procedure is called on to return a String. This procedure is defined in the Object class, which is the ultimate base class of everything, though we do not need to explicitly inherit from it when constructing a new class. By default, the procedure will simply return the name of the class, but this procedure can be overridden to return any String. We can modify our program to call ToString:
Sub Main()
Console.WriteLine("Hello World.".ToString())
Console.ReadKey()
EndSub
The effect is the same.
The second thing I'd like to point out is WriteLine's ability to work with format strings. Let's say we have two objects. One is an Integer and one is a String. We need to print both of them out on the same line, along with some other text. One way to do this would be to append everything together using the concatenation operator, &:
Console.WriteLine("Two objects: " & 4 & ":-)")
This approach certainly works, but it's a bit ugly, and would be even uglier if we had more objects. A better way is to use a format string to specify what will go where. Here, we use a format string:
This example will produce the exact same output as the earlier example, but this version looks a lot neater. Notice how, above, we have two bracketed numbers, starting with 0. These bracketed numbers will be replaced with objects passed as arguments in the exact same order that the objects are passed. So, {0} is replaced by the first object passed, {1} is replaced by the second, and so forth.
So, now we know how to write a line of text to the console, but this, of course, won't be enough for a project like ours. Recall the nature of the game: we have a dungeon made up of tiles, creatures, and items, all of which are really characters. We need to be able to draw every one of these, and we need to be able to redraw them if they move or are covered up or uncovered. For example, if the player walks from one space to another space, then we need to erase the tile he steps on, replacing it with the image of the player, and we need to redraw the tile that he stepped off of, erasing the old image of the player. Moreover, things may need to be represented in different colors, not just the default gray. Clearly, WriteLine isn't going to accomplish all of this, and, in fact, our use of it in the game will be limited.
WriteLine writes out a string of text and then moves to the next line. In most cases, this isn't what we want at all. We may only need to write one character at a specific location on the screen. To do this, we need a very similar procedure. Write. Write operates much like WriteLine, except it doesn't move to a new line.
Earlier, we wrote “Hello World” out to the screen like this:
Console.WriteLine("Hello World.")
Though it isn't terribly obvious unless you pay attention to the blinking cursor, WriteLine moved to the next line. If we were to write some more text to the screen, it would be more noticeable, since the new text would start on the next line.
However, a combination (of no real-world value) of Write and WriteLine would have achieved the exact same effect:
Console.Write("Hello ")
Console.WriteLine("World.")
First, we make a call to Write, writing “Hello “ (with a space) out to the screen. We then write “World.” out to the screen with WriteLine. Since Write doesn't add a new line, the call to WriteLine continues writing on the same line.
We can break it up even further, into three calls, while still achieving the same effect:
Console.Write("Hello ")
Console.Write("World.")
Console.WriteLine()
While the above sample has no practical purpose, it does illustrate something else we should know: WriteLine can be called with no arguments. If this happens, it simply moves the cursor to the next line. This is useful in some situations.
But, as noted earlier, we can't adopt a line-by-line approach to creating our game's interface. Rather than writing one line and then moving onto another in a linear fashion, we need to be able to update individual characters. So, how does Write allow us to do this? Alone, it doesn't, but when used in conjunction with another procedure, it allows us to write a character in any location in the console. This is exactly what we need.
Write and WriteLine write to the screen wherever the cursor is at. Usually, the cursor will just move straight down the screen. A line will be written out, and then the cursor will move to the first column of the next row in the console in preparation for another line being written. This default behavior is of little use to us, but if Write follows around the cursor, then all we need to do is change the location of the cursor. Then we can write characters out anywhere.
We can move the cursor around using the SetCursorPosition procedure. It takes two Integer arguments, which basically form a standard (x, y) coordinate. The first argument indicates the horizontal position of the cursor (the column that the cursor is in) and the second argument indicates the vertical position of the cursor (the row that the cursor is in). Note that the top left edge of the console is the zeroth column of the zeroth row. So, to write something at the very top left, we'd do this:
Console.SetCursorPosition(0, 0)
Console.Write("Hello World.")
Note that after running the above code, the cursor will be immediately after the written text. So, if we write more text to the screen, it will follow right after “Hello World.”
Feel free to go ahead and play around a bit more with SetCursorPosition. Here, we write “Hello World” well off the top left edge of the screen:
Console.SetCursorPosition(10, 10)
Console.Write("Hello World.")
We can also set the position of the cursor through the CursorLeft and CursorTop properties of Console. The following code produces the same effect as the above code:
Console.CursorLeft = 10
Console.CursorTop = 10
Console.Write("Hello World.")
We can use these same two properties to read the position of the cursor. Here, we display the position of the cursor using Write and a format string:
Console.Write("The cursor is at ({0}, {1})", Console.CursorLeft, Console.CursorTop)
I'm going to switch topics for just a moment because this is a good time to introduce an aspect of Visual Basic's syntax. Notice how the above line is a little long. You may be tempted to break long lines of code into two or more lines. This is, of course, possible, but you can't just break them up with the return key. Instead, you have to mark line breaks with underscores, like this:
Console.Write("The cursor is at ({0}, {1}).", _
Console.CursorLeft, _
Console.CursorTop)
Notice how the underscore has a space before it. This is required.
Of course, throwing boring gray characters across the screen isn't very impressive. For our game, we're going to need a bit of color. Thankfully, adding color is incredibly easy. Two properties of Console help us out in this area: ForegroundColor and BackgroundColor. The former sets the color of the character itself, and the latter sets the color of the character's background. Both properties accept a value in the ConsoleColor enumeration. Here, we set the foreground color to yellow and the background color to dark red:
Console.ForegroundColor = ConsoleColor.Yellow
Console.BackgroundColor = ConsoleColor.DarkRed
In Visual Studio, as soon as you key the assignment operator (=), a list of all possible values should appear. A list of colors can also be found here:
When we finish messing with color, we can restore the system colors:
Console.ResetColor()
Manipulating the Console Window
We can also manipulate the console window itself by setting the size of the console area and giving the window a title. Let's start with size first. The window's height is measured in rows and can be accessed using the WindowHeight property of Console. The window's width is measured in columns and can be accessed using the WindowWidth property of Console. Here, we display the console window's height and width:
Console.WriteLine("Console Size: {0}x{1}", _
Console.WindowHeight, _
Console.WindowWidth)
Using these two properties, we can also set the height and width of the console window. Here, we make the console window larger than normal, giving it a height of thirty rows and a width of one hundred columns:
Console.WindowHeight = 30
Console.WindowWidth = 100
There's also a procedure for setting the size of the window, SetWindowSize. The first argument is the width of the window, and the second argument is the height of the window.
Console.SetWindowSize(80, 25)
This ability will come in handy later when we create the game's interface. We need the console window to be high enough and wide enough to accommodate everything.
Now let's work with the title of the window. The title of the window is stored in the Title property of Console. We can read and set the title using this property: