Implementing 2D Vectors
by Willem van Doesburg © March 2000

download samples: 2DVector.h 2DVector.cpp Ball.h Ball.cpp

Introduction

In my explorations as a beginner game-programmer across the web and various books, I have not yet found a document that satisfactorily explains how to implement vectors in a game. Sure, I found numerous documents on: vector math, drawing lines, vector classes and physics. But I couldn't find a document that explains how all these things play a part in making a game. A document on vector math doesn't deal with the twisted co-ordinate system of a computer screen. A document on drawing lines doesn't explain how to represent a line as a class. A Vector class document doesn't explain operations on that class. A physics document doesn't give you any useful algorithms for your program. This document attempts to satisfy all the shortcomings of those other documents. This document will use C++ notation for examples because to me there is only ONE language.

A Vector (class)

Vectors in general can be represented in 2 ways.

1: by a length and an angle
2: by a begin and endpoint

These two methods each have their own math. Method 1 uses trigonometry to calculate angles and normals. Method 2 uses matrix math to calculate rotations and other operations. In this document we'll implement a vector represented by method 1.

Here's the class listing:

class C2DVector { private: long x,y; //Endpoint coordinates int angle; //Angle of vector long length; //length of the vector public: void SetX( long Xin ); //Set the X element of the endpoint void SetY( long Yin ); //Set the Y element of the endpoint void SetLength( long Lin ); //Set the length in pixels void SetAngle( int Ain ); //Set the angle in degrees long GetX(); //Get the X element of the endpoint long GetY(); //Get the Y element of the endpoint long GetLength(); //Get the length int GetAngle(); //Get the angle public: void operator=(C2DVector aVector); //Make this vector equal with another C2DVector(); //constructor C2DVector( long Xin, long Yin ); //overloaded constructor ~C2DVector(); //destructor };

I'll have to explain some things about this implementation of a Vector class.

You'll notice that I included the endpoint in my class while I said earlier that you only need an angle and length to specify a vector. I chose to include the endpoint because of efficiency. You need a cosine and sine function to calculate the endpoint. By storing the endpoint we only have to do this calculation once every time the endpoint changes instead of every time we want to use the endpoint. The length of the vector is specified in amount of pixels but that could easily be modified to anything you want.

Another important thing to keep in mind is the co-ordinate system we use. Normal vector mathematics usually uses the Cartesian co-ordinate system.

A Cartesian co-ordinate system looks like this:

The computer designers of course chose a different approach to deal with things (as usual).

A computer screen uses the following co-ordinate system:

To make it easy for ourselves (so we don't need to rebuild a lot of formulas) we will use the Cartesian co-ordinate system. Later in this document I'll tell you how to convert your co-ordinates from 1 system to the other and back.

The angle that we use in our class is the angle between the vector and the positive X-axis.

Preparations

There are some numbers you'll need for the calculations. We need cosine and sine to calculate rotations. Experienced readers will have noticed that I store the vector angle in degrees while most math libraries use radians to calculate sine and cosine. So we'll need to convert our degrees to radians for these functions. For that we need PI so let's define PI.

#define PI 3.141592654

Again for efficiency I am going to store both sine and cosine values for every angle in an array so we don't need to use those slow sin() and cos() routines every time. So let's declare those:

float costable[360]; float sintable[360];

We'll also need a routine to fill these tables.

This function needs to convert degrees to radians, which can be done by:

rad = deg / 180 * PI

void InitTables() { float temp; for( int i = 0; i<360; i++ ) { temp = ((float)(360-i)/180; //cast needed for calculation //I use (360 - i) to compensate for the weird screen coordinates temp *= PI; costable[i] = (float)cos( temp ); sintable[i] = (float)sin( temp ); } }

The class definition

Now that we're prepared let's look at the 2 most important functions of our class. SetLength( long Lin ) and SetAngle( long Ain ) these functions cause a new endpoint to be calculated and stored.

We can do this using the following calculation:

x = cos(angle) * length
y = sin(angle) * length

Here's what the functions look like in our code:

void C2DVector::SetLength( long Lin ) { length = Lin; x = costable[angle]*length; y = sintable[angle]*length; } void C2DVector::SetAngle( int Ain ) { angle = Ain; x = costable[angle]*length; y = sintable[angle]*length; }

How to use this class

Now that we have a vector class we want to put it to use.

Well let's use a pooltable to test our vector.

In this scenario we'll have an object Ball which will use a vector to keep track of it's velocity.

This is quite simple we just move our ball around by specifying an angle and a length and then we keep adding the endpoint x,y to the x,y of the ball.

A problem arises when our ball hits a cushion on the pooltable. When this happens we'll have to calculate a new velocity vector so our ball will bounce naturally. If we now the normal of the cushion we can calculate the new vector as follows:

  1. invert the velocity vector
  2. calculate delta( v, n )
  3. angle = angle +/- delta( v, n)

Here's the code:

void CBall::HitBoundary( C2DVector* BoundaryNormal ) { int angle; //the new angle int OppAngle; //the inverted old angle int NormDiffAngle; //difference between the inverted angle & the BoundaryNormal angle angle = Velocity.GetAngle(); OppAngle = (angle + 180) % 360; if( BoundaryNormal->GetAngle() >= OppAngle ) { NormDiffAngle = BoundaryNormal->GetAngle() - OppAngle; angle = (BoundaryNormal->GetAngle() + NormDiffAngle) % 360; } if( BoundaryNormal->GetAngle() < OppAngle ) { NormDiffAngle = OppAngle - BoundaryNormal->GetAngle(); angle = BoundaryNormal->GetAngle() - NormDiffAngle; if( angle < 0 ) angle += 360; } Velocity.SetAngle( angle ); }

In the implementation of my Vector class I adjusted for the weird screen coordinate system. But if you ever need to convert cartesian coordinates to the screen you can do this as follows:

void ConvertCoordinates( long* X, long* Y, BOOL convert ) { int Xmiddle; int Ymiddle; Xmiddle = (int)(XRES/2); //XRES is the Screen resolution in the x direction Ymiddle = (int)(YRES/2); //YRES is the Screen resolution in the y direction if( convert) { *Y = -*Y; *X = *X + Xmiddle; *Y = *Y + Ymiddle; } else { *X = *X - Xmiddle; *Y = *Y - Ymiddle; *Y = -*Y; } }

Conclusion

Now you know how to effectively use vectors in your games. You'll be suprised at the number of situations you can apply vectors in, especially in games. I've included links for some sample code at the top of this page.

Discuss this article in the forums


Date this article was posted to GameDev.net: 5/21/2000
(Note that this date does not necessarily correspond to the date the article was written)

See Also:
General
Sweet Snippets

© 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
Comments? Questions? Feedback? Click here!