Saturday, September 7, 2019

mathematics - Why isn't this physics code scaling velocities separately?


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?



Answer




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.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...