7
\$\begingroup\$

I'm working my way through this book and I'm trying to work this code out:

var squaredVelocity = this.velocity.x*this.velocity.x
                    + this.velocity.z*this.velocity.z;

if (squaredVelocity > Player.SPEED*Player.SPEED) {
   var scalar = Player.SPEED / Math.sqrt(squaredVelocity);
   this.velocity.x *= scalar;
   this.velocity.z *= scalar;
}

The book doesn't explain it. I've been staring at it for an hour. I can see exactly what it does (in the technical sense), but what's its purpose?

I understand bits: The scalar variable is always a fraction, so multiplying the velocity by it scales back that velocity.

Is this code for limiting speed? Then why are the x and y velocities added together? Shouldn't the X and Y be scaled independently?


Do I need to know this stuff for a career in video games? I understand that all this math is educational, but will I still have to do this sort of thing in an engine like Physijs?

\$\endgroup\$
1
  • \$\begingroup\$ Absolutely not. A lot of game engines will hide this from you \$\endgroup\$ Commented May 24, 2014 at 0:52

2 Answers 2

18
\$\begingroup\$

The reason for this is Pythagorean Theorem, and it's probably the bit of math I use most often in games. Even when working with a full-featured engine, there are times when knowing this math has helped me get the gameplay and look I wanted.

Don't worry though, it's very simple once you've used it a few times. :)

What this bit of code does is ensure that the player's velocity (this.velocity) doesn't exceed a defined maximum speed (Player.SPEED)

To do this, it's not enough to check the x and z components separately. If both x and z were equal to Player.SPEED, then looking at each component it would appear that we're within our speed limit. But something that's moving at Player.SPEED forward AND right must be going faster than Player.SPEED in total. We need a way to calculate the combined effect in these two axes.

If we picture the velocity as an arrow in space (pointing from where the object is now to where it will be in one unit of time), the speed per unit of time is the length of this arrow.

When you break the arrow into its x and z components, you find that they make a triangle - a right angled triangle, since the x and z axes are perpendicular to each other. That means we can use Pythagorean Theorem to find the length of the diagonal, the hypotenuse of the triangle.

enter image description here \$\begin{align} {total\,speed}^2 &= velocity.x^2 + velocity.z^2 \\ {total\,speed} &= \sqrt{velocity.x^2 + velocity.z^2} \end{align}\$

That's this line:

var squaredVelocity = this.velocity.x*this.velocity.x + this.velocity.z*this.velocity.z;

Why should this math work? My favourite proof is just this image:

enter image description here

$$\LARGE{a^2+b^2=c^2}$$

The big squares to the left & right have the same side length \$(a + b)\$, so they must have equal area. The purple areas inside are all composed of the same four right triangles, with identical side lengths a & b, so the purple parts of the left & right squares must also have equal area. That means what's left must also be equal: the white area on the left \$(a^2 + b^2)\$ must equal the white area on the right (\$c^2\$, the square whose side length is the hypotenuse of the purple triangles).

That leaves one detail: why is this code written with so many squares, instead of just taking the square root in the first line?

That's because of the way computer processors work. They can add and multiply numbers with blinding speed, but division and square roots take significantly longer. On today's hardware you'd only notice the difference if you were doing many millions of computations, but in the old days it was a big deal, and it can still add up in performance-critical code like physics & rendering systems, which crunch gobs of numbers every frame.

So gamedevs have developed a bunch of tricks to avoid these costlier operations, or at least hold off on them until they're absolutely necessary. One is working with squares:

if (squaredVelocity > Player.SPEED*Player.SPEED)

Comparing the square of the velocity vector's length vs the square of the max speed will always give the same result as comparing the unsquared versions (since neither one is allowed to be negative by definition). If the velocity is below the max speed then we can safely skip ahead, never having to compute the square root.

If the velocity is too great, then we need to scale the vector down. Dividing each component by its current length would uniformly scale it down to a length of one (called a "unit vector"). Multiplying a unit vector by the Player.SPEED then scales it up uniformly to have a length equal to Player.SPEED. This code combines these two scale operations into one scale factor, and applies it to each component:

var scalar = Player.SPEED / Math.sqrt(squaredVelocity);
this.velocity.x *= scalar;
this.velocity.z *= scalar;

At the end of this code, we can be sure that this.velocity has length less than or equal to Player.SPEED.

One last note: Unless you're writing engine code, you can probably get away with using square roots wherever you want. Looking at other devs' code, you'll find we're often using these tricks out of habit, even if it's a script that only runs once per frame, or where we're memory- not cpu-bound - and it looks like that might be the case in this example. Pythagorean Theorem is definitely worth knowing, but the particular form of this example isn't something you need to duplicate exactly. Just write code that's clear to you.

\$\endgroup\$
7
  • 1
    \$\begingroup\$ Love the diagram to "prove" Pythagoras' theorem. Had not seen that version before but I agree it's very neat. \$\endgroup\$
    – Floris
    Commented May 24, 2014 at 4:52
  • 1
    \$\begingroup\$ @Floris Why do you put "prove" in quotation marks? \$\endgroup\$
    – Matthias
    Commented May 24, 2014 at 12:42
  • 1
    \$\begingroup\$ @Matthias - I suppose I did it because I am used to a proof looking more formal - buy this definitely works for me. Don't read too much into it. \$\endgroup\$
    – Floris
    Commented May 24, 2014 at 13:02
  • 1
    \$\begingroup\$ @DMGregory Thanks very much Gregory, I get everything! (Even Pythagoras for the first time!) Bad variable names (e.g. I think SPEED should be MAXVELOCITY) and literally no explanation make this book pretty impenetrable for beginners. Maybe you could write one if you're getting into WebGL because you're very good at explaining! Also, could you add to your answer about how knowing this helped get you the 'gameplay' you wanted? Because I'm told using an engine shields you from this, and yet you say it's somewhat necessary? Do you mean you used this knowledge to tweak the engine itself? \$\endgroup\$
    – Starkers
    Commented May 25, 2014 at 14:56
  • \$\begingroup\$ Aww, thank you! That means a lot to me! The answer's already a bit long so I'll just add here: knowing this math helps me work out what I need before I sit down to code, and know what to look for in the engine/libraries I use. Sure, the code I actually write usually says something like Vector3.Distance(a, b) or (a - b).magnitude and doesn't show the componentwise steps, but the engine won't tell me how to use those functions to do stuff like this: gamedev.stackexchange.com/questions/75262/… \$\endgroup\$
    – DMGregory
    Commented May 25, 2014 at 15:54
3
\$\begingroup\$

The length of a vector x, y is sqrt(x^2+y^2).

That's basically the gist of that whole snippet. If you properly define vector operations and a "length" method for them it would look like this:

// Set the velocity of the object to the maximum if it is above the maximum
if (velocity.length() > Player.SPEED)
  velocity = velocity  / velocity.length() * Player.SPEED;

Dividing a vector by it's length is called "normalization". Normalizing a vector gives you a so called "unit" or "direction" vector that points into the same direction but whose length is exactly 1.

So for example 17, 0 normalized is 1, 0. And 0, 5.32 normalized would be 0, 1. And 4, 4 normalized would be 1/sqrt(2), 1/sqrt(2) e.g. 0.703, 0.703. Since the length is 1 you can just multiply it with any scalar to get a vector of that length.

So even more simplified it would be:

if (velocity.length() > Player.SPEED)
  velocity = velocity.normalized() * Player.SPEED;
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .