Requesting Help - calculating trigonometry

Howdy devs!

Today I come to you seeking advice. I’ve tried my hand at this but I just can’t figure it out after spending quite a few hours on it.

THE PROBLEM:
There is an object “red X” which is at the end of a raycast from the camera to the mouse position. I collect this raycast by using the following variables:
start position = camera CFrame
end position = camera:ViewportPointToRay(mouseX, mouseY).Direction * 1000
I need to obtain the CFrame so the blue X will never be more than 50 studs from the player’s head CFrame. If the impact position of the camera->mouse ray is closer to the player’s head than 50 studs, then the blue X will be at the impact position (image 1 below). However if the impact point along the camera->mouse ray is further away from the player’s head than 50 studs, then I need to find the 3D point in space along that ray which is within that 50 stud limit (image 2 below). Does anyone have any suggestions on how to accomplish this?

Any and all feedback here is appreciated!

1 Like

Why position the part along the ray?

Why not just raycast from the head to mouse position and limit the length by 50 studs?

``````character.Head.Position + (mouse.Hit.Position - character.Head.Position).Unit * 50
``````

The system that is being described sounds simar to this one below.

I might very well be wrong, but I don’t think you can model an equation for that as there’s an infinite amount of combinations. Interpreting what you mean I think you are trying to get the one the farthest back but also close to the thing. You could apply a proportional closed loop system then just check if it is within some tolerance.

``````if distanceFromPlayerToX > 50 then
bias = sign(player-x:Dot(-direction))
out = 1
err = 50 - o+d*(bias*1.1)
while true {
pos = o+d*out
err = 50 - (player-pos).Magnitude
out = 0.1*err
if abs(err) < 1 {
print(o+d*out)
break
}
}
end
``````
1 Like

I don’t think it’s infinite, there’s a sphere around the head with radius 50, and a line in space elsewhere

The line intersects the sphere at 0 to 2 points, one of which is the distance-limited point op wants

I guess the problem is that there may not be any solution in general.

@ChadTheCreator can you describe what you actually want to use this for? It would help come up with a better answer

1 Like

Thanks for the response! You’re correct, there are 0 or 2 intersection points in what I’m trying to do.

The player has an attack ability with a range of 50. I want to keep that range around the player’s head so they can’t angle their camera to use that ability further away from themselves than 50 studs. But I also want the ability to be where their mouse is (camera->mouse ray). So ideally the ability would happen along that camera->mouse ray, as close to the hit point as possible, but no further from the character’s head than 50 studs.

And ideally, if there happens to be 0 intersection points, it would just put the ability at 50 studs along a ray from your head to our mouse click. I can probably do that one relatively easily - the hard part is I can’t figure out the formula for the top-left square. Examples below:

Are you sure you don’t want the ray to just go from their head to the mouse in the first place? With this system they could angle their camera to shoot on the other side of a wall, even if there’s no line of sight between them and their mouse.

I’m fine with players being able to use this ability in that way (looking around corners, as long as the target is within 50 studs of the player).

P1 = camera position
P2 = mouse position

Just instead of `+/- sqrt` you’d use only the `+` (or maybe just the `-`?) so you get the second point.

That’s after checking for 0 intersections special case of course.

Edit: I guess there’s a few more special cases, like if you have 1 intersection you need to figure out if you’re inside or outside the sphere to start, because in the former case you should use the intersection point but in the latter you should use the ray hit pos, etc.

I’ll try to mess around with this, though following the formula has yielded some weird results so far.

That image reminded me of some formula I saw. Using the ray trace equation for a sphere and just only looking for the greatest T value gives it. A Minimal Ray-Tracer: Rendering Simple Shapes (Sphere, Cube, Disk, Plane, etc.) (Ray-Sphere Intersection)

or actually, it would either have to be greatest T or smallest T depending on the angle of the player in relation to the the ray

This seemed to work with me. The first function returns the two parameters, where one or more can be nil.

