Quake c - of vectors and angles

Or how to tell where the gun aim is pointing and how to get there...

Posted by numbersix on Oct 2nd, 2011 *Intermediate Server Side Coding.*

What is a vector?

Dictionary.reference.com - "a quantity possessing both magnitude and direction, represented by an arrow the direction of which indicates the direction of the quantity and the length of which is proportional to the magnitude."

Game space concept: a single 3D point with an arrow heading some direction away from the point. The arrow point is the direction and the length of the arrow is the magnitude. This might seem like a simple concept, but it is not.

If you were to follow the vector from the origin point you would end up where it is pointing.

What is an angle?

Dictionary.reference.com - "c. the amount of

rotation needed to bring one line or plane into coincidence with

another, generally measured in radians or in degrees, minutes, and

seconds.

Game space concept: start with a facing angle of 0 degrees, and rotate some arbitrary degrees less than 360. This is referenced as Yaw. Then from that angle look some degrees up or down (generally restricted to less than 90 degrees) - this is referenced pitch.

The game engine (since the original quake 1 engine) stores this value in an entity variable vector named ".angles". The vector that points to the gun aim (center screen where the player is looking in just about every first person shooter...) in an entity variable vector named ".v_angle"

If you were to start a darkplaces game, with only one player connected, pull down the console and enter:

prvm_edict server 1

You would see two entries among the many listed:

angles ' 30.0000 14.8700 0.0000'

v_angle ' -90.0000 14.8700 0.0000'

These are accessed in quake c with:

self.angles

self.v_angle

where self is a memory pointer to the entity in question.

Each vector is composed of 3 floating point values (as shown) that can be accessed in quake c:

self.angles_x

self.angles_y

self.angles_z

When working with the angles variable, _x is the pitch (how far the player ent is looking up and down from a horizontal plane at 0 degrees pitch.) This is a rotation around the y axis. The _y is the yaw component - how far away from 0 degrees the player ent is rotated around the z axis.

It is interesting to note that when characters in Star Trek refer to a bearing angle mark some other angle with regards to piloting their ship that bearing is yaw, and the mark is pitch.

Note: there are a couple caveats about the _x representation of pitch that need to be understood.

So, what can you do with this information?

Well, if the auto-aim feature is off (and it should be, unless you are a wimpy fps player) and you fire a: rocket, nail, or lightning bolt the self.v_angle is where that shot is going. If its a hit-scan weapon like a shotgun, the v_angle vector is followed till it hits a solid object or something that takes damage.

But other things can happen as well. Brass can be ejected from guns. The origin the object is fired from can be adjusted to conform to the exact point of the gun barrel. I did this for the visible weapons upgrade starting with painkeep 2.0.

There are 3 more vectors related to any vector - you can get them with this quake c:

makevectors(self.v_angle);

makevectors(self.angles);

That gives 3 normals:

v_forward

v_right

v_up

A normal is a vector of length unit 1 - v_forward points exactly where v_angle points but with length 1. v_up points up 90 degrees from v_forward. This is not directly up but is rotated with the pitch of v_forward. Likewise v_right points right, rotated 90 degrees right from v_forward.

These are used to calculated distances along each vector. With v_forward you can find out what is 200 units in front of the gun aim:

float p;

p = pointcontents(self.origin + (v_forward * 200));

pointcontents is a quake c builtin that returns one of several CONTENT_* values.

CONTENT_EMPTY = -1;

CONTENT_SOLID = -2;

CONTENT_WATER = -3;

CONTENT_SLIME = -4;

CONTENT_LAVA = -5;

CONTENT_SKY = -6;

So if empty space was 200 units along the gun aim, CONTENT_EMPTY would be assigned to p.

That explains how you get to a point along the vector of the gun aim.

Now say you want to point your player in a certain direction. You can NOT assign the vector to v_angle. You have to change self.angles.

You take a direction vector v1:

vector v1, v2;

v1 = func_providing_vector_facing()

v2 = vectoangles(v1);

self.angles_y = v2_y;

