Efficient way of getting entities in the player's FOV?

I understand that you could loop through every entity using ents.GetAll and test it against ply:Visible(ent), but that doesn’t sound like the most efficient way. Is there a function I can use that already does this, or is a loop the best way?

[editline]8th July 2014[/editline]

Hmm, seems that ply:Visible(ent) only checks if the entity has a direct line of sight to the player, and not if the player is looking at it.

You can play with Entity:GetPos():ToScreen() and see if the returned values are lower than 0 and higher than both ScrW() and ScrH().

Thanks! Looks like it’s better to trust the X and Y values than the “visible” field returned by ToScreen, as it seems to be true when it shouldn’t be;


	for _, v in pairs(player.GetAll()) do
		if not (v == ply) then
			local screen_pos = v:GetPos():ToScreen()

			print(not (screen_pos.x > ScrW() or screen_pos.y > ScrH()), screen_pos.visible)
		end
	end

Keep in mind that #GetPos() will only check the entity’s origin.
#Visible() checks if the entity is visible at all.

ToScreen isn’t really efficient though

Visible check if the entity it’s on the PVS, not on the FOV

[LUA]
local ratio = 0.95

local Player = FindMetaTable( “Player” )
function Player:IsLookingAt( vec )
return self:GetAimVector():DotProduct( ( vec - self:EyePos() ):Normalize() ) < ratio
end
[/LUA]

you could play with the ratio variable until it seems to only work when the ent is on your screen.

In the past I’ve used ents.FindInSphere as it’s much faster than going through ALL entities along with ToScreen.

Not sure if you’re only looking to find players in a sphere around a player, or all entites.

However, if you are only looking for players in a sphere, here’s a function that I posted a while back that’s much more efficient than ents.FindInSphere if you only want players
[LUA]
local Vect = FindMetaTable( “Vector” )
local Ent = FindMetaTable( “Entity” )
local GetAll = player.GetAll

function Vect:PlayersInSphere( radius )

local plys = {}
local radius = radius * radius
local idx = 1

local DistToSqr = Vect.DistToSqr
local GetPos = Ent.GetPos

for _, ply in ipairs( GetAll() ) do
	
	if DistToSqr( self, GetPos( ply ) ) &lt;= radius then

		plys[idx] = ply
		idx = idx + 1

	end

end

return plys

end
[/LUA]

Just posting to make this more accurate.

[lua]local cos = math.cos
local Player = FindMetaTable(“Player”)

function Player:GetDefaultFOV()
if self:IsBot() then
return 90 – Bots are bots, who cares
end
return tonumber( self:GetInfo( “fov_desired” ) )
end

function Player:IsInFOV( pos )
local fov = cos( self:GetFOV() / self:GetDefaultFOV() )
local dir = ( pos - self:EyePos() ):GetNormal()

return self:GetAimVector():Dot( dir ) &lt; fov

end[/lua]