# Issues with Vectors in Zero-G Spaceflight

7 replies to this topic

### #1Flamesilver

New Member

• Members
• 28 posts

Posted 11 February 2011 - 02:15 PM

I'm trying to prototype a Star Control / Space War type zero-G flying game using Visual C++ 2008 and Dark GDK. My physics code isn't quite working.

Everything seems fine when I'm facing Right (1st and 4th Quadrant, I think), but then when I face left weird stuff happens...

Go easy on my guys - I haven't written code in 7 years and it's my first 3D project ever.


#include "DarkGDK.h"

#include <math.h>

// pi and conversion function

#define PI 3.14159265358979323846

class vect2 {

// Generic Vector Class

public:

float x, y;	// seperate component coordinates

// ctor

vect2( float in_x = 0, float in_y = 0 ) {

x = in_x;

y = in_y;

}

float getAngle() {	return (float) RAD2DEG(atan( y / x )); }	// returns angle of vector based on x, y

float getLength() {	return (float) sqrt( x*x + y*y ); }		// returns magnitude

// set x, y based on angle , magnitude

void setXYFromAM( float a, float m ) {

x = m * cos( DEG2RAD(a) );

y = m * sin( DEG2RAD(a) );

}

vect2 operator+ ( vect2 v ) { return vect2( x + v.x, y + v.y ); }	// add 2 vectors

};

class Ship {

private:

int id;			// id of the ship and form object

float x, y, z;	// position in space

float dir;		// direction facing

float aim;		// direction aiming

float speed;		// current speed

public:

Ship (int i);				// constructor

void reset();

float getX() { return x; }	// accessors

float getY() { return y; }

float getZ() { return z; }

float getDir() { return dir; }

int getid() { return id; }

float setX( float i ) { x = i; }	// set functions

float setY( float i ) { y = i; }

float setZ( float i ) { z = i; }

float setDir( float i ) { dir = i; }	// set dir

void thrust( float i );						// thrust forward or backward

void turn ( float i ) { aim = aim + i; }	// change facing direction of ship

void update();								// updates position of the ship based on dir, speed

};

Ship::Ship( int i ) {

id = i;				// get ship id

reset();			// call the reset function to init

}

void Ship::reset() {

x = y = z = 0;		// position to be 0,0,0

aim = 90;			// faces 90

dir = 90;			// goes 90

speed = 0;			// not moving

}

void Ship::thrust(float i) {

// add a thrust vector based on aim, thrust to the current movement vector

// declare vT, vM

vect2 vT, vM, result;						// declare Thrust, Cur Movement, and Result (new move) vector

vT.setXYFromAM( aim, i );		// initialize Thrust vector from aim, i

vM.setXYFromAM( dir, speed );	// initialize Move vector from dir, speed

result = vT + vM;							// result vector = adding thrust to current movement

dir = result.getAngle();		// calculate new direction based on added vector (rounded)

speed = result.getLength();		// calculate new speed

}

void Ship::update() {

vect2 pos( x, y );		// make pos vector based on current x, y

vect2 v;				// the movement vector

v.setXYFromAM( dir, speed );

pos = pos + v;

x = pos.x;

y = pos.y;

dbRotateObject(id, 0, 0, aim);

// update the position

dbPositionObject(id, x, y, z);

}

// the main entry point for the application is this function

void DarkGDK ( void )

{

dbSyncOn   ( );			// sync

dbSyncRate ( 60 );

dbRandomize ( dbTimer ( ) ); // set our random seed to a value from the timer

Ship PlayerShip(1);		// instance Player

// Load the ship and place it at 0,0,0

dbPositionObject(PlayerShip.getid(), PlayerShip.getX(), PlayerShip.getY(), PlayerShip.getZ());

// Position the Camera

dbPositionCamera ( 0, 0, -2500 );

// GDK Loop

while ( LoopGDK() ) {

// display some text on screen

dbText ( 0, 0, "Some Text" );

// add thrust to the ship

if ( dbUpKey ( ) )

PlayerShip.thrust( 3 );

// subtract thrust to the ship

if ( dbDownKey ( ) )

PlayerShip.thrust( -3 );

// turn ship left

if ( dbLeftKey ( ) )

PlayerShip.turn( 11.25 );

// turn ship right

if ( dbRightKey ( ) )

PlayerShip.turn( -11.25 );

// reset ship

if ( dbSpaceKey ( ) )

PlayerShip.reset();

// Update Ship Position

PlayerShip.update();

// Perform Sync

dbSync();

}

dbDeleteObject ( PlayerShip.getid() );	// cleanup PlayerShip

return;

}