This changes the self entity facing without adjusting the pitch angle. If you wanted the pitch angle as well:

self.angles = v2;

NOTE: to actually change a player entity facing angle or pitch you need a bit more code.

If you want to go from pitch and yaw to a vector you use makevectors noted above:

makevectors(self.angles);

You should now understand the relationship between the gun aim vector and entity facing angles.

This concept is used over and over in quake c programming.

Note: about using pitch for calculations. This is from an old code segment that used pitch:

// This corrects an ID mistake. They had the pitch angle in

// reverse.

dir2_x = dir2_x * -1;

If anyone wants to know how (if it does) affects quake c coding, I'll leave that for the comments section and the forum boards.

When you test it - pitch angle is from 30 (looking straight up) to -30 (looking straight down).

This would seem to support the reverse comment (adding degrees to pitch should look down and vice versa) and if you are calculating degrees from the plane at 0 degrees pitch, the max of 30 is 1/3 of the 90 degree angle.

Post a comment

Profile

Tutorial

Related Engines

Related Groups

thanks

Quake-c Manual ver 3.4 entry for vector math:

2.1.3 Vector type

A vector, made of 3 float coordinates.

Used to represent positions or directions in 3D space.

Valid syntax: '0 0 0' or '20.5 -10 0.00001'

Note the simple quotes around the vector. Do not use double quotes, they are reserved for strings.

If you declare a vector foobar, then you can access it's x, y and z fields with: foobar_x, foobar_y,foobar_z.

8.2 Vector maths

Function: normalize

vector normalize(vector v)

returns a vector of length 1

Gives the vector colinear to v, but of length 1. This can be useful for calculation of distance along an axis.

Function: vlen

float vlen(vector v)

Returns the length of vector v (never < 0).

Function: vectoyaw

float vectoyaw(vector v)

returns and angle in degree

Vector to yaw: calculates the yaw angle (bearing) corresponding to a given 3D direction v.

Function: vectoangles

vector vectoangles(vector v)

returns vector 'pitch yaw 0 '

Vector to angles: calculates the pitch angle (aiming) and yaw angle (bearing) corresponding to a given 3D direction v.

Function: vtos

string vtos(vector v)

Vector to String: print a vector, as a string.

Function: makevectors

void makevectors(vector angles)

angle = 'pitch yaw 0'

Calculate the vectors pointing forward, right and up, according to the provided angles.

Returns result in the global variables:

vector v_forward; // points forward

vector v_up; // points up

vector v_right; // points toward the right

Quake-c Manual ver 3.4 entry for entity movement - (this uses or affects some of the same vectors):

8.5 Entity movements

Function: ChangeYaw

void ChangeYaw()

Change the horizontal orientation of self. Turns towards self.ideal_yaw at self.yaw_speed, and sets the global variable current_yaw.

Called every 0.1 sec by monsters

Function: walkmove

float walkmove(float yaw, float dist)

returns TRUE or FALSE

Moves self in the given direction.

Returns FALSE if could not move (used to detect blocked monsters).

Function: droptofloor

float droptofloor()

returns TRUE or FALSE

Drops self to the floor, if the floor is less than -256 coordinates below.

Returns TRUE if landed on floor.

Mainly used to spawn items or walking monsters on the floor.

Function: setorigin

void setorigin (entity e, vector position)

e = entity to be moved

position = new position for the entity

Move an entity to a given location. That function is to be used when spawning an entity or when teleporting it.

This is the only valid way to move an object without using the physics of the world (setting velocity and waiting).

DO NOT change directly e.origin, otherwise internal links would be screwed, and entity clipping would be messed up.

Function: setsize

void setsize (entity e, vector min, vector max)

e = entity whose bounding box is to be set

min = minimum, for bounding box (ex: VEC_HULL2_MIN)

max = maximum, for bounding box (ex: VEC_HULL2_MAX)

Set the size of the entity bounding box, relative to the entity origin. The size box is rotated by the current angle.

Function: movetogoal

void movetogoal (float step)

Move self toward it's goal.

Used for monsters.