Best way to get closest Vector3 player's camera is looking at?

I have a list of Vector3 points.
How would I get the closest point that the player’s camera is looking at?

Kinda like what’s going on in this game:

Notice the nametags for the training dummys

This is a way I would try implementing it
image
Loop through every point once and make sure the point is visible on the camera using WorldToViewportPoint

Then draw a series of points right in front of the camera. For every new point, loop through every NPC position to find the closest.

this is a lot of looping tho, and it has to happen very often

Is there a more efficient/better way to do this?

2 Likes

You should have some way of getting every point that is in range without having to loop through every point in the game. Look into quadtrees for a really nice way of doing that.

You then filter out every point that is off-screen.

You then sort the remaining points based on a scoring function. The scoring function should take into account distance from the camera and the angle to the camera’s look direction. You can tweak how important each of these are to get the right feel.


function vectorAngle( v1, v2 )
	return math.acos( v1.Unit:Dot(v2.Unit) )
end

function vectorDistance( v1, v2 )
	return (v2 - v1).Magnitude
end

function targetScore( targetPoint, cameraPos, cameraLookVector )
	local distance = vectorDistance(targetPoint, cameraPos)
	local angle = vectorAngle(cameraLookVector, (cameraPos - targetPoint))
	return distance * 10 + math.deg(angle) * 2
end

function sortTargets( targets, cameraPos, cameraLookVector )
	table.sort(targets, function( a, b )
		return 
			targetScore( a, cameraPos, cameraLookVector ) < 
			targetScore( b, cameraPos, cameraLookVector )
	end)
end

function isOnScreen( worldPoint )
	local _, onScreen = camera:WorldToScreenPoint(worldPoint)
	return onScreen
end

function getPotentialTargets( camera )
	-- you figure this one out. Or just return every target in the game to test it.
end

function getBestTarget( camera )
	local ts = {}
	for _, t in pairs(getPotentialTargets(camera)) do
		if isOnScreen(t) and vectorDistance(camera.CFrame.p) <= MAX_TARGET_DISTANCE then
			table.insert(r, ts)
		end
	end

	sortTargets(ts)

	return ts[1]
end
4 Likes

That’s smart!
Using angles instead of position is way more efficient.

Is it worth doing a quad tree if the points are dynamically moving around though?

Just using angles will significantly improve performance. I might skip on using quadtrees because I don’t completely see how that would work.

Thank you

Computing angles vs distances isn’t really that much faster. I just think it might make the system work more intuitively to have both distance and angle have an influence, so e.g. a really close enemy at the edge of the screen doesn’t get targeted if there’s a sorta close enemy in the center of the screen.

The issue is having to do it for every single thing that can be targeted, which might be in the 100s to 1000s.

Keeping the quadtree up-to-date is obviously more complex when enemies are moving around, but it can be done and it should still give some performance gain. Don’t optimize prematurely tho

1 Like