### #2tobeythorn

Valued Member

• Members
• 189 posts

Posted 11 February 2011 - 02:56 PM

in getAngle, what happens when x = 0?

### #3Flamesilver

New Member

• Members
• 28 posts

Posted 11 February 2011 - 06:08 PM

Wow... I'm surprised it's not crashing with divide by zero...

I'll start with that little fix, but I'd imagine the problem is a bit deeper than that.

### #4Flamesilver

New Member

• Members
• 28 posts

Posted 14 February 2011 - 05:08 AM

Hey Everyone! Problem SOLVED! Thanks you for your efforts.

I didn't really find out exactly what the problem was, but I cleaned up my code and rethought some of my design decisions. I ended up storing a movement vector for the inertia of the ship as movementVector(x, y), did the thrust adding via another vector, and skipped out on the nasty conversion back to angle,magnitude representations.

Here's my code for those who care (note I even added in maximum speed accounting for outside factors accelerating the ship beyond max thrustable speed, but never being able to add more thrust to a ship moving beyond max speed)

#include "DarkGDK.h"
#include <math.h>
#include <stdio.h>

// pi and conversion function
#define PI 3.14159265358979323846

class vect2 {

// Generic Vector Class

public:

float x, y;	// seperate component coordinates

// ctor
vect2( float in_x = 0, float in_y = 0 ) {
x = in_x;
y = in_y;
}

void setXY( float i, float j ) { x = i; y = j; }	// set (x, y)

void normalize() {

// normalize to unit vector (direction only)
// if length > 0  you divide each side by length

float l = getLength();	// get the current length

if ( l != 0 ) {
x /= l;
y /= l;
}
}

float getAngle() {	return (float) RAD2DEG(atan( y / x )); }	// returns angle of vector based on x, y
float getLength() {	return (float) sqrt( ((x*x) + (y*y)) ); }		// returns magnitude

// set x, y based on angle , magnitude
void setXYFromAM( float a, float m ) {
x = m * cos( DEG2RAD(a) );
y = m * sin( DEG2RAD(a) );
}

void scaleTo ( float s ) {

normalize();
x *= s;
y *= s;

}

vect2 operator+ ( vect2 v ) { return vect2( x + v.x, y + v.y ); }	// add 2 vectors
vect2 operator+= ( vect2 v ) { return vect2( x += v.x, y += v.y ); }	// add 2 vectors

};

class Ship {

private:

int id;			// id of the ship and form object

float x, y, z;	// position in space
vect2 movementVector;	// current movement vector due to inertia (used to update ship position)

float aim;				// direction aiming

float thrustspeed;		// thrust speed
float turnspeed;		// turning speed
float maxspeed;			// maximum speed

public:

Ship (int i);				// constructor
void reset();

float getX() { return x; }	// accessors
float getY() { return y; }
float getZ() { return z; }

int getid() { return id; }

void setX( float i ) { x = i; }	// set functions
void setY( float i ) { y = i; }
void setZ( float i ) { z = i; }
void setXYZ( float i, float j, float k ) { x = i; y = j; z = k; }

void setTurnspeed( float i ) { turnspeed = i; }	// set turnspeed, thrustspeed, and maximum speed
void setThrustspeed( float i ) { thrustspeed = i; }
void setMaxspeed( float i ) { maxspeed = i; }

void thrust( float i );						// thrust forward or backward
void turn ( float i ) { aim = aim + i*turnspeed; }	// change facing direction of ship

void update();								// updates position of the ship based on dir, speed

};

Ship::Ship( int i ) {

id = i;				// get ship id
reset();			// call the reset function to init
}

