Primer on Vector Math (for Unity)

I’m not sure if this is useful to anyone or not, but here is my 10-section primer on beginner Vector Math in Unity.

I’m hoping it will be helpful to some people, but since its a giant unedited wall of text, it probably won’t. But at least some people may get an idea of what specific terms or concepts they should research.

2 Likes

Section 1, part 1. What is a data type?

Let’s keep things simple and say that for our purposes, a data type is a type of data. Data is information, so data type is a type of information. In math, we have different number types such as real numbers, whole numbers, complex numbers, etc. In literature, we have different data types such as the poem, the short story, the novel, etc. In programming, our data types are things like integers (whole numbers), floats (decimel numbers), strings (letters and words), etc.

Section 1, part 2. What is a Vector2?

A Vector2 is poorly named, because it doesn’t necessarily have anything to do with vectors. A Vector2 is simply a data type, custom made by the folks at Big Unity. Instead of being represented by a number, it’s a piece of data that is represented with two numbers. (Two floats, actually.)

It is used to represent vectors, hence the name, but it can also represent anything we track with two numbers. It can be height (feet and inches), for example, or the final scores to a baseball game. Understanding this now will make things easier later: A vector2 is not necessarily a vector. Don’t get confused by the name. A vector2 is just a group of 2 numbers, taken together in their specific order.

score[0] = Vector2(6,3);

score[1] = Vector2(7,4);

score[2] = Vector2(2,3);

These are vector2s. They don’t mean anything or represent anything until we give them meaning. In this case, we’ve decided they represent the final scores to three baseball games. The first number, we’ve decided, is the player’s team score and the second number is the opponent’s team number. We can look at any of these scores and deduce who won. They don’t have anything to do with vectors or transforms or anything. They are just Vector2s.

Notice that we can’t just write (6,3) and expect Unity to understand what we are talking about. We have to specifically say Vector2. This is calling the Vector2 class (or something like that). The Vector2 class returns a custom data type, but it needs two floats, so we have to pass those floats in parenthesis after saying Vector2. Vector2(6,3), for example, will return a different custom value than Vector2(7,4)

1 Like

Section 2. Making a Vector2

A vector2 is not a standard data type. (Standard? Generic? Default? I’m not sure what the right word is here.) It’s a custom data type. You can make your own data types. In this case, it’s constructed out of two floats, seperated by a comma.

When we want to make a new data type, we must use the "new" keyword. This is a hard concept to grasp, but take a look at the examples above. If I have two scores for the first game (game 0), 6 and 3, I may be tempted to try to put them in a Vector2 like this:

score[0] = Vector2(6,3);

or

score[0] = Vector2(playerScore, opponentScore);

Those won’t actually work because that custom data type hasn’t actually been constructed. The number (6,3) is not a type of data your computer recognizes yet. You need to say something like, “I want to make a new type of data, it’s going to be a vector2, and I want it to be constructed using these numbers.” The way we do that is:

score[0] = new Vector2(6,3);

We only really need to do this when establishing the initial values. There are some Vector2s that are so useful and are used so often, that Unity keeps those Vector2s built-in and gives them names. Those are: (0,0), (0,1), and (1,0).

Those values are stored in variables under the Vector2 class. To use them, you simple type Vector2.zero, Vector2.up, and Vector2.right.

Vector2.zero = Vector2(0,0)

Vector2.up = Vector2(0,1)

Vector2.right = Vector2(1,0)

If you are using Vector3, the Vector3 class works just like Vector2 except it takes in three numbers as floats. In this case:

Vector3.zero = Vector3(0,0,0)

Vector3.right = Vector3(1,0,0)

Vector3.up = Vector3(0,1,0)

Vector3.forward = Vector3(0,0,1)

Notice that the order of these numbers matters. Vector2(1,0) and Vector2(0,1) are very different values. How different are they? Well, it depends on what the Vector2 represents to us. If the Vector2 was the final score of a baseball game, then it’s the difference between an epic victory and disheartening defeat.

So if we wanted to keep track of baseball scores in Vector2s and we were starting a brand new game we can either say:

score[3] = new Vector2(0,0);

or

score[3] = Vector2.zero;

This is because Vector2(0,0) already exists in Unity as Vector2.zero, ready for your use. This won’t stop you from making a new Vector2(0,0) if you want, just like any other variables created in Unity can have the same values.

Section 3. Learning to Count Section Numbers

I apparently forgot to write a section 3.

Section 4. Using a Vector2 (as Position)

