This is a fun vector problem! Feel free to skip to the end if you only want the code.
Step 1: The problem
Imagine that you are the blue point and an enemy is the green point. Your gun shoots in the direction of the blue vector a
, and the enemy is in the direction of the green vector b
.
The shortest distance from the enemy to the bullet is c
, which is perpendicular to the gunshot. Hence, we have a right triangle.
Step 2: The setup
Let’s call the angle between the two vectors θ
.
Let a
be the third side of the triangle (I know the blue vector is longer, but I’m not redoing the graphic). If you’re familiar with trigonometry, we know that sin(θ) = opposite / hypotenuse
, or in this case, sin(θ) = c / b
. So if we find θ
and b
, we can find c
.
Step 3: Solve for b
This is rather simple. The length of vector b
is its magnitude. We write this as |b|
.
Step 4: Solve for θ
There is a special operation we can perform on two vectors called the dot product. I’ll let EgoMoose explain it here.
We can calculate the dot product of vectors a
and b
as follows:
a = (a1, a2)
b = (b1, b2)
a • b = a1*b1 + a2*b2
What makes the dot product super special is that it can actually be defined in two ways.
a • b = |a||b|cos(θ)
Setting them equal to eachother, we can solve for θ
.
a1*b1 + a2*b2 = |a||b|cos(θ)
⇒ θ = acos((a1*b1 + a2*b2) / |a||b|)
Step 5: Putting it all together
We’ve solved for b
and θ
. Rearranging the sine formula, we get c = sin(θ) * b
. Now we can plug in b
and θ
and solve.
c = sin(acos((a1*b1 + a2*b2) / |a||b|)) * |b|
Step 6: A loose end
We have one final loose end to deal with. If the gun shoots away from the enemy, we can throw out all of our math. The enemy’s distance to the bullet simply becomes their distance to the gun.
We know this occurs when the angle is greater than 90 degrees. This also means the bullet doesn’t whiz by the enemy’s ear at all, so do with that what you will.
-- @param origin Vector3 The position of the gun
-- @param a Vector3 The vector from the gun to the enemy
-- @param b Vector3 The vector of the bullet
local function DistToBullet(origin, a, b)
local theta = math.acos(math.clamp(a:Dot(b) / (a.Magnitude * b.Magnitude), -1, 1))
local hypotenuse = a.Magnitude
if theta > math.pi / 2 then
return hypotenuse
else
return math.sin(theta) * hypotenuse
end
end
EDIT: My solution is trig-based, whereas sircfenner’s uses simpler vector math. I suggest you use his because it also has early exits and runs faster.