Implementing a Vector Class with Overloading


In chapter 4, I briefly introduced vectors and stated that a vector contains a magnitude and a direction. There's really no better way to specify how sprites in games should move than with vectors. In fact, if you ever study the physics topics of dynamics and kinematics, you'll see that physicists specify movement almost completely in vectors.

If a vector has a magnitude and direction, how does that help you in games?

If your game specifies an object's movement as a vector, the vector's magnitude tells how fast the object moves. The direction of the object's movement is specified by the vector's direction. Also, vectors can be applied to any coordinate system. This enables you to define your vectors any way you want. For example, if you live outside the United States, the speedometers of cars in your country probably give the car's speed in kilometers per hour. If you live in the United States, it's miles per hour. Either way, specifying a car's speed with vectors works exactly the same. Whether your game has a coordinate system that uses kph or mph, you can use vectors to describe the motion of its sprites.

You can specify a vector's direction and magnitude in multiple ways. One is to just specify the vector's direction and magnitude. That may sound a bit silly to say, but it's actually not. The reason is that you can also specify a vector's direction and magnitude by breaking the vector into components. Figure 5.1 illustrates what I mean.

Figure 5.1. Two ways to specify vectors.


Figure 5.1 shows two views of a ball at the origin of an x,y coordinate system. The one on the left has a vector that specifies the direction and speed that the ball is moving. The direction is stated in terms of an angle represented by the Greek letter theta (q). If you're familiar with specifying angles, you probably know that they can be specified either in degrees or radians. In this coordinate system, we'll use degrees and say that 0° is at the positive x-axis. The angle increases in the counterclockwise direction. The speed of the ball is represented by S. The speed and the direction specify the magnitude and direction of the ball's motion vector.

The second diagram in Figure 5.1 (on the right) also shows the ball's movement vector. This time, it's specified in terms of its component vectors. This is an extremely easy way to specify vectors in games. You can take the x and y components as pixel displacements, as we did in the final program in chapter 4. Specifying an x component of 5 for the ball's motion vector and a y component of 10 means that the ball will move 5 pixels in the x direction and 10 in the y direction in each frame. This is exactly how most 2D games use vectors.

Tip

When you write 2D games, I recommend that you specify vectors in terms of x and y components rather than direction angles and magnitudes. It's much easier if you do.


We're ready to see overloading in action in the vector class. First, let's examine the class definition itself (see Listing 5.2). After that, we'll see how overloaded functions and operators work.

Listing 5.2. A vector class

 1   class vector 2   { 3   public: 4     vector(); 5     vector( 6       int xComponent, 7       int yComponent); 8 9     void X( 10       int xComponent); 11    int X(void); 12 13    void Y( 14       int yComponent); 15    int Y(void); 16 17    vector operator +( 18       vector &rightOperand); 19    vector operator -( 20       vector &rightOperand); 21 22    vector operator *( 23       int rightOperand); 24    friend vector operator *( 25       int leftOperand, 26       vector &rightOperand); 27 28    vector operator /( 29       int rightOperand); 30    friend vector operator /( 31       int leftOperand, 32       vector &rightOperand); 33 34    vector &operator +=( 35       vector &rightOperand); 36    vector &operator -=( 37       vector &rightOperand); 38    vector &operator *=( 39       int rightOperand); 40    vector &operator /=( 41       int rightOperand); 42 43    int Dot( 44        vector &rightOperand); 45 46    int Magnitude(); 47    int MagnitudeSquared(); 48    vector Normalize(); 49 50  private: 51     int x,y; 52  }; 

This vector class makes heavy use of function and operator overloading. It provides two constructors, two X() functions, two Y() functions, and two * operators. It also has the operators +, -, and /. In addition, it provides some new operators: +=, -=, *=, and /=.

Overloaded Vector Constructors

Like the point_2d class in Listing 5.1, the vector class in Listing 5.2 contains two overloaded constructors. The default constructor (the one with no parameters) sets the values of the private data members x and y to 0. The other, whose prototype is shown on lines 57, sets x and y to the value of its parameters. Here is the code for both functions:

 inline vector::vector() {     x = y = 0; } inline vector::vector(     int xComponent,     int yComponent). {     x = xComponent;     y = yComponent; } 


Programs automatically call the default constructor whenever they declare a vector variable without initializing it, like this:

 vector thisVector; 


Note

Using overloaded constructors, you can provide any type of initialization that makes sense for the class. This includes an initialization with no parameters, one parameter, two parameters, and so on.


To initialize a vector variable, programs must either provide no parameters and accept the default initialization, or provide two parameters. There are no other choices because there are no other constructors. It is possible to add a constructor that has only one parameter. However, for the vector class, that approach doesn't make sense.

When a program calls the constructor with two parameters, it uses a function-style declaration, like the following statement:

 vector thatVector(20,30); 


Factoid

C++ programmers also call parameters "arguments"-so they will often call the default constructor the "no-arg constructor" to indicate that it has no arguments. Similarly, they say that one-parameter constructors are one-arg constructors, two-parameter constructors are two-arg constructors, and so on.


This example is similar to the point_2d example earlier in this chapter. The variable name thatVector is followed by parentheses as if it were a function name. However, it is not. Instead, this notation tells the program to call the vector constructor and pass it two parameters. This causes the program to call the constructor that has two parameters and set the values of xComponent and yComponent to 20 and 30, respectively. The two-parameter constructor sets the x and y values of thatVector to 20 and 30.

Addition and Subtraction of Vectors

When you add or subtract vectors, it's very much as if you're laying them end to end. Figure 5.2 illustrates adding vectors.

Figure 5.2. Adding vectors A and B produces vector C.


To add vector A in Figure 5.2 to vector B, you essentially put the start of B at the end of A. You then use the starting point of A and the ending point of B for the start and end of a new vector, called C, that is the answer to the addition. Whenever you add two vectors, you always get another vector.

Subtracting vectors is the same as adding the negative, as Figure 5.3 demonstrates.

Figure 5.3. Subtracting vectors A and B produces vector C.


In this figure, I've used vectors A and B from the previous diagram. To subtract A from B, I reversed the direction of B. This turns B into B. Now it is easy to lay the two vectors end to end and add them. The result is a new vector, C.

The vector class in Listing 5.2 supports both addition and subtraction. It does so by providing its own functions for those operators. The prototypes are on lines 1720. In general, a prototype for overloading any operators follows the pattern that these two use. That is, it first states the return type, which is often the name of the class. Next, it uses the C++ keyword operator to indicate that an operator is being overloaded for the class. That is followed by the symbol for the operator itself-in this case, the + and - signs for the addition and subtraction operator. Finally, the prototype presents the parameter list.

Most of the operators that you'll overload are binary operators. That means they have two operands. Addition is a perfect example. Here's an integer addition:

3+5

You always add two things together, so you need two operands and an operator. In this case, the 3 and the 5 are the operands and the + is the operator. If you're adding vectors, it works the same way. Here's a vector addition:

Warning

It is possible for you to overload operators in unusual ways. For example, you can overload the + operator to do subtraction and the - operator to do addition. This is not a good idea. You should always overload the operators to do what most people expect.


a + b

The format is just the same as an integer addition. The equation contains two vector operands, a and b, and the + operator.

When you add two integers in a C++ program, you use a notation that is similar to normal integer addition, like the one shown here:

 int a = 3, b = 5; int c; c = a + b; 


This simply declares two integer variables, a and b, adds them, and stores the result in the variable c. This code looks very normal and understandable to anyone familiar with addition. When we overload the addition operator for any class, such as the vector class, we want our code to look essentially the same way. Specifically, we'd like to be able to write our code like the following code fragment:

 vector a(10,20); vector b(30,40); vector c; c = a + b; 


This is exactly the way that you can write your code when you overload the + operator.

Note

Notice that the vectors a and b are now printed in bold lettering. This is actually the proper way to show vectors. Printing them in bold type indicates that you're adding vectors, not integer or floating-point variables. Of course, this is just the convention used in books. Happily, we do not have to try and bold the variables in our programs. Compilers don't like that sort of thing.