Now that you understand how vector2s are made, let’s go into how they are modified… but before we do, let’s talk about one of the most common ways to use Vector2s: position. The reason why I made such a big deal before that Vector2s are not vectors, is because Vector2s are FREQUENTLY used to keep track of positions, and positions are not vectors.

A position is a unique point in world space. Every position is unique and identified by a Vector2 (or Vector3 in a 3D environment).

If you had the entire 2d world laid out in a screen (like in Super Mario Brothers -style platformer), then any given point can be identified by a set of coordinates. Let’s say, the center of our world is in the center of the screen.

These coordinates are represented using grid coordinates, using an x and y axis. Typically, we keep track of those (x,y) coordinates in a Vector2.

So let’s say that the right side of the screen is 6 units away from the center, at x=6, so the right-center of the screne is Vector2(6,0). The center-left would be Vector2(-6,0).

The specific numbers don’t matter. What matters is that when we are talking about position, Vector2s represent (x,y) where x and y are coordinates within Unity “world space”. The position is held in a component called the “transform”. (Why is it called this? I don’t know – the words transform and translate have strange meanings and I think it has something to do with the lingo of digital art.) The transform is a component that holds all your position and orientation data in world space at any given time.

At any given point, something is going to have a position in world space. We tend to put "empty" game objects at position (0,0).

If we want to change a unit’s position, we can just assign a new Vector2 to the transform’s position property. We just have to construct a new Vector2 and assign that to transform.position.

Let’s say we have a character at (0,0) and we want to move him to the right six spaces. We can’t just say “add 6 to x”. We have to make a new Vector2, using that “new” constructor we talked about earlier.

transform.position = new Vector2(6,0);

Your transform WAS at 0,0. Now, after this command, it’s been changed. The numbers 6 and 0 were used to create a new Vector2 data type, and that data was put into transform.position, overriding whatever information was there. Instead of (0,0), your position is (6,0).

This is one of several different options we have available to move things via secript. WE can simply change the position of something. If we change it by a small amount every frame, it will have the appearance of movement. This is essentially how all movement works in computers. Understanding that things don’t actually move, but appear to move because of really fast changes in position (micro-teleports) will help you with physics issues later.

Section 5 was accidently posted out of order. I apologize for that. You can scroll down to the bottom to find section 5.

Click Here For Section 5


Section 6. What’s a vector?

Because Vector2s are frequently used to represent both positions and vectors, the first thing to realize is that positions are not vectors. That’s important because in code, they can often look the same and you will often be using them together. Annoying, right?

A vector is a mathmatical and physics concept usually representing a force, velocity, or direction of movement. Specifically, it represents a change in position, but doesn’t hold any data about the position itself. This is surprisingly useful.

Let’s say, I have a Vector2(0,2) and this represents a vector now. Specifically it represents a change in direction that doesn’t change the x direction (to the right) but increases the y direction (upward) by 2. For now, let’s store this vector in a variable called jump.

Vector2 jump = new Vector2(0,2);

Here’s the weird thing about vectors: they have no position information. They can start from anywhere, any position. This “jump” vector just means something is moving two spaces upward. Maybe it’s my player character, maybe it’s a rabbit on the screen somewhere. If we are both jumping, we might have the same vector but be in two completely different positions.

Here’s where this gets really useful: if you add a vector to a position, you get a new position. If my player is at (0,0) and I add “jump” to his position, he’ll have a new position at (0,2).

Let’s take a look at that for a second:

transform.position = transform.position + jump

Notice how there’s no need to construct a new vector2. We are adding two Vectors2 that already exist, and the result is naturally a new Vector2. More specifically, we adding a vector to a position, resulting in a new specific position.

If I instead wrote

transform.position = transform.position - jump

then this is the same as:

transform.position = transform.position - new Vector2(0,2)

or

transform.position = transform.position + new Vector2(-0,-2)

So by simply adding a "negative sign" or "subtracting", I will move downward instead of upward. A negative vector will always result in a vecotor going in the opposite direction.

Section 7. Working With Vectors and Position

Let’s say you are have a ball at position (0,0). When the character kicks this ball, you want it to fly forward and slightly upward.

First, it will help to know which way the character is facing. If we can access this character’s transform, his transform stores his “relative rotation” as a Vector2 (or Vector3 in a 3D world).

The variable “Vector2.right” will always refer to the right side of the screen, but the variable “transform.right” will always be a Vector2 that points in the direction toward the right side of the screen at the character’s default rotation. Imagine, he starts facing right. So “transform.right” is his forward direction. But later, he switches facing, so now he’s facing a new direction. “transform.right” is sitll forward for him, even thought that’s facing left now. (Don’t get too hung up on names here.) Just remember that “transform” is his component and those values are unique to him. The values stores in the Vector2 class are world-wide components and are the same for everyone.

This is useful because if we have the character’s transform, we can send the ball in that direction.

We may be tempted to write:

float horizontalBallDirection = character.transform.right.x;

The problem is that we don’t want the ball always to go in one direction. We want it to look at the character’s facing and translate that into a direction. It’s facing is transform.right (in local coordinates) and the TransformDirection() method gives us a useful function to change that to world coordinates.

float horizontalBallDirection = TransformDirection(character.transform.right).x;

First we get the character’s forward direction, which is always transform.right in local coordinates (because at it’s default rotation, it faces toward Vector2.right). Next, we use the TransformDirection() function to change this vector from being represented in local coordinates to being represented in world coordinates. (It is still the same vector, but is represented with different numbers because the 0,0 we are measuring from has changed.). Finally, we isolate the horizontal ‘x’ value, which is a float.

Now, regardless of which way the character is facing, the ball’s direction of world movement will be oriented in the same direction.

(I would have preferred “east” “west” etc for world coordinates and “left” “right” for local values, but whatever. You can actually do that if you want but it’s not default. Still, to prevent confusion, it can be helpful to think of transform.right as “right” and Vector3.right as “east”. The direction “right” will change as a character rotates, but “east” is always the same regardless of a character’s rotation.)

Now, we want the ball to go upward. Do we want world’s up or the character’s up? Hopefully, these are the same, but I’m going to go for the world’s up, I think.

verticalBallDirection = Vector2.up;

Now we have two seperate directions that the ball wants to go. A horizontal direction and a vertical direction. How do we get it to do both? Well, we simply add them.

ballDirection = horizontalBallDirection + verticalBallDirection;

Easy. So what is ballDirection now? Well, horizontalDirection was probably (1,0) or (-1,0), depending on which way the character is facing, and verticalDirection is (0,1). So now ball direction is likely to be (1,1) or (-1,1). Meaning it will move one direction to the right or left, plus one direction up. This is a nice vector to have, but it really doesn’t do much until we add it to a position.

Before we do though, there’s one more thing we should do: scale it.

Section 8. Clarifications on vectors

The coordinate system in vectors also use (x,y) convention and are stored as Vector2s, just like positions. But they are not positions. Those (x,y) are not specific points in space they are relative positions to the vector’s origins. Instead of (x,y), I like to think of vectors as (+x,+y). It just makes more sense to me. So a Vector2(0,3) can be thought of as (+0,+3), meaning it will add those numbers to whatever position it starts from. This makes more sense to me, and I hope it helps you as well.

When you add the vectors together, these additions still work. For example, a transform.right vector (+1,+0) and a Vector2.up vector (+0,+1), add up to a (+1,+1). You can see how this vector still isn’t super useful until we know the vector starts from. This is why vectors get added to positions, and why they result in a new position.

Vectors can be visualized as arrows. A vector of (0,2), for example, is an arrow that points upwards. A vector of (1,1) is a vector that points upwards and to the right, so it’s 45-degree diagnal. When visualizing vectors in your head, you can usually assume they start at (0,0). The final result at the end of the arrow is the new position, relative to the origin of the vecotor. (In this case, two units upward from the starting position, wherever that might be.)

When visualizing the addition of two vectors, first visualize one vector. (Either one). Imagine something moved to that new relative position. Then imagine the second vector being applied afterward.

So something like this

(0,2) ----------------> >(2,2)
^ (+2,+0)
^
|
| (+0,+2)
|
|
(0,0)

You are adding two vectors, (0,2) and (2,0). When visualizing this in your head you can imagine first moving two units up. Then once you are done with that, you can imagine moving two units to the right. The result is your final position like so:

//TODO: insert cool ascii art here

Just imagine that, you know, drawn better.

Section 9. Scaling Vectors

Vectors have another important property inherent in how they are recorded and the way we think of them. To understand this, it’s best to think of them as represented by arrows. This quality that they have is called the “magnitude” and it’s represented by the length of the vector.

When the vector represents movement (velocity), the length represents speed. A ball moving at a velocity vector of (1,1) is moving one unit upward and one unit to the right every unit of time that passes. (In most cases, our unit of time is frames, which is many times per second, but we’ll go into that later.) When we apply vectors to position, the arrow points in the direction of movement and the length of the arrow is how fast it’s moving. A Vector2(1,1) has a magnitude of 1. Magnitudes of 1 are kind of special and useful – we call them “normalized” vectors. If the vector’s length was any shorter or longer, it wouldn’t be normalized. For example, Vector2(3,3) is not normalized.

We tend to get nice, normalized vectors when we use Unity’s built-in vectors because they are all normalized. We can add Vector3.up, Vector3.right, and Vector3.forward in any order and we’ll always get a normalized vector as a result.

Normalized vectors are really easy to scale. A vector can be scaled by multiplying it times a float. This is a bit counter-intuitive, because it’s usually hard to perform math operations with two different data types. But in this case, a vector times a float, will always get you a new vector of a different size. This is called scaling and is really useful because it doesn’t change the direction of the vector.

Normally, the pythagream theorem is used to calculate magnitudes, but Unity will do this for you. In fact, when you create a Vector2, it’s magnitude is stored as a variable within that Vector2 function call. So, remember our jump vector. It’s really easy to get the magnitude of that: jump.magnitude

That tells us basically how fast something is jumping (per frame, in this case, which is absurdly fast. More on that later.) To turn this into a normalized vector, I can divide both coordinates by the magnitude… or I could just let Unity do it. Once again, it’s built into our Vector2s.

Vector2.normalized

This will get us a new Vector2, which will have the same direction as the original Vector2 but have a magnitude (arrow length) of exactly 1.

So let’s say, I wanted to build a jump function.

void HasJumped()

{

}

In this function, I’m take the character’s current position (wherever that might be) and I want to send it upwards. Let’s ignore “jump” for right now and use Unity’s built in “Vector2.up”, which is already normalized for us.

void HasJumped()

{

transform.position = newPositionAfterJumping;

}

What is newPositionAfterJumping? Well, it has to start from where you are now (the old position), and then we apply the up vector (jumping), and that gets us our new position. So it will be "transform.position + Vector2.up".

void HasJumped()

{

transform.position = transform.position + Vector2.up;

}

But how much does he jump? It’s logical at that some point we’re going to make a “jump speed” variable (that we’ll call jumpSpeed) and we want to apply this to the jump. Since the vector’s magnitude represents the speed, we want to adjust that magnitude by scaling the vector by jumpSpeed. (When we multiply a number to scale the vector that number is called the “scalar”.)

We’re going to be doing this a lot. “Position + (Vector * speed)” Don’t write that down quite yet, until we get to part 2 of this section.

So here’s how our function looks right now:

void HasJumped()

{

transform.position = transform.position + (Vector2.up * jumpSpeed);

}

Just to look at that in more detail:

(Vector2.up * jumpSpeed) // A vector times a float gets us a new vector.

// A normalized vector times a scalar gets us a new scaled vector

transform.position + (Vec… // A position plus a vector gets us a new position

transform.position = tra… // The new position is stored in the transform’s position property

Section 9, part 2. Time

Just a quick note because it’s come up quite a bit. Speed is a measure of distance over time. In scripts, our unit of time is one frame. Games run on many frames per second, such as 30fps, 60fps, etc. depending on hardware performance.

Speed is a measure of distance over time. Vectors are a measure of distance. The length of the arrow is how much distance is covered in one frame of time.

So, if you want to move a unit 6 units to the right, then you want to move it by “Vector2.right * moveSpeed”, but if you want to take 3 seconds to get there, how low should moveSpeed be? If you have 30 fps, then you can calculate that as 1/15, but I don’t know how to cap the fps and I’m not sure I’d want to.

Unity gives us a useful value to work with called Time.deltaTime. Time.deltaTime is the amount of time that passed since the last frame was loaded. It’s a very small amount of time - like 1/50th of a second. If you multiply a value by this number, the result will be very small, but then, you want only a small amount of movement each frame. In fact, this property is useful because if you multiply Time.deltaTime by any given value, that will result in a number of seconds equal to that value.

Let’s say each frame takes 1/50th of a second, or 0.02 seconds. If you multiplied speed of 2 by this amount, you’d get 0.04 speed. This is a small amount, but it moves 0.04 unity units every frame. So if you had 50 frames per second, then you would move exactly 2 units after 50 frames (1 second) had passed. Now, the frame times will fluctuate, but so will Time.deltaTime, so it will all work out.

If we want to move 6 units to the right, and we want it to take about 3 seconds, we need a moveSpeed of 3. When we apply this moveSpeed though, we can’t just use vector * moveSpeed, because that will mvoe us 3 units every frame, and there’s dozens of frames each second. What we need to do is say vector * moveSpeed * Time.deltaTime.

Here’s the quick summary: whenever you use speed, always multiply it by Time.deltaTime. This essentially gets you “distance per second” instead of just “distance”.

Section 10. Calculating Vectors

Quick review.

Vectors can be added to or multipied by other vectors to make a new vector from the result.

Vectors can also be multiplied by a scalar (float) to get a new vector with the same direction.

Vectors can be applied to positions, but this results in a new position, not a new vector.

So what happens when you add a position to another position?

I’m don’t think you can. IT doesn’t make sense.

I mean, I know you CAN, because any two Vector2s can be added, but don’t. That’s why I stress that you should know if any given Vector2 is a position or a vector. Some operations make sense, but some will just be goofy.

One very, very useful operation however is to subtract a position from another position. Paradoxically, this gets you a vector.

Position - position = vector.

What’s really useful about this, it is will get you a vector going from one position to the other.

Position To Go <------------------- Position Start

Vector

Vector2 vectorFromStartToGo = toGo.position - start.position;

Notice again how we don’t have to use the “new” keyword to create a new vector and plug in values. The new vector2 is a calculation from subtracting the two Vector2 positions. But the result is not a position, it’s a vector. And it’s a vector that looks kind of backwards to me. It goes from the position being subtracted to the position you start the operation from.

Basically, if I want to go my room, I should start by taking the room’s position. Then I subtract my own position. The result is a vector that goes to my room. I’m not quite sure the best way of explaining that right now, so that may just take practice.

I guess you can think of it this way – imagine you on a road trip to Graceland ('cause, why not?). To figure out how to get there, you’d look at your directions but you want to ignore all the progress you’ve already made. If there are 10 steps to get there, and you’ve already travelled 4, then to get to graceland you need to start with those 10 steps, take away the 4 you’ve already travelled, and the remaining steps are your directions to Graceland.

This should be more than enough material to get you going with vectors, movement, velocity, etc. There’s still more to learn, especially with line of sight, so maybe another few sections will be added later if there’s enough interest.

Happy GameDeving!

Section 5. Modifying a Vector2

Let’s get back to some math. Understanding that position is one of the most common ways to use a Vector2 will help you understand why Vector2s are built the way they are. Vector2s are built, as I said, with two numbers. Those numbers are labelled internally in the Vector2 class as “x” and “Y”. (Vector3s also have a “z”).

When you have a Vector2, the first number is always “x” and the second number is always “y”. This is true for any Vector2, even if we are using it to calculate game scores for a baseball game.

For example, if we had:

score[1] = Vector2(7,4);

Then the Vector2 data type corresponding to (7,4) is stored within score[1].

We could easily access the player’s score in this game later in the code by saying:

score[1].x

Likewise, the opponent’s score would be:

score[1].y

Now if the game was in progress, and we wanted to add +1 to the player’s score, it might be tempting to say:

score[1].x += 1;

But we can’t do that. While the Vector2 remembers the “x” value, it’s no longer a float – it’s a single data type (7,4) and we can’t just modify one of those numbers – because as I mentioned earlier, (8,4) would be a completely different value. Instead, we have to create a new Vector2 and put it in the score[1] variable.

score[1] = new Vector2 (score[1].x + 1, score[1].y);

score[1].x will return a 7, and 7+1 is equivilent to 8. Score[1].y is a 4. So this is esssentially the same as saying:

score[1] = new Vector2(8,4);

I hope that helps more than it confuses.

While we can’t add to the individual components of Vector2s, they can be added to EACHOTHER, because they are the same datatype. So if we decided to get all the total baseball scores by both teams, we can simply add all these Vector2s together. When that happens, each x value is combined together and each y value is combined together.

Vector2 combinedScore = score[0] + score[1] + score[2]

This gets us the value of Vector2(6,3) plus Vector2(7,4) plus Vector2(2,3), and puts the resulting sum in a Vector2 called “combinedScore”.

This will get us the same result as:

Vector2 combinedScore = new Vector2(15,10);

All the basic operations of addition, subtraction, muiltiplication and division can be done on Vector2s with other Vector2s. There’s no reason to really ever do any of this stuff with positions though. Math operations aren’t really needed until we start talking about vectors.

(Please take a break now if you have not done so already. Maybe get some lunch or something. Then continue reading from Section 6, above.)

Click Here To Return To Section 6

plzzz i need more sections

I’m glad you found it helpful but now that there is an official Math course provided by GameDev.TV, I don’t really see the need to update this.

Privacy & Terms