Thông tin tài liệu
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 1 of 44
Game Institute
by Stan Trujillo
Week
6
Introduction to C and C++
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 2 of 44
© 2001, eInstitute, Inc.
You may print one copy of this document for your own personal use.
You agree to destroy any worn copy prior to printing another. You may
not distribute this document in paper, fax, magnetic, electronic or other
telecommunications format to anyone else.
This is the companion text to the www.gameinstitute.com
course of the
same title. With minor modifications made for print formatting, it is
identical to the viewable text, but without the audio.
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 3 of 44
Table of Contents
Lesson 6 – GameDesign 4
Namespaces 4
Templates 8
STL 9
The Snake Sample 12
The Pong Game 18
The Game Classes 20
The GameObject Class 22
The Player Class 25
The HumanPlayer Class 28
The ArtificialPlayer Class 30
The Ball Class 34
The Application Class 37
Final Thoughts 42
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 4 of 44
Lesson 6 – GameDesign
Learning C++ is really a two-part process, although the two are usually done in parallel. The first part is
the C++ language itself, which includes keywords, intrinsic data types, loops, conditionals, arrays,
structures, and classes. The second part is learning to use APIs. This second subject can include both the
standard libraries and third-party libraries.
Using APIs requires knowledge of the language, and to a lesser degree learning C++ requires knowledge
of the standards libraries. Using the cout object, which is not part of the language itself, although it is
supported by every implementation of C++, is a good example.
The remaining subjects that we’ll cover in these lessons fall into both categories. There are some C++
language constructs, and some topics that involve the standard libraries. We’ll start with the C++
language constructs.
It may seem late in the course to be introducing new C++ topics. Indeed, it is, but the idea behind this
course is to concentrate on the core C++ constructs that are used in game programming. The first three
lessons introduced the portions of the language that are almost always used. Here, we’ll cover some topics
that, while important, are not always used.
Namespaces
C++, like most languages in current use today, is still evolving. Some features, such as classes, have been
with the language all along, and others have been added somewhere along the way. Originally, C++
didn’t support the concept of a namespace, but it proved necessary once developers starting using C++ for
large projects.
A namespace is scope in which data types can be defined in order to avoid cluttering the global scope. To
understand what this means, it’s necessary to talk about what it means to define data types globally.
By default, when a new data type is defined it exists globally. This means that it is available to every
function in the application. This is convenient, but it prevents any additional uses of the data type name.
For example, if we declare a structure like this:
struct Player
{
};
This means that, in the global namespace, the name “Player” refers to this structure. If any of the code in
the program tries to declare a different global data type with the same name, an error will result:
class Player // compiler error! Player has already been defined
{
};
These two type definitions don’t mix, because they use the same name. What’s worse, this is true even if
the two data types are exactly the same; a data type cannot be defined twice. This is reasonable enough,
but what if we don’t have any control over the name of this data type? What if Player is defined by an
API that we need to use? This means that the name “Player” has been reserved by an API that we have no
control over. Most of the time, this simply means that we can’t use the name “Player” for our own data
types.
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 5 of 44
But what if we’re using two APIs, and both define a data type with the name “Player”? Now, not only are
we prohibited from using this name, but our code will not compile because there is a name conflict. In
this situation, there is no way to proceed without getting the maker of one of the other APIs to change the
name of their data types. As things stand, the two APIs are incompatible.
Before the introduction of namespace support, API developers would often prefix their data type names
with the name of the company, the name of the API, or an acronym, like this:
class abcPlayer // API “ABC” prefixes all of their data types with “abc”
{
};
struct defPlayer // API “DEF” does the same thing
{
};
This prevented the name conflict problem—most of the time. But there is still a chance that two different
companies will use the same name. And furthermore, the name of each data type is now obscured.
Namespaces allow data types to be declared in a private namespace, or scope. This prevents data type
names from cluttering the global namespace, and therefore prevents name conflicts. (As long as the name
of the namespace itself is unique.) Using namespaces, the APIs abc and def would define their own
version of “Player” without a prefix:
namespace abc
{
struct Player
{
};
};
namespace def
{
class Player
{
};
};
This code defines two different data types, both named Player, but each within its own namespace, so
there is no conflict. What’s more, since the Player name is kept out of the global namespace, we’re free
to use the name “Player” in our code:
namespace abc
{
struct Player
{
};
};
namespace def
{
class Player
{
};
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 6 of 44
};
typedef int Player;
Each usage of the name “Player” refers to a different data type. One is a class, one is a structure, and one
is a typedef to an int. But, because each resides in a separate namespace, there is no conflict (the typedef
version of Player exists in the global namespace).
What’s the catch? Each time you use the name “Player”, you must specific which one you’re referring to.
By default, the global namespace is used, so in this case you’ll get the global “Player”, like this:
Player p; // p is an ‘int’ (via typedef)
If the global Player typedef shown above were not defined, then using the plain name “Player” would
fail, because no such data type exists not in the global namespace.
To indicate a data type that is part of a non-global namespace, the scope resolution operator (::) must be
used, like this:
abc::Player p1;
def::Player p2;
These declarations create two different variables, each with a different type, despite the fact that the data
type name is “Player” in both cases. The first creates a variable based on Player as defined within the abc
namespace, while the second uses the Player data type from the def namespace. We can prove that the
two variables are different by adding member functions to each version of Player:
namespace abc
{
struct Player
{
void Blah();
};
};
namespace def
{
class Player
{
public:
void Yak();
};
};
abc::Player p1;
def::Player p2;
p1.Blah();
p2.Yak();
These function calls are specific to each version of Player, so the fact that this code compiles proves that
the p1 and p2 variables are indeed based on different types.
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 7 of 44
Any number of different data types can be added to a namespace. Here, a namespace called xyz is defined
that includes typedefs, a structure, and a function:
namespace xyz
{
typedef int Type1;
typedef float Type2;
struct Type3
{
};
void Func()
{
}
};
Using data types that are defined as part of a namespace can be performed either with the scope resolution
operator for each usage, like this:
xyz::Type1 a;
xyz::Type2 b;
xyz::Type3 c;
xyz::Func();
Or with the using keyword, which activates a namespace for use by the code that follows:
using namespace xyz;
Type1 a;
Type2 b;
Type3 c;
Func();
The using keyword activates a namespace, but it does not prohibit the usage of names in the global
namespaces, so the code above has access to both the xyz and the global namespace. As a result, the
using keyword doesn’t work with names that are present in both namespaces::
namespace xyz
{
typedef int Type1;
typedef float Type2;
struct Type3
{
};
void Func()
{
}
};
void Func();
using namespace xyz;
Func(); // compiler error! The name “Func” is ambigous
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 8 of 44
This code won’t compile, despite the fact that the xyz namespace has been activated with the using
keyword. In this case it is necessary to use the scope resolution operator to indicate which version of
Func we’re referring to:
xyz::Func(); // calls Func as provided in the xyz namespace
::Func(); // calls the global version of Func
Using the scope resolution operator with no preceding scope indicates the global namespace. This is only
necessary if the current scope includes a name that is the same as one in the global namespace.
During the normal course of game programming, it is not normally necessary to define new namespaces.
Even on a large project, name conflicts are fairly unlikely, but if it does crop up, it is usually just a matter
of talking with the programmer in the next cubicle to decide which type can be renamed to resolve the
conflict.
Using existing namespaces is more common, as many APIs define their data types within non-global
namespaces to avoid conflict. This includes APIs that are part of the standards libraries, as we’ll soon see
when we look at STL.
Templates
Another feature added to C++ after it had become widely used is templates. A template is an incomplete
type. One or more key elements are missing, but can be provided when the data type is used. Templates
are sometimes called parameterized types because they take parameters. But unlike a function, template
parameters are data types—not data values. Consider this code:
template <class T> class Data
{
public:
T GetValue() { return value; }
void SetValue (T v) { value = v; }
private:
T value;
};
This is a template for a class called Data. This template takes one parameter called T (a capital T is the
standard name for a template parameter.) The definition begins with the template keyword, and is
followed by a template parameter list. In this case there is only one parameter, but there can be any
number of parameters. Unlike function parameter lists, template parameter lists are enclosed in angle
brackets. Most of the time parameter names are preceded by the class keyword, but class has a different
meaning when used in this context. A template parameter that uses the class keyword can be any data
type, not just a class.
The Data class declares two member functions and a data member. The functions allow the private data
member to be assigned, and retrieved. Notice the type used as a return value for GetValue, the argument
for SetValue, and the data member. Instead of a type like int, or float, the template parameter name is
used. In each case the data type used is contingent on the parameter passed to the template as T.
In order to use a template, a data type must be supplied, like this:
Data<int> dataObject1;
Data<float> dataObject2;
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 9 of 44
In this case dataObject1 is an instance of the Data class that is parameterized on the int type. The result
is that this object can be used as though each instance of T were actually an int. Likewise, dataObject2
uses the float type. These objects can then be used like this:
dataObject1.SetValue( 33 );
int val1 = dataObject1.GetValue();
cout << val1 << endl; // displays 33
dataObject2.SetValue( 44.5f );
float val2 = dataObject.GetValue();
cout << val2 << endl; // displays 44.5
Once an object has been created based on a template class, there’s no evidence that it is template-based. It
can be used as if the class upon which it is based were written specifically with the provided data type.
Any data type can be used as a template argument, including pointers:
Data<Player*> obj;
Player p;
Obj.SetValue( &p );
Obj.GetValue()->SetName( “slowpoke” );
What are templates for? Templates allow the definition of generic data types. They can be used to write
classes that aren’t specific to any single data type. Templates are often used to write container classes, as
we’ll see in the next section.
STL
As its name implies, the Standard Template Library (STL), is a set of templates. Now part of the standard
C++ libraries, these templates provide functionality that is extremely flexible and powerful.
STL provides support for several data containers. Because they are templates, these containers are
completely generic. They can be used to store any type of data. We won’t cover all of the STL templates,
but STL defines these containers:
• string
• list
• deque
• stack
• vector
• map
• set
Although we won’t cover all of these types, most of the STL containers work in a similar way. The string
data type is a container that stores characters. Unlike other types, string doesn’t require a template
argument, because it is actually a typedef for the STL basic_string type, which is templatized on the
char type, like this:
typedef basic_string<char> string;
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 10 of 44
All of the STL data types are defined in std namespace (short for standard), so this namespace must be
specified with the scope resolution operator, or the using keyword. Declaring a string, therefore looks like
this:
std::string str1;
// or
using namespace std;
string str2;
The string type, through basic_string, provides a host of copy constructors, overloaded operators, and
member functions, all of which serve to make string handling much easier than it is with char arrays.
Here’s an example:
using namespace std;
string a("one"); // string ‘a’ contains “one”
a.append(" "); // append one space to the end
a+="two"; // append another string (‘+=’ is the same as append)
string b(" three"); // string ‘b’ contains “three”
a+=b; // append an object of the string type
int size = a.size(); // retrieve the string length
cout << a.data() << " size=" << size << endl;
// displays ‘one two three size=13’
(If this seems familiar, that is probably because we used the STL string class in Lesson 3 to implement
the HighScores sample.)
The overloaded constructors and operators that the string class provides are nice, but that’s just the
beginning of what this class can do. The string class also provides support for searching, replacing, and
inserting. Likewise, the other STL containers provide member functions that provide convenient features,
such as sorting.
Many of the STL container features rely on the concept of an iterator. An iterator is a class that is
designed solely for manipulating the contents of a container object. STL iterators provide a number of
overloaded operators that allow them to be used just like pointers.
STL defines a different iterator for each container, but the iterator class always has the name iterator.
This is possible because the iterator class is defined inside the container class. As a result, the scope
resolution operator must be used in order to access the iterator data type. For the string class, declaring
an iterator looks like this:
std::string::iterator it;
Despite the odd syntax, this is a simple object declaration. An iterator called it is declared based on the
iterator class, which is defined within the scope of the string class, which in turn is part of the std
namespace; hence the use of two scope resolution operators.
This new iterator is initialized, as the string::iterator class provides a default constructor, but the new
object doesn’t indicate an element until used with an existing instance of string, like this:
[...]... game that is 30 years old? The point of this sample is not to amaze you with new ideas for games, but to showcase the C++ features that we’ve covered in this course in the context of a game By using a simple game, we can keep the code fairly simple, but still use the features that make C++ such a potent game programming language The Game Classes Before we look at any code, or design any class interfaces,... just as items can’t be added to the beginning of an array without moving all existing items The vector class overloads the square bracket so that elements can be accessed as though the container is a C++ array The map container uses a key system to provide access to its contents Each item added to a map is accompanied by a key that is used by the map container to store the item If the item is required... end of the deque and add one at the beginning The result is the animation of a shape that looks something like a snake The final executable looks like this: www.gameinstitute.com Introduction to C and C++ : Week 6: Page 12 of 44 To add a bit of interaction, we’ll configure the F2 key to prohibit the removal of deque elements from the end of the collection, causing the length of the collection to be... member is an instance of D3DModel As with the Circle sample, although we’ll be rendering multiple instances of the model, only one model object is required www.gameinstitute.com Introduction to C and C++ : Week 6: Page 13 of 44 We’ll also use some literal constants to define values used in the implementation: const int LimitX = 200; const int LimitY = 150; const int IncX = 7; const int IncY = 4; const... false ); HRESULT hr = InitializeDirect3D(); if (FAILED(hr)) { return false; } // prepare the "snake" int x = 0, y = 0; for (int i=0; iTestCooperativeLevel() ) ) { if( D3DERR_DEVICELOST == hr ) return true; if( D3DERR_DEVICENOTRESET == hr ) { if( FAILED( hr = Reset3DEnvironment() ) ) www.gameinstitute.com Introduction to C and C++ : Week 6: Page 16 of 44 return true; } return true; } Clear3DBuffer( 0 ); if (SUCCEEDED(device->BeginScene())) { D3DVIEWPORT8 viewport; device->GetViewport( &viewport ); DrawScene( device, viewport... while (keyboard->GetEvent( event )) { if (event.dwOfs == DIK_F2) { if (event.dwData & 0x80) grow = true; else grow = false; } else if (event.dwOfs == DIK_F3) www.gameinstitute.com Introduction to C and C++ : Week 6: Page 17 of 44 { if (event.dwData & 0x80) shrink = true; else shrink = false; } } // update snake int size = pointDeque.size(); if ( ! grow || size >= SnakeLenMax ) pointDeque.pop_back();... only option Most of these units had a long cord so that you could play from your couch (this was well before the wireless revolution), and had two dials for www.gameinstitute.com Introduction to C and C++ : Week 6: Page 18 of 44 exciting multi-player action (Well, it seemed exciting at the time.) Although Pong is unlikely to make a comeback, it can’t hurt to remake this classic game, but this time,... faster, and the AI opponent gets faster Whoever gets 30 points first wins the game, at which point a menu is displayed that asks whether you want to play again: www.gameinstitute.com Introduction to C and C++ : Week 6: Page 19 of 44 If you select “Play Again”, the state of the game objects is reset, and the game starts over, otherwise the application terminates There is another menu that is displayed if . Stan Trujillo
Week
6
Introduction to C and C++
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 2 of 44
© 2001, eInstitute,.
www.gameinstitute.com Introduction to C and C++ : Week 6: Page 4 of 44
Lesson 6 – GameDesign
Learning C++ is really a two-part process, although the
Ngày đăng: 19/01/2014, 02:20
Xem thêm: Tài liệu Giáo trình C++ P6 ppt, Tài liệu Giáo trình C++ P6 ppt