Here's the code for the + and operators for the vector class:

 inline vector vector::operator +(         vector &rightOperand) {     vector temp;     temp.x = x + rightOperand.x;     temp.y = y + rightOperand.y;     return (temp); } inline vector vector::operator -(     vector &rightOperand) {     vector temp;     temp.x = x - rightOperand.x;     temp.y = y - rightOperand.y;     return (temp); } 


This code fragment calls these two functions:

 vector v1(10,20), v2(20,30), v3; v3 = v1 + v2; v3 = v1 - v2; 


Bear in mind that when your program calls a class's member function, it must use an object. For instance, calling the X() function from the vector class would require a

 vector variable: vector a; a.X(10); 


Here, the variable a is used to call X(). Calling your overloaded operator functions also requires a class variable. In the previous vector example, the object that calls the + function is v1. The variable v2 gets passed to the + function through the parameter list. If you look back at Listing 5.2, you'll see that the name of the parameter for the + operator is rightOperator. Now you know why.

The addition function contains the statement

 temp.x = x + rightOperand.x; 


Notice that there are three references to the private data member called x. The leftmost one is the x for the temp variable. The rightmost x (rightOperand.x) is the x for the right-hand operator in the addition. The one in the middle is just x. It doesn't have an object name in front of it. That's because it's the x for the object that was used to call the + function. The object that's used to call the addition function is always the left-hand operand. Therefore, in the statement

 v3 = v1 + v2; 


the variable v1 calls the + function and v2 is copied into the parameter rightOperand. The function adds the x and y values of v2 to the x and y values of v1, stores them in the variable temp, and then uses the return statement to send the answer back. The answer is then stored in v3.

Factoid

Even if your class only provides one version of an operator function, such as a + operator, it is said to be overloading that operator for the class.


Multiplication of Vectors

Vectors are different than regular numbers in that there are three ways to multiply vectors. Not only that, but when you do vector multiplication, you have to write four multiplication functions to cover all three methods. That seems odd, but there's a good reason for it, which we'll get to shortly.

The three ways of multiplying vectors are:

Multiplying vectors by scalars

Multiplying a vector by a vector to get the dot product

Multiplying a vector by a vector to get the cross product

We're going to simplify things a bit. In this book, we're only working in two dimensions. You must use 3D to find a cross product, so we'll just ignore that completely. The only kinds of vector multiplication I'll present are those that work in 2D. Therefore, we'll look at multiplying vectors by scalars and finding the dot product. When we multiply vectors and scalars, we actually have to write two functions to get it done.

Multiplying Vectors by Scalars

When you multiply a vector by a number, you're said to be multiplying it by a scalar. A scalar can be either an integer or a floating-point number. The reason we call them scalars becomes clear when you look at the code for the * operator for the vector class:

 inline vector vector::operator *(     int rightOperand) {     vector temp;     temp.x = x * rightOperand;     temp.y = y * rightOperand;     return (temp); } 


The form of this * operator follows the same pattern that we saw in the + operator. Specifically, the return type is the class name and the parameter is the right operand. This is how you'd use the previous function:

 vector v1, v2(20,30); v1 = v2 * 5; 


When your program multiplies a vector by a scalar, such as the integer 5, it calls the function for the multiplication operator. The vector object is the left operand. It is the object that calls the * function. The 5 gets passed to the * function as the value for the parameter rightOperand. When it does, the * function multiplies the vector's x and y values by the value in rightOperand. What this does is multiply the length of the vector by 5. That's why, when you're speaking of vectors, regular numbers such as integers and floating-point numbers are called scalars. They scale the length of the vector. In this case, it made the vector five times longer. If you want to make it half as long, you multiply it by 0.5:

 vector v1, v2(20,30); v1 = v2 * 0.5; 


Multiplying Scalars by Vectors with Friend Functions

Multiplying vectors by scalars should mean that we can multiply scalars by vectors. That is, if we can do this:

 v1 = v2 * 5; 


we should also be able to do this:

 v1 = 5 * v2; 


The rules of math say that's okay. However, we're also working with C++, and its rules say that member functions must be called by objects. The statement

 v1 = v2 * 5; 


calls the operator *() function because v2 is a vector object and it is on the left of the * operator. However, if we change that to

 v1 = 5 * v2; 


then the left-hand operand is an integer. Integers won't call the * function for the vector class. In situations like this, it'd be nice to have a friend to help us out. In fact, C++ provides a special type of function called a friend function to solve problems like this. If you look back at Listing 5.2, you'll see the declaration of a friend function on lines 2426. Its prototype begins with the keyword friend. The function itself is not a member of the class but just a friend. Because it's not a member, it doesn't need to be called with a vector object on the left side of the * operator. Because it's a friend, it can access all the class's private member data. It's as if the class is an exclusive club that lets in only members-and their friends.

Notice that the friend function needs parameters for both the left and right operands. The left operand is an integer and the right one is a vector. Notice also that there's an ampersand sign (&) in front of the parameter rightOperand. You don't have to pay attention to that now. I'll explain it shortly.

Here's the code for the friend function:

 inline vector operator *(     int leftOperand,     vector &rightOperand) {     return (rightOperand * leftOperand); } 


An important point here is that the friend function is not a member of the class. As a result, it does not have vector:: in front of the word operator as the previous * function did.

The friend function switches the order of the left and right operands and performs the multiplication. That puts the vector object that came into the friend function as the right operator on the left. That's important; it means that the vector object now calls the * function. Which * function does it call? The one we saw in the previous section that is a member of the vector class. That function actually does the multiplication by a scalar. When it returns its answer to the friend function, the friend function just returns that answer to the calling program.

With the friend function in place, we can now have statements such as the following in our programs:

 v1 = 5 * v2; 


The Dot Product

Multiplying a vector by a scalar, or scalar by a vector, results in a vector. There is also a way of multiplying two vectors to get a scalar. This process is called the dot product, so called because you use a dot symbol when you write the equation, as shown here.

 a • b 


The equation finds the dot product of the vectors a and b. C++ does not supply a dot symbol. As a result, most programmers simply name their functions that perform the dot product Dot(). If you look on lines 4344 of Listing 5.2, you'll see that I've followed this convention in the vector class.

When you're performing this kind of vector multiplication, you simply multiply the components of the two vectors involved. The resulting code is quite simple:

 inline int vector::Dot(     vector &rightOperand) {     return (x*rightOperand.x + y*rightOperand.y); } 


Warning

Some programmers like to overload the * symbol for their dot product. This is okay if you know your code will always be used by an experienced graphics programmer. However, beginners find it terribly confusing. For that reason, it's best to avoid using the * symbol for the dot product.


Although the name of the parameter for this function is rightOperand, your programs cannot use this function like it does operators. A function call to the Dot() function resembles normal function calls:

 vector v1(10,20), v2(20,30); int answer; answer = v1.Dot(v2); 


As you get into game programming, you'll find that the primary use of the dot product is to calculate the physics of moving objects. Since we're not getting into that much physics, we won't delve into the dot product further. However, its relevance to the current discussion is that it shows the limits of operator overloading. It demonstrates that we cannot overload any symbol we would like. There is a definite set of symbols that can be overloaded, as shown in Table 5.1.

Note

I've deliberately omitted the following operators from Table 5.1: ->,->* , ()

These operators can be overloaded, but should not be unless the circumstances are extremely exceptional.


Table 5.1. The C++ Operators That Can Be Overloaded

Operator

type

Description

new

N/A

Allocates dynamic memory.

delete

N/A

Releases dynamic memory.

new[]

N/A

Allocates dynamic array.

delete[]

N/A

Releases a dynamic array.

+

Unary.

Indicates a positive value.

+

Binary.

Addition.

-

Unary.

Makes a value negative.

-

Binary.

Subtraction.

*

Unary.

Dereference.

*

Binary.

Multiplication.

/

Binary.

Division.

%

Binary.

Modulus.

^

Binary.

Bitwise Exclusive Or.

&

Unary.

Reference.

&

Binary.

Bitwise And.

|

Binary.

Bitwise Or.

~

Unary.

Bitwise negation.

!

Unary.

Logical Not.

=

Binary.

Assignment.

<

Binary.

Less than.

<=

Binary.

Less than or equal to.

==

Binary.

Equal to.

!=

Binary.

Not equal to.

>=

Binary.

Greater than or equal to.

>

Binary.

Greater than.

&&

Binary.

Logical And.

||

Binary.

Logical Or.

+=

Binary.

Add assign.

-=

Binary.

Subtract assign.

*=

Binary.

Multiply assign.

/=

Binary.

Divide assign.

%=

Binary.

Modulus assign.

&=

Binary.

Bitwise And assign.

|=

Binary.

Bitwise Or assign.

^=

Binary.

Bitwise Exclusive Or assign.

<<

Binary.

Left shift.

>>

Binary.

Right shift.

<<=

Binary.

Left shift assign.

>>=

Binary.

Right shift assign.

++

Unary.

Increment.

--

Unary.

Decrement.

[]

Unary.

Array item dereference.


Division of Vectors

Vectors can be divided by scalars, but not by other vectors. Therefore, writing an operator /() function for the vector class is just a matter of writing a function that divides a vector's components by a scalar. If you look in Listing 5.2, you'll see the prototype for the operator /() function on lines 2829. Here's the code:

 inline vector vector::operator /( int rightOperand) { vector temp; temp.x = x / rightOperand; temp.y = y / rightOperand; return (temp); } 


In a manner similar to the operator *() function, the operator /() function creates a temporary variable. It divides the x and y components of the vector by the right operand and stores the results in the temporary variable. Lastly, it returns the contents of the temporary variable to the calling program.

Factoid

Variables in functions are called temporary or local variables.


When your program calls the operator /() function, it uses the style you would expect for division. For example:

 vector v1(10,20); vector v2; v2 = v1 / 10; 


As with addition and multiplication, the vector object on the left of the / operator calls the operator /() function. That is normally how we would expect division to work. If we were to reverse the order, as in the following code fragment, it looks strange:

 vector v1(10,20); vector v2; v2 = 10 / v1; 


This is not following the normal rules of division. Just as 10/2 is not the same thing as 2/10, v1/10 is not the same thing as 10/v1. Nevertheless, to provide another example of the use of friend functions, I'll implement an operator that allows you to write equations such as 10/v1 in your programs. The prototype for this function is on lines 3032 of Listing 5.3.

 inline vector operator /(     int leftOperand,     vector &rightOperand) {     return (rightOperand / leftOperand); } 


You've already seen how to call this function, so I won't provide another example. I want to emphasize that function does not follow the normal rules of division. If I weren't providing another example of friend functions, I would not include it in LlamaWorks2D.

Operator-Equals Operators

C++ provides a useful shorthand version of the basic math operators that can be applied to many classes, including vectors. To see why these might be useful, suppose for a moment that you want to add a number to a variable and store the result in the same variable. In such a case, you might write code like this:

 int i = 10; i = i + 2; 


This code declares a variable and initializes it to 10. The expression

 i + 2 


gets the value in the variable i and adds 2 to it. The assignment operator, which is the equal sign, stores the result back in the variable i in the statement

 i = i + 2; 


There is a shorter way to write statements like this. C++ provides a += (pronounced "plus-equal") operator that gets the value from a variable, adds a number to it, and stores the result in the same variable. As a result, we can change statements such as

 i = i + 2; 


to

 i += 2; 


Although such a statement may look odd at first, programmers rapidly come to find the += operator very handy.

In addition, C++ provides similar operators for other operations such as subtraction, multiplication, and division. The general name for this type of operator is the operator-equals operators. Some of the most commonly used operator-equals operators are -=, *=, and /=.

Note

C++ provides more operator-equals operators than I show here. However, we will not cover them at this time because most of them involve operations we have not yet discussed.


The prototypes for the +=, -=, *=, and /= operators for the vector class appear on lines 3441 of Listing 5.2. We've already seen what it means to add and subtract vectors, so you can probably imagine how operators such as += might be used for a vector class. Here's an example:

 vector v1(10,20), v2(20,30); v1 += v2; 


