Discover a few util tricks on how to use vectors in Unity!
This article is also available on Medium.
When creating a game, be it in 2D or 3D, you’ll often need to communicate with and update the objects in your scene via scripting. In particular, you usually have to move them around, or compute spatial data based on their Transforms.
To do this, game engines like Unity provide you with linear algebra tools like vectors. More precisely, in Unity, the
Mathf package contains various classes depending on the type of data and the space dimension you’re working in:
Today, here are a few tips and tricks on how to use those vectors and solve common scenarios 🙂
Tip #1: Local or world space?
Mathematical spaces and basis are a complex and deep topic; but, in video games, you can focus on some crucial aspects of it and already solve quite a lot of situations.
For example, one of the first questions you’ll probably need to ask yourself when you start working with vectors is: am I working in local or world (= global) space?
Suppose you have a very simple scene with just a 3D cube that’s rotated a bit on its side, like this, and that has a little offset to the origin point:
You can see that this cube is 1 unit to the right and 1 unit forward, so its world position is
(x = 1, y = 0, z = 1). Also, the cube has a rotation of 45° around the Z-axis, so its world Euler angles is
(x = 0, y = 0, z = 45).
This is the info that you see by default in the Transform component of your game object, at the top of the Inspector window:
But of course, the local space of this object is different from the world space; this is something that you can see clearly by switching between the “local” and “global” mode in Unity’s toolbar:
You see here that the red, green and blue axis (respectively the X, Y and Z axis) are not pointing in the same direction in global and in local mode.
Let’s check this out in a C# script, too. Create a new class called
Debugger.cs and attach it to your cube. Then, in the
Start() method, debug the following:
When you use the
Vector3.X properties, you are in world space; when you use the
transform.X properties, you are in local space.
This is essential if you want to add some movement to the cube, for example an oscillation along its up axis.
If you take the world up axis (i.e. the world Y green direction), you’ll get a cube moving from top to bottom in world space, like this:
But if you take the local up axis (i.e. the transform Y green direction), the cube will move along a diagonal:
Of course, this is just a very basic overview of the world VS local coordinate systems – and if you dive into this, you’ll have to explore how hierarchy and children nesting impact these values… 😉
Tip #2: Getting a direction
One very common task when working with vectors in Unity is to get the direction between two points. This can be done very simply just by computing the difference between the target and the origin:
But we often like for directions to be normalised (meaning that their norm is 1). This way, the distance between the points has no impact, and it’s just about finding the proper “angle”.
Once again, Unity has us covered – we can use the
Normalize() method to modify the vector in place or compute the normalised version directly by using the
Tip #3: Rotating a vector by a given angle
Oftentimes, you’ll also need to apply some sort of rotation to this direction. For example, if you want to compute the cone of vision of an enemy, you might want to take the direction it’s facing and compute some lines on each side to determine this cone:
Again, this is pretty straight-forward – we can use Unity’s quaternions to compute the rotated vector! But because quaternions are always a pain to build properly, let’s use the Euler-based constructor.
Note: for more info on quaternions and Euler angles, check out this article I wrote a while ago for my RTS tutorial series where I discuss the difference between those two rotation systems 😉
This piece of code creates a “rotate by 30° around the world up axis” rotation variable. This rotation can then be applied to a vector to get the rotated version:
For our cone of vision example, we could easily get two diagonals around the forward direction of the enemy’s game object Transform like this:
Tip #4: Checking if vectors have similar directions
Another common question when working with vectors is whether or not they are facing the same direction. To check this, we can use the dot product – this number is a very useful math tool that:
- is positive when the vectors have similar directions
- is negative when the vectors are facing opposite directions
- and precisely: it is equal to 1 when vectors follow exactly the same axis and have the same orientation; it is equal to -1 when vectors have the same axis but opposite orientations; it is equal to 0 when vectors are orthogonal
So, suppose you want to check if the enemy we talked about before is facing you or not. You can compute the dot product of your forward direction (in white) and the forward direction of the enemy (in red):
You see that when the vectors are in similar directions, meaning that the enemy is not facing you, the dot product is positive and pretty close to 1; whereas when the vectors are in opposite directions, the dot product is negative and closer to -1.
It is important to remember, though, that here we are using the vectors as pure directions and not spatial positions. This means that you would get the exact same result (i.e. the same dot product) no matter where the vectors’ origin is.
Tip #5: Sorting vectors by length the optimised way
Something crucial whenever you start doing maths in a video game is that some operations are more costly than others. In particular, square roots are bad.
That’s why, when possible, you should try and avoid computing square roots. For example, when comparing vector norms.
The distance we usually use for computing vector lengths (the one Unity uses by default) is the Euclidean L2 norm, that looks like this (here shown in 3D):
And as you can see, it requires an ugly square root in the end…
Ok so – let’s say you want to find the point closest to you among a set of possibilities. So you have a list of
Vector3s that are each the vector between a candidate target point and you origin point, and you want to find the one with the smallest length.
Well, in truth, you don’t really care about the exact length of these vectors for your sorting process: all that matters is that squared distances are sorted the same way non-squared distances are (we say that the square function is an increasing function – it maintains the order of the inputs in the outputs).
This means that rather than sorting the vectors magnitudes (
.magnitude, d in the above picture), you can sort the squared magnitudes (
.sqrMagnitude, d2): you’ll get the same sorted list but it’s way more optimised because you don’t compute square roots! 🙂
Tip #6: Storing non-spatial data in vectors…
What if I told you that vectors can be used to store something else than positions in the world or on your tilemap?
Perhaps you’ve already hinted at this when you saw that Unity has
Vector4s, too. Clearly, those can’t be used in daily space-oriented scenarios. At best, the 4th component would represent time, if you were modelling a physical system, maybe?
But that’s a nice example to remind us of what vectors are, truly: just a set of (somewhat related) floats. It’s just like having an array of floats, except that Unity has some additional ice cream for it with all the vector methods and handy shortcuts, like the ones we talked about before.
This idea of using vectors as plain sequences of numbers is very much borrowed from the shader philosophy: in shaders, a
float array is just an ensemble of 4 floats. That’s it. And those can be directly related (like the 4 channels of a colour, counting the alpha for transparency), or just a bunch of things that you’d like to pass in at the same time (like an edge thickness you computed in your geometry shader, a normalised distance to the nearest point source, an angle between two face normals…).
The other day, for example, I used this technique as I was working on a basic 2D Pacman-like game. I need to get the 2D position of my hero but also its direction, so that I could pull up the right sprite animation and better show the user where it was going to be next cycle. Because I was working on a tiled grid with cell-by-cell movement, all of these were integers.
Granted, I could have had a
Vector2Int position on one side and a simple
int on the other. But where’s the fun in that? And also – it meant I’d have to take care of two variables whenever I passed the info through my codebase, which is more complex than having just one variable 🙂
Note: if you don’t know about
Vector3Ints types, those are really great Unity types that allow you to store vectors containing only integers, which is super-useful when you work on a discrete space… or when you’re storing non-spatial data with ints. It’s lighter than the floating-point equivalent because ints or smaller than floats 😉
So, instead, I decided to use a
Vector3Int and store the direction in the
z component, as an int going from 0 to 3. This way, whenever I passed this variable to a function, I actually had the entire “state” of my hero all at once.
Be careful, however, that as soon as you start “deviating” from the common usage of vectors, all the tools like norm, angles, cross vector, dot product and so on will become unusable (as-is) or, at the very least, give out very strange results!
Vectors are an essential element in the toolbox of any game developer because they are the common representation of spatial data: we use them to manipulate 2D or 3D coordinates, directions, distances and so on. We can even use them as plain sets of numbers and store more esoteric data in vectors (especially
Vector4s), just like we do in shaders.
Those were just some quick tips about vectors in Unity – I hope they’ll be useful to you! And of course don’t hesitate to leave a comment with ideas of other topics Unity/C# I could talk about 🙂