The second one just returns the further intersection point, or nil.

``````local function SphereLineIntersectionsU(sphereCenter: Vector3, sphereRadius: number, lineP1: Vector3, lineP2: Vector3)
local x1, y1, z1 = lineP1.X, lineP1.Y, lineP1.Z
local x2, y2, z2 = lineP2.X, lineP2.Y, lineP2.Z
local x3, y3, z3 = sphereCenter.X, sphereCenter.Y, sphereCenter.Z

local a = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1)
local b = 2*((x2-x1)*(x1-x3) + (y2-y1)*(y1-y3) + (z2-z1)*(z1-z3))
local c = x3*x3 + y3*y3 + z3*z3 + x1*x1 + y1*y1 + z1*z1 - 2*(x3*x1 + y3*y1 + z3*z1) - sphereRadius*sphereRadius

local radicand = b*b - 4*a*c

if radicand < 0 then return nil, nil end

if radicand < 0.1 then return -b / (2*a) end -- doesn't really happen in practice

end

local function GetFurthestSphereIntersection(sphereCenter: Vector3, sphereRadius: number, lineP1: Vector3, lineP2: Vector3)
local u1, u2 = SphereLineIntersectionsU(sphereCenter, sphereRadius, lineP1, lineP2)
if u1 then return lineP1 + (lineP2-lineP1)*u1 end
return nil
end
``````
1 Like

I think it’d need to do this

``````local function SphereLineIntersectionsU(sphereCenter: Vector3, sphereRadius: number, lineP1: Vector3, lineP2: Vector3)
local x1, y1, z1 = lineP1.X, lineP1.Y, lineP1.Z
local x2, y2, z2 = lineP2.X, lineP2.Y, lineP2.Z
local x3, y3, z3 = sphereCenter.X, sphereCenter.Y, sphereCenter.Z

local a = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1)
local b = 2*((x2-x1)*(x1-x3) + (y2-y1)*(y1-y3) + (z2-z1)*(z1-z3))
local c = x3*x3 + y3*y3 + z3*z3 + x1*x1 + y1*y1 + z1*z1 - 2*(x3*x1 + y3*y1 + z3*z1) - sphereRadius*sphereRadius

local radicand = b*b - 4*a*c

if radicand < 0 then return nil, nil end

if radicand < 0.1 then return -b / (2*a) end -- doesn't really happen in practice

end

local bias = sign(xToPlayer:Dot(-dir)

local u1, u2 = SphereLineIntersectionsU(sphereCenter, sphereRadius, lineP1, lineP2)
if u1 > u2 then
greatest = u1
least = u2
else
greatest = u2
least = u1
end

if bias > 0 then
return camera.CFrame.Position + dir*greatest
else
return camera.CFrame.Position + dir*least
end
``````

I think he wants the dot to be closest to the wall is why

1 Like

Maybe? I’m not quite sure what you mean by `xToPlayer`, and you don’t ever use `greatest/least` from what I see

In any case `u1` is always the larger value though (closer to P2) because it’s got the `+` instead of `-`. In my testing it works fine:

1 Like

Yes, relative to the ray in which it cast. You need to do a check on your scalars so it is the closest to what you want relatively. In this case, the wall

@CoderHusk @nicemike40 Thanks for the feedback yall. I’m not sure what I did wrong in my way of plugging in that formula, guess I missed somethin

I do have some questions though, I just want to be sure about what the following variables mean.

What is sign()? What are these variables: “dir”, “lineP1”, “lineP2”, “xToPlayer”?

sign is math.sign, dir is the ray direction from the camera to the point in the world, lineP1 is camera.CFrame.Position, lineP2 is lineP1+dir, xToPlayer is player.Position-x.Position. You probably need to sort the bias not by the angle but rather the closest distance to the x point now that I think of it

It finally works! Thank you so much for taking the time to help me with this bit of trig. The result: