Detecting if point can see an object

Hello! I’m trying to implement a system for detecting if a dummy can see a player. Originally I though of using raycasts but came to the realisation that to detect if a point given a CFrame and FOV would see a slight part of the player, that I’d have to use many many raycasts to get it right.

My question is, is there a way to detect if a point with a CFrame and FOV can detect if the part can be seen. even if its slightly obscured

Use the Dot product combined with raycasts. If the raycast hits the part and the dot product is within given range the position can be seen

No? If there is a wall between them and the dotProduct gets within a valid range the result will be that the NPC can “See” the player even tho in that case the wall should completley block all vision

Raycast at the Center of Mass, if doesn’t see the player, try the edge of the player’s bounding box (locations shown as red dots).

  • Minimum of 1 raycast, and maximum of 9 raycast needed.
  • This along with the Dot method should work quite well for most games.
  • May cause false positive (see second image)

Of course, if you really want to be precise, you can raycast the corner of each body part instead but this will increase the raycast maxumum by a lot so I don’t recommend unless it’s really important.

False positive edge case. The program will see the player when it shouldn’t.

Edit: I realized you specified parts, for normal blocks, this method works very well. For balls, mesh, and or cylinders, you may get the false positive edge cases.

2 Likes

Maybe instead of doing a “box” there I raycast to each and every corner maybe I can raycast at some important corners that shape the player. In the worst case scenario that would mean 48 raycasts at max, but given the structure of the Roblox character I could reduce that to be 28 raycasts!

Maybe that’s a dumb idea, it would almost eliminate false positive signs! I’ll get to working on making the system, thanks for the idea :wink:

1 Like

And that worked really well! Thank you so much for the idea, I really appreciate it!

In case anyone is curios on how I coded this, here’s the answer

local LimbSize = {
	Head = Character:WaitForChild("Head").Size*.5,
	LowerTorso = Character:WaitForChild("LowerTorso").Size*.5,
	UpperTorso = Character:WaitForChild("UpperTorso").Size*.5,

	RightUpperArm = Character:WaitForChild("RightUpperArm").Size*.5,
	RightHand = Character:WaitForChild("RightHand").Size*.5,
	LeftUpperArm = Character:WaitForChild("LeftUpperArm").Size*.5,
	LeftHand = Character:WaitForChild("LeftHand").Size*.5,
			
	RightUpperLeg = Character:WaitForChild("RightUpperLeg").Size*.5,
	RightFoot = Character:WaitForChild("RightFoot").Size*.5,
	LeftUpperLeg = Character:WaitForChild("LeftUpperLeg").Size*.5,
	LeftFoot = Character:WaitForChild("LeftFoot").Size*.5
}
		
local OffsetCFrames = { -- Offset from some body part
	-- Base Check
			
	Character:WaitForChild("UpperTorso").CFrame,
			
	-- Torso
			
	Character:WaitForChild("UpperTorso").CFrame * CFrame.new(LimbSize.UpperTorso),
	Character:WaitForChild("UpperTorso").CFrame * CFrame.new(LimbSize.UpperTorso * Vector3.new(-1,1,1)),
	Character:WaitForChild("UpperTorso").CFrame * CFrame.new(LimbSize.UpperTorso * Vector3.new(-1,1,-1)),
	Character:WaitForChild("UpperTorso").CFrame * CFrame.new(LimbSize.UpperTorso * Vector3.new(1,1,-1)),
	Character:WaitForChild("LowerTorso").CFrame * CFrame.new(LimbSize.LowerTorso * Vector3.new(1,-1,1)),
	Character:WaitForChild("LowerTorso").CFrame * CFrame.new(-LimbSize.LowerTorso * Vector3.new(-1,-1,1)),
	Character:WaitForChild("LowerTorso").CFrame * CFrame.new(-LimbSize.LowerTorso),
	Character:WaitForChild("LowerTorso").CFrame * CFrame.new(LimbSize.LowerTorso * Vector3.new(1,-1,-1)),

	-- Head
			
	Character:WaitForChild("Head").CFrame * CFrame.new(LimbSize.Head),
	Character:WaitForChild("Head").CFrame * CFrame.new(LimbSize.Head * Vector3.new(-1,1,1)),
	Character:WaitForChild("Head").CFrame * CFrame.new(LimbSize.Head * Vector3.new(-1,1,-1)),
	Character:WaitForChild("Head").CFrame * CFrame.new(LimbSize.Head * Vector3.new(1,1,-1)),
			
	-- RightArm
			
	Character:WaitForChild("RightUpperArm").CFrame * CFrame.new(LimbSize.RightUpperArm),
	Character:WaitForChild("RightUpperArm").CFrame * CFrame.new(LimbSize.RightUpperArm * Vector3.new(1,1,-1)),
	Character:WaitForChild("RightHand").CFrame * CFrame.new(LimbSize.RightHand * Vector3.new(1,-1,1)),
	Character:WaitForChild("RightHand").CFrame * CFrame.new(LimbSize.RightHand * Vector3.new(1,-1,-1)),
			
	-- LeftArm
			
	Character:WaitForChild("LeftUpperArm").CFrame * CFrame.new(LimbSize.LeftUpperArm * Vector3.new(-1,1,1)),
	Character:WaitForChild("LeftUpperArm").CFrame * CFrame.new(LimbSize.LeftUpperArm * Vector3.new(-1,1,-1)),
	Character:WaitForChild("LeftHand").CFrame * CFrame.new(LimbSize.LeftHand * Vector3.new(-1,-1,1)),
	Character:WaitForChild("LeftHand").CFrame * CFrame.new(-LimbSize.LeftHand),
			
	Character:WaitForChild("RightFoot").CFrame * CFrame.new(LimbSize.RightFoot * Vector3.new(1,-1,1)),
	Character:WaitForChild("RightFoot").CFrame * CFrame.new(LimbSize.RightFoot * Vector3.new(1,-1,-1)),
	Character:WaitForChild("LeftFoot").CFrame * CFrame.new(LimbSize.LeftFoot * Vector3.new(-1,-1,1)),
	Character:WaitForChild("LeftFoot").CFrame * CFrame.new(-LimbSize.LeftFoot)
}
		
local RayParams = RaycastParams.new()
RayParams.FilterType = Enum.RaycastFilterType.Exclude
RayParams.IgnoreWater = true
RayParams.FilterDescendantsInstances = table.pack(table.unpack(Character:GetChildren()), table.unpack(Character:GetDescendants()))
		
local ray
		
for i = 1, #OffsetCFrames do -- testing if any raycasts work
	ray = game.Workspace:Raycast(HeadCFrame.Position, (OffsetCFrames[i].Position - HeadCFrame.Position), RayParams)
			
	if not ray then
		break -- We can break early from the loop
	end
end
		
if not ray then
	print("Player Seen")
end
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.