void Ship::reset() {

x = y = z = 0;					// position to be 0,0,0
aim = 0;						// faces 90
movementVector.setXY(0, 0);		// no movement

}

void Ship::thrust(float i) {

// add a thrust vector based on aim, thrust to the current movement vector
// declare vT, vM

vect2 vT;				// declare Thrust, Cur Movement, and Result (new move) vector
float l1, l2;			// length temp variables

vT.setXYFromAM( aim, i*thrustspeed );		// initialize Thrust vector from aim, i

l1 = movementVector.getLength();			// get original speed it's moving at

movementVector += vT;						// result vector = adding thrust to current movement

l2 = movementVector.getLength();

// speed limiting code
if ( ( l2 > l1 ) && ( l1 > maxspeed ) ) {

// if it was already going faster than maxspeed, make it go at original speed with current vector

movementVector.scaleTo( l1 - thrustspeed );

}

}

void Ship::update() {

x += movementVector.x;
y += movementVector.y;

dbRotateObject(id, 0, 0, aim);

// update the position
dbPositionObject(id, x, y, z);

}

// the main entry point for the application is this function
void DarkGDK ( void )
{

dbSyncOn   ( );			// sync
dbSyncRate ( 60 );

dbRandomize ( dbTimer ( ) ); // set our random seed to a value from the timer

// Create Playership and Set Variables
Ship PlayerShip(1);		// instance Player
PlayerShip.setThrustspeed(.5);		// set Thrust applied per cycle
PlayerShip.setTurnspeed(3);			// set turn applied per cycle
PlayerShip.setMaxspeed(20);			// set turn applied per cycle

// Load the ship and place it at 0,0,0
dbPositionObject(PlayerShip.getid(), PlayerShip.getX(), PlayerShip.getY(), PlayerShip.getZ());

// Position the Camera
dbPositionCamera ( 0, 0, -2500 );

// GDK Loop

while ( LoopGDK() ) {

// display some text on screen
dbText ( 0, 0, "Sigh.... " );

// add thrust to the ship
if ( dbUpKey ( ) )
PlayerShip.thrust( 1 );

// subtract thrust to the ship
if ( dbDownKey ( ) )
PlayerShip.thrust( -1 );

// turn ship left
if ( dbLeftKey ( ) )
PlayerShip.turn( 1 );

// turn ship right
if ( dbRightKey ( ) )
PlayerShip.turn( -1 );

// reset ship
if ( dbSpaceKey ( ) )
PlayerShip.reset();

// Update Ship Position
PlayerShip.update();

// Perform Sync
dbSync();

}

dbDeleteObject ( PlayerShip.getid() );	// cleanup PlayerShip

return;
}



### #5tobeythorn

Valued Member

• Members
• 189 posts

Posted 14 February 2011 - 05:40 AM

glad you solved your problem and cleaned up your code. I do not have fond memories of working with inverse trig functions in game programming and try to avoid them at all costs. If you do use them (and sometime you really do need them), remember you have to check for divide by zeros of your input, and also check to make sure that your input is valid (imprecision can turn an +/-1.0 into a +/-1.0001 which will fail when you take the arccos or arcsin).

### #6}:+()___ (Smile)

Member

• Members
• 169 posts

Posted 16 February 2011 - 01:58 PM

There is two-argument inverse trigonometric function atan2(y,x) which does the same as atan(y/x) with automatic quadrant detection and without division by zero problem. I suggest to use it instead all other arc-functions (in fact, these functions defined in terms of atan2).

Also, why use degrees for angles? I suggest using small integer types with warping arithmetics. For example, use 1-byte integer and measure angles in 360/256 degree units, or 2-byte with 360/65536 units, etc... This measure eliminates overflow/precision problem with non-warping angles.
Sorry my broken english!

### #7tobeythorn

Valued Member

• Members
• 189 posts

Posted 16 February 2011 - 04:18 PM

Smile,
Some interesting suggestions you have. Thanks man.

### #8Flamesilver

New Member

• Members
• 28 posts

Posted 22 February 2011 - 12:34 AM

Yeah, thanks Smile! I'm going to use that to improve my current vector classes.

#### 1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users