To write your C++ game program, you will need your computer to remember a lot of things. Things such as where in the world is the player, how many hit points he has, how much ammunition he has left, where the items are in the world, what power-ups they provide, and the letters that make up the player's screen name.
The computer that you have actually has a sort of electronic sketchpad inside it called memory, or RAM. Physically, computer memory is made out of silicon and it looks something similar to what is shown in the following screenshot:
RAM is short for Random Access Memory. It is called random access because you can access any part of it at any time. If you still have some CDs lying around, they are an example of non-random access. CDs are meant to be read and played back in order. I still remember jumping tracks on Michael Jackson's Dangerous album way back when switching tracks on a CD took a lot of time! Hopping around and accessing different cells of RAM, however, doesn't take much time at all. RAM is a type of fast memory access known as flash memory.
RAM is called volatile flash memory because when the computer is shut down, RAM's contents are cleared and the old contents of RAM are lost unless they were saved to the hard disk first.
For permanent storage, you have to save your data into a hard disk. There are two main types of hard disks, platter-based Hard Disk Drives (HDDs) and Solid-state Drives (SSDs). SSDs are more modern than platter-based HDDs, since they use RAM's fast-access (Flash) memory principle. Unlike RAM, however, the data on an SSD persists after the computer is shut down. If you can get an SSD, I'd highly recommend that you use it! Platter-based drives are outdated. We need a way to reserve a space on the RAM and read and write from it. Fortunately, C++ makes this easy.
A saved location in computer memory that we can read or write to is called a variable.
A variable is a component whose value can vary. In a computer program, you can think of a variable as a container, into which you can store some data. In C++, these data containers (variables) have types. You have to use the right type of data container to save your data in your program.
If you want to save an integer, such as 1, 0, or 20, you will use an int
type container. You can use float-type containers to carry around floating-point (decimal) values, such as 38.87, and you can use string variables to carry around strings of letters (think of it as a "string of pearls", where each letter is a pearl).
You can think of your reserved spot in RAM like reserving parking space in a parking garage: once we declare our variable and get a spot for it, no one else (not even other programs running on the same machine) will be given that piece of RAM by the operating system. The RAM beside your variable might be unused or it might be used by other programs.
The operating system exists to keep programs from stepping on each other's toes and accessing the same bits of computer hardware at the same time. In general, civil computer programs should not read or write to each other's memory. However, some types of cheat programs (for example, maphacks) secretly access your program's memory. Programs such as PunkBuster were introduced to prevent cheating in online games.
Reserving a spot on computer memory using C++ is easy. We'll want to name our chunk of memory that we will store our data in with a good, descriptive name.
For example, say, we know that player hit points (hp) will be an integer (whole) number, such as 1, 2, 3, or 100. To get a piece of silicon to store the player's hp in memory, we will declare the following line of code:
int hp; // declare variable to store the player's hp
This line of code reserves a small chunk of RAM to store an integer (int
is short for integer), called hp. The following is an example of our chunk of RAM used to store the player's hp. This reserves a parking space for us in memory (among all the other parking spaces), and we can refer to this space in memory by its label (hp).
Notice how the variable space is type-marked in this diagram as int: if it is a space for a double or a different type of variable. C++ remembers the spaces that you reserve for your program in memory not only by name but by the type of variable it is as well.
Notice that we haven't put anything in hp's box yet! We'll do that later—right now, the value of the hp variable is not set, so it will have the value that was left in that parking space by the previous occupant (the value left behind by another program, perhaps). Telling C++ the type of the variable is important! Later, we will declare a variable to store decimal values, such as 3.75.
Writing a value into memory is easy! Once you have an hp
variable, you just write to it using the =
sign:
hp = 500;
Voila! The player has 500 hp.
Reading the variable is equally simple. To print out the value of the variable, simply put this:
cout << hp << endl;
This will print the value stored inside the hp variable. If you change the value of hp, and then use cout
again, the most up-to-date value will be printed, as shown here:
hp = 1200; cout << hp << endl; // now shows 1200
Something that you need to get used to when you start computer programming is that a surprising number of things can be stored in computer memory as just numbers. A player's hp? As we just saw in the previous section, hp can just be an integer number. If the player gets hurt, we reduce this number. If the player gains health, we increase the number.
Colors can be stored as numbers too! If you've used standard image editing programs, there are usually sliders that indicate color as how much red, green, and blue are being used, such as Pixelmator's color sliders. A color is then represented by three numbers. The purple color shown in the following screenshot is (R=127, G=34, B=203):
What about world geometry? These are also just numbers: all we have to do is store a list of 3D space points (x, y, and z coordinates) and then store another list of points that explain how those points can be connected to form triangles. In the following screenshot, we can see how 3D space points are used to represent world geometry:
The combination of numbers for colors and numbers for 3D space points will let you draw large and colored landscapes in your game world.
The trick with the preceding examples is how we interpret the stored numbers so that we can make them mean what we want them to mean.
You can think of variables as animal-carrying cases. A cat carrier can be used to carry a cat, but not a dog. Similarly, you should use a float-type variable to carry decimal-valued numbers. If you store a decimal value inside an int
variable, it will not fit:
int x = 38.87f; cout << x << endl; // prints 38, not 38.87
What's really happening here is that C++ does an automatic type conversion on 38.87, transmogrifying it to an integer to fit in the int
carrying case. It drops the decimal to convert 38.87 into the integer value 38.
So, for example, we can modify the code to include the use of three types of variables, as shown in the following code:
#include <iostream> #include <string> // need this to use string variables! using namespace std; int main() { string name; int goldPieces; float hp; name = "William"; // That's my name goldPieces = 322; // start with this much gold hp = 75.5f; // hit points are decimal valued cout << "Character " << name << " has " << hp << " hp and " << goldPieces << " gold."; }
In the first three lines, we declare three boxes to store our data parts into, as shown here:
string name; int goldPieces; float hp;
These three lines reserve three spots in memory (like parking spaces). The next three lines fill the variables with the values we desire, as follows:
name = "William"; goldPieces = 322; hp = 75.5f;
In computer memory, this will look as shown in the following figure:
You can change the contents of a variable at any time. You can write a variable using the =
assignment operator, as follows:
goldPieces = 522;// = is called the "assignment operator"
You can also read the contents of a variable at any time. That's what the next three lines of code do, as shown here:
cout << "Character " << name << " has " << hp << " hp and " << goldPieces << " gold.";
Take a look at this line:
cout << "I have " << hp << " hp." << endl;
There are two uses of the word hp
in this line. One is between double quotes, while the other is not. Words between double quotes are always output exactly as you typed them. When double quotes are not used (for example, << hp <
), a variable lookup is performed. If the variable does not exist, then you will get a compiler error (undeclared identifier).
There is a space in memory that is allocated for the name, a space for how many goldPieces
the player has, and a space for the hp of the player.
Math in C++ is easy to do; + (plus), - (minus), * (times), / (divide by) are all common C++ operations, and proper BEDMAS order will be followed (Brackets, Exponents, Division, Multiplication, Addition, and Subtraction). For example, we can do as shown in the following code:
int answer = 277 + 5 * 4 / 2 + 20;
Another operator that you might not be familiar with yet is % (modulus). Modulus (for example, 10 % 3) finds the remainder of when x
is divided by y
. See the following table for examples:
Operator (name) |
Example |
Answer |
---|---|---|
+ (plus) |
7 + 3 |
10 |
- (minus) |
8 - 5 |
3 |
* (times) |
5*6 |
30 |
/ (division) |
12/6 |
2 |
% (modulus) |
10 % 3 |
1 (because 10/3 is 3 the remainder = 1). |
However, we often don't want to do math in this manner. Instead, we usually want to change the value of a variable by a certain computed amount. This is a concept that is harder to understand. Say the player encounters an imp and is dealt 15 damage.
The following line of code will be used to reduce the player's hp by 15 (believe it or not):
hp = hp - 15; // probably confusing :)
You might ask why. Because on the right-hand side, we are computing a new value for hp (hp-15
). After the new value for hp is found (15 less than what it was before), the new value is written into the hp variable.
Pitfall
An uninitialized variable has the bit pattern that was held in memory for it before. Declaring a variable does not clear the memory. So, say we used the following line of code:
int hp; hp = hp - 15;
The second line of code reduces the hp by 15 from its previous value. What was its previous value if we never set hp = 100 or so? It could be 0, but not always.
One of the most common errors is to proceed with using a variable without initializing it first.
The following is a shorthand syntax for doing this:
hp -= 15;
Besides -=
, you can use += to add some amount to a variable, *= to multiply a variable by an amount, and /= to divide a variable by some amount.
In the previous section, you learned that every piece of data that you save in C++ has a type. All variables are created in the same way; in C++, variable declarations are of the form:
variableType variableName;
The variableType
tells you what type of data we are going to store in our variable. The variableName
is the symbol we'll use to read or write to that piece of memory.
We previously talked about how all the data inside a computer will at some point be a number. Your computer code is responsible for interpreting that number correctly.
It is said that C++ only defines a few basic data types, as shown in the following table:
|
A single letter, such as 'a', 'b', or '+' |
|
An integer from -32,767 to +32,768 |
|
An integer from -2,147,483,647 to +2,147,483,648 |
|
Any decimal value from approx. -1x1038 to 1x1038 |
|
Any decimal value from approx. -1x10308 to 1x10308 |
|
true or false |
There are unsigned versions of each of the variable types mentioned in the preceding table. An unsigned variable can contain natural numbers, including 0 (x >= 0). An unsigned short
, for example, might have a value between 0 and 65535.
It turns out that these simple data types alone can be used to construct arbitrarily complex programs. "How?" you ask. Isn't it hard to build a 3D game using just floats and integers?
It is not really difficult to build a game from float and int, but more complex data types help. It will be tedious and messy to program if we used loose floats for the player's position.
C++ gives you structures to group variables together, which will make your life a lot easier. Take an example of the following block of code:
#include <iostream> using namespace std; struct Vector // BEGIN Vector OBJECT DEFINITION { float x, y, z; // x, y and z positions all floats }; // END Vector OBJECT DEFINITION. // The computer now knows what a Vector is // So we can create one. int main() { Vector v; // Create a Vector instance called v v.x=20, v.y=30, v.z=40; // assign some values cout << "A 3-space vector at " << v.x << ", " << v.y << ", " << v.z << endl; }
The way this looks in memory is pretty intuitive; a Vector is just a chunk of memory with three floats, as shown in the following figure:
Here are a couple of review notes about the preceding code listing:
First, even before we use our Vector object type, we have to define it. C++ does not come with built-in types for math vectors (it only supports scalar numbers, and they thought that was enough!). So, C++ lets you build your own object constructions to make your life easier. We first had the following definition:
struct Vector // BEGIN Vector OBJECT DEFINITION { float x, y, z; // x, y, and z positions all floats }; // END Vector OBJECT DEFINITION.
This it tells the computer what a Vector is (it's 3 floats, all of which are declared to be sitting next to each other in the memory). The way a Vector will look in the memory is shown in preceding figure.
Next, we use our Vector object definition to create a Vector instance called v
:
Vector v; // Create a Vector instance called v
The struct
Vector definition doesn't actually create a Vector object. You can't do Vector.x = 1
. "Which object instance are you talking about?" the C++ compiler will ask. You need to create a Vector instance first, such as Vector v1; then, you can do assignments on the v1 instance, such as v1.x = 0.
We then use this instance to write values into v
:
v.x=20, v.y=30, v.z=40; // assign some values
This makes v
look as in the preceding screenshot. Then, we print them out:
cout << "A 3-space vector at " << v.x << ", " << v.y << ", " << v.z << endl;
In both the lines of code here, we access the individual data members inside the object by simply using a dot (.
). v.x
refers to the x
member inside the object v
. Each Vector object will have exactly three floats inside it: one called x
, one called y
, and one called z
.
Define a C++ data struct for a Player object. Then, create an instance of your Player class and fill each of the data members with values.
Let's declare our Player object. We want to group together everything to do with the player into the Player object. We do this so that the code is neat and tidy. The code you read in Unreal Engine will use objects such as these everywhere; so, pay attention:
struct Player { string name; int hp; Vector position; }; // Don't forget this semicolon at the end! int main() { // create an object of type Player, Player me; // instance named 'me' me.name = "William"; me.hp = 100.0f; me.position.x = me.position.y = me.position.z=0; }
The struct Player definition is what tells the computer how a Player object is laid out in memory.
Inside a Player object, we declared a string for the player's name, a float for his hp, and a Vector object for his complete xyz position.
When I say object, I mean a C++ struct (or later, we will introduce the term class).
Wait! We put a Vector object inside a Player object! Yes, you can do that.
After the definition of what a Player object has inside it, we actually create a Player object instance called me and assign it some values.
After the assignment, the me object looks as shown in the following figure:
A particularly tricky concept to grasp is the concept of pointers. Pointers aren't that hard to understand but can take a while to get a firm handle on.
Say we have, as before, declared a variable of the type Player in memory:
Player me; me.name = "William"; me.hp = 100.0f;
We now declare a pointer to the Player:
Player* ptrMe; // Declaring a pointer
The *
characters usually make things special. In this case, the *
makes ptrMe
special. The *
is what makes ptrMe
a pointer type.
We now want to link ptrMe
to me:
ptrMe = &me; // LINKAGE
ptrMe
now refers to the same object as me. Changing ptrMe
will change me, as shown in the following figure:
When we set up the linkage between the pointer variable and what it is pointing to, we can manipulate the variable that is pointed to through the pointer.
One use of pointers is to refer to the same object from several different locations of the code. The Player object is a good candidate for being pointed to. You can create as many pointers as you wish to the same object. Objects that are pointed to do not necessarily know that they are being pointed at, but changes can be made to the object through the pointers.
For instance, say the player got attacked. A reduction in his hp will result, and this reduction will be done using the pointer, as shown in the following code:
ptrMe->hp -= 33; // reduced the player's hp by 33 ptrMe->name = "John";// changed his name to John
Here's how the Player object looks now:
So, we changed me.name
by changing ptrMe->name
. Because ptrMe
points to me, changes through ptrMe
affect me directly.
Besides the funky arrow syntax (use ->
when the variable is a pointer), this concept isn't all that hard to understand.
Notice the use of the &
symbol in the preceding code example. The &
operator gets the memory address of a variable. A variable's memory address is where it lives in the computer memory space. C++ is able to get the memory address of any object in your program's memory. The address of a variable is unique and also, kind of, random.
Say, we print the address of an integer variable x
, as follows:
int x = 22; cout << &x << endl; // print the address of x
On the first run of the program, my computer prints the following:
0023F744
This number (the value of &x
) is just the memory cell where the variable x
is stored. What this means is that in this particular launch of the program, the variable x
is located at memory cell number 0023F744, as shown in the following figure:
Now, create and assign a pointer variable to the address of x
:
int *px; px = &x;
What we're doing here is storing the memory address of x
inside the variable px
. So, we are metaphorically pointing to the variable x
using another different variable called px
. This might look something similar to what is shown in the following figure:
Here, the variable px
has the address of the variable x
inside it. In other words, the variable px
is a reference to another variable. Differencing px
means to access the variable that px
is referencing. Differencing is done using the *
symbol:
cout << *px << endl;
A null pointer is a pointer variable with the value 0
. In general, most programmers like to initialize pointers to Null (0
) on the creation of new pointer variables. Computer programs, in general, can't access the memory address 0
(it is reserved), so if you try to reference a Null pointer, your program will crash, as shown in the following screenshot:
Pointer Fun with Binky is a fun video about pointers. Take a look at http://www.youtube.com/watch?v=i49_SNt4yfk.
cin
is the way C++ traditionally takes input from the user into the program. cin
is easy to use, because it looks at the type of variable it will put the value into as it puts it in. For example, say we want to ask the user his age and store it in an int
variable. We can do that as follows:
cout << "What is your age?" << endl; int age; cin >> age;
Although we have used cout
to print out variables so far, you need to know about another common function that is used to print to the console. This function is called the printf
function. The printf
function is included in the <iostream>
library, so you don't have to #include
anything extra to use it. Some people in the gaming industry prefer printf
to cout
(I know I do), so let's introduce it.
Let's proceed to how printf()
works, as shown in the following code:
#include <iostream> #include <string> using namespace std; int main() { char character = 'A'; int integer = 1; printf( "integer %d, character %c ", integer, character ); }
Downloading the example code
You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
We start with a format string. The format string is like a picture frame, and the variables will get plugged in at the locations of the %
in the format string. Then, the entire thing gets dumped out to the console. In the preceding example, the integer variable will be plugged into the location of the first %
(%d
), and the character will be plugged into the location of the second %
(%c
), as shown in the following screenshot:
You have to use the right format code to get the output to format correctly; take a look at the following table:
Data type |
Format code |
---|---|
Int |
%d |
Char |
%c |
String |
%s |
To print a C++ string, you must use the string.c_str()
function:
string s = "Hello"; printf( "string %s ", s.c_str() );
The s.c_str()
function accesses the C pointer to the string, which printf
needs.
If you use the wrong format code, the output won't appear correctly or the program might crash.
Ask the user his name and age and take them in using cin
. Then, issue a greeting for him at the console using printf()
(not cout
).
This is how the program will look:
#include <iostream> #include <string> using namespace std; int main() { cout << "Name?" << endl; string name; cin >> name; cout << "Age?" << endl; int age; cin >> age; cout << "Hello " << name << " I see you have attained " << age << " years. Congratulations." << endl; }
3.14.129.194