This statement is the same as writing

 v1 = v1 + v2; 


It results in vector variable v1 being added to vector variable v2. The result is stored in v1.

Because the implementations of these four functions contains a fair bit of code, I'll show them in listing 5.3 rather than just displaying them in among the paragraphs of text as I have been doing with the other functions in the vector class.

Listing 5.3. The operator-equals functions for the vector class

 1      inline vector & vector::operator +=( 2  vector &rightOperand) 3      { 4          x += rightOperand.x; 5          y += rightOperand.y; 6          return (*this); 7      } 8 9      inline vector & vector::operator -=( 10         vector &rightOperand) 11     { 12         x -= rightOperand.x; 13         y -= rightOperand.y; 14         return (*this); 15     } 16 17     inline vector & vector::operator *=( 18           int rightOperand) 19    { 20        x *= rightOperand; 21        y *= rightOperand; 22        return (*this); 23     } 24 25     inline vector & vector::operator /=( 26     int rightOperand) 27     { 28         x /= rightOperand; 29         y /= rightOperand; 30         return (*this); 31     } 

The operator-equals operator functions in Listing 5.3 each use the operator-equals operators for the vector components. For instance, the operator +=() function on lines 17 uses the += operator for integers to add the x component of the right operand to the x component of the left operand and store the result in the left operand. It does the same on line 5 for the y components.

These four operator functions work differently than any we've discussed so far. First, they do not declare temporary variables. Second, they return something called *this. Third, the return type is vector &, not just vector. It's natural to wonder what all this means.

To understand what's happening here, let's first look at what happens when your functions return values. Normally, the value a function returns is automatically copied into a special area of memory called the program stack. The program uses its stack to store values that functions return. We can see how the stack works by looking back at the vector::X() function whose prototype is on line 11 of Listing 5.2 I'll repeat its code here for convenience:

 inline int vector::X(void) {     return (x); } 


This function does nothing but return a value. Suppose a program contains statements like the following:

 vector v1(10,20); int thisInt = v1.X(); 


When the program executes the statement

 int thisInt = v1.X(); 


it calls the X() function. The X() function in turn returns the value of v1's private data member x. When it does, the program automatically copies the value being returned onto the stack. It then jumps back to the point at which the X() function was called. Of course, that point was the statement

 int thisInt = v1.X(); 


The value being returned is still on the program's stack. The assignment operator (=) causes that value to be copied into the variable thisInt. The value on the stack is then thrown away, but that's okay because the program copied it into a variable. That's how things normally work when your function returns a value.

It is possible for function to bypass the normal mechanisms of returning a valuewhich is what's happening in Listing 5.3 with the operator-equals functions. Whenever the return type of a function is followed by an ampersand (&), it means that the function returns a reference instead of a value. When a function returns a reference, it returns an actual variable rather than a copy of the variable.

What does this all mean?

Look once more at how programs use the += operator. If your program contains the statements

 vector v1(10,20), v2(20,30); v1+=v2; 


then it is changing the contents of v1, and the results of the addition are stored there. If you write the operator +=() function for the vector class like this:

 inline vector vector::operator +=(     vector &rightOperand) {     vector temp;     temp.x = x + rightOperand.x;     temp.y = y + rightOperand.y;     return (temp); } 


This function has a problem. Do you see what it is?

In this example, the operator +=() function declares a variable called temp, which stores the results of the addition. The function then returns that variable. But the whole idea behind the += operator is to change the variable that called the function.

This version of the operator +=() function does not do that. At no time does it change the x or y value of the left operand.

"Well," you might say, "that's easy to fix. Just write it like you had it before, but return a vector instead of a vector &." If we did that, the code would look like this:

 inline vector vector::operator +=(     vector &rightOperand) {     x += rightOperand.x;     y += rightOperand.y; } 


What happened to the return statement? It's gone. When we get rid of the temporary variable temp, we no longer have anything to return. "That's okay," you might say. "I don't want to return anything anyway." You may not want to, but you need to. According to the normal rules of C++, all operator-equals functions must return a value. This enables you to write statements such as

 vector v1(10,20), v2(20,30), v3; v3 += v2 += v1; 


C++ does not force you to return a value from an operator +=() function. However, if you don't, other programmers will have problems with your code. They expect all operator +=() functions to return a value.

To solve this problem, the operator +=() function must return the left operand. The way it does that is with the statement

 return (*this); 


This statement is a way of saying, "Return this object." And in all cases, "this object" is the one that called the function.

If a function contains the statement

 return (*this); 


it cannot return a copy of an object. It must return a reference to an object instead. That's why the operator-equals functions in Listing 5.3 all return references. They have to bypass the normal return mechanisms and return a reference rather than a value because they all end with the statement

 return (*this); 


By using a reference for the return type, and by returning *this, your operator-equals functions all work properly. Your functions can change the contents of the left operand. They let you write statements such as

 v3 += v2 += v1; 


in your programs. This is how operator-equals functions are supposed to work.

Magnitude

Finding the length, or magnitude, of a vector does not have anything to do with overloading. However, I thought I'd cover it here because it's part of the vector class.

It is not unusual in games to need to get the length of a vector. For instance, you might want to compare the lengths of two vectors that represent the speed of sprites. That way, you could see which is moving faster.

The vector class stores the length of the vector's components, not the length of the vector itself. It has to calculate that. The formula for calculating a vector's length is given here.

To find the length of a vector from its components, you first square the components. Next, you add the squares together and find the square root of the result.

The vector class contains a function called Magnitude() that uses the formula above to calculate the length of the vector from its components. Here's the code:

 inline int vector::Magnitude() {     return (::sqrt(x*x + y*y)); } 


Using the formula, this function squares the vector's x and y components. It does so by multiplying the private data members x*x and y*y. It then adds the results. Next, the Magnitude() function calls the C++ Standard Library sqrt() function to find the square root. Whenever your program uses the sqrt() function, it must include the file math.h. So you should put the statement

 #include <math.h> 


at the beginning of your program's file.

Note

Putting the scope resolution operator (::) with no class name in front of a function call, as shown in the Magnitude() function, tells the compiler that the function (in this case sqrt()) is a member of the global namespace. This enables it to be accessed from anywhere in your program. In general, most things in the C++ Standard Library are either in the global namespace or the std namespace.


Finding a square root is a high-overhead operation. If you can avoid it, you should. This should not cause you a problem in your game. One of the main reasons to find the length of a vector is to compare it to the length of another vector or compare it to 0. In either case, you can use the square of the length instead.

Suppose the variable length1 contains the length of one vector and length2 contains the length of another. Imagine that length1 is greater than length2. If that's the case, then it's also true that length12 is greater than length22. Therefore, we don't need to compare the length of the vectors; we can compare the length of the squares of the vectors instead. The vector class enables you to do that by providing a function called MagnitudeSquared(). The MagnitudeSquared() function returns the length of a vector squared. Here's the code:

 inline int vector::MagnitudeSquared() {     return (x*x + y*y); } 


This is essentially the same as the Magnitude() function, except that it doesn't calculate the square root. Therefore, this function is much faster than Magnitude().

You should use it in your games instead of Magnitude() whenever you can.

Unit Vectors

A unit vector is a vector that has a length of 1. The vector class provides a function called Normalize(). This function calculates the unit vector for any vector variable. The resulting unit vector points in the same direction as the vector variable. Here's the code for Normalize():

 inline vector vector::Normalize() {     vector temp;     int length = Magnitude();     if (length != 0)     {         temp.x = x/length;         temp.y = y/length;     }     return (temp); } 


After declaring a local variable, this function tests to see if the length is not equal to 0. If it's not, the function divides the x and y components of the current vector object by the length and stores the result in temp. It then returns temp.



Creating Games in C++(c) A Step-by-Step Guide
Creating Games in C++: A Step-by-Step Guide
ISBN: 0735714347
EAN: 2147483647
Year: N/A
Pages: 148

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net