Detect if player is looking at object

I want to detect if a part is in the players general field of view. Ive tried raycasting but I have no idea how to use it and WorldToViewportPoint detect it through walls. Any help would be amazing

5 Likes

There are a lot of resources about this and is probably the best way if you don’t want it to happen through walls.

https://developer.roblox.com/en-us/api-reference/function/WorldRoot/Raycast

1 Like

thats the exact video I looked at XD I don’t understand how I can do that for the players head though

Good question, and you’ve got pretty much the exact right idea already. Use WorldToViewportPoint to first check if it’s on-screen at all, and then raycast from that position toward the object to see if it hits anything. If both checks pass, then the player can see the object.

Just note, there’s a caveat to this: It ​isn’t perfect, because it’s just point-checking. In other words, we’re just checking if one point of the object is visible. We would have to do a ton of raycasting to check if any part of the object is visible.

But this is roughly how it would be done:

-- LocalScript

local partToCheck = workspace.SomePart
local camera = workspace.CurrentCamera

-- Check if on screen:
local vector, inViewport = camera:WorldToViewportPoint(partToCheck.Position)
local onScreen = inViewport and vector.Z > 0

-- Raycast outward:
local ray = camera:ViewportPointToRay(vector.X, vector.Y, 0)
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
raycastParams.FilterDescendantsInstances = {partToCheck, game.Players.LocalPlayer.Character}
local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 1000, raycastParams)

-- Final check:
local isVisible = onScreen and not raycastResult
if isVisible then
   -- Do something
end
21 Likes

it came up with this error any idea what it means?
attempt to perform arithmetic (mul) on Ray and number

1 Like

Try it now. I used ray.Unit on accident. Should have been ray.Direction.

1 Like

now theres no error but it just doesn’t print

-- LocalScript

local partToCheck = workspace.SomePart

local camera = workspace.CurrentCamera

-- Check if on screen:

local vector, inViewport = camera:WorldToViewportPoint(partToCheck.Position)

local onScreen = inViewport and vector.Z > 0

-- Raycast outward:

local ray = camera:ViewportPointToRay(vector.X, vector.Y, 0)

local raycastParams = RaycastParams.new()

raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

raycastParams.FilterDescendantsInstances = {partToCheck, game.Players.LocalPlayer.Character}

local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 1000, raycastParams)

-- Final check:

local isVisible = onScreen and not raycastResult

while true do

wait()

if isVisible then

print("works")

end

end
1 Like

The isVisible variable won’t magically update itself. You need to call the remainder of the code every time. You could do this by wrapping it in a function:

local function IsVisible()
   -- The code I wrote before
   return isVisible
end

while true do
   task.wait()
   local isVisible = IsVisible()
   if isVisible then
      print("visible")
   else
      print("not visible")
   end
end
13 Likes

thanks a ton. This really helped me.

2 Likes

so this?

local function IsVisible()
	local partToCheck = workspace.SomePart
	local camera = workspace.CurrentCamera

	-- Check if on screen:
	local vector, inViewport = camera:WorldToViewportPoint(partToCheck.Position)
	local onScreen = inViewport and vector.Z > 0

	-- Raycast outward:
	local ray = camera:ViewportPointToRay(vector.X, vector.Y, 0)
	local raycastParams = RaycastParams.new()
	raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
	raycastParams.FilterDescendantsInstances = {partToCheck, game.Players.LocalPlayer.Character}
	local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 1000, raycastParams)

	-- Final check:
	local isVisible = onScreen and not raycastResult
	
	return isVisible
end

while true do
	task.wait()
	local isVisible = IsVisible()
	if isVisible then
		print("visible")
	else
		print("not visible")
	end
end
7 Likes

– I just found out this method creds to @fredfishy

player = game:GetService(“Players”).LocalPlayer
character = player.Character or player.CharacterAdded:wait()
torso = character:WaitForChild(“Humanoid”).RootPart
target = workspace:WaitForChild(“SomePart”) – The part to look at
vectors = {}
function dotProduct(a, b)
return a.x * b.x + a.y * b.y + a.z * b.z
end
function getAngle(a, b)
return math.acos(dotProduct(a, b) / (a.magnitude * b.magnitude))
end
function drawVector(position, angle, magnitude, name)
if vectors[name] then
vectors[name]:Destroy()
end
end

game:GetService(“RunService”).RenderStepped:Connect(function()
local torsoAngle = torso.CFrame.lookVector
local targetVector = torso.Position - target.Position
local flatTorsoAngle = Vector3.new(torsoAngle.x, 0, torsoAngle.z)
local flatTargetVector = Vector3.new(targetVector.x, 0, targetVector.z)
drawVector(torso.Position - Vector3.new(0, 2.5, 0), flatTorsoAngle, flatTargetVector.magnitude, “torso”)
drawVector(target.Position, flatTargetVector.unit, flatTargetVector.magnitude, “target”)
local angle = math.deg(getAngle(-flatTorsoAngle, flatTargetVector))
if angle > 70 then
target.BrickColor = BrickColor.new(“Bright red”) – Player is NOT looking at Part
else
target.BrickColor = BrickColor.new(“Bright green”) – Player is looking at Part
end
end)

1 Like

hi, i know it is a bit old, but how to add distance to it? like how close you must be to make it work

1 Like

local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 1000, raycastParams)

the “* 1000” is the distance in studs

3 Likes

It didn’t work for me, there was nothing blocking the way

The Block should be moving upwards
RobloxStudioBeta_KtIn33aONu

The Code:

-- LocalScript

local partToCheck = workspace.Backrooms.Level00.Structure.Creature
local tweenservice = game:GetService("TweenService")
local TweenA = tweenservice:Create(partToCheck,TweenInfo.new(0.5), {Position = Vector3.new(125.975, 152.35, -242.8)})


local function IsVisible()

	local camera = workspace.CurrentCamera

	-- Check if on screen:
	local vector, inViewport = camera:WorldToViewportPoint(partToCheck.Position)
	local onScreen = inViewport and vector.Z > 0

	-- Raycast outward:
	local ray = camera:ViewportPointToRay(vector.X, vector.Y, 0)
	local raycastParams = RaycastParams.new()
	raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
	raycastParams.FilterDescendantsInstances = {partToCheck, game.Players.LocalPlayer.Character}
	local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 2048, raycastParams)

	-- Final check:
	local isVisible = onScreen and not raycastResult
	if isVisible then
		wait(0.5)
		partToCheck.ParticleEmitter.Enabled = false
		TweenA:Play()
		wait(10)
	else
		partToCheck.Position = Vector3.new(125.975, 130.85, -242.8)
		partToCheck.ParticleEmitter.Enabled = true
	end
	return isVisible
end




while true do
	wait(0.1)
	local isVisible = IsVisible()
	if isVisible then
		print("visible on screen")
	else
		print("not visible on screen")
	end
end
4 Likes

I think you need to but the tweening thing not in final check and in the while true function

It’s because the raycast is picking up objects behind the part you’re checking for. Which Ultimately causes the script to think there’s something infront of the object you’re looking for

Here’s a modification I made, it checks based on the distance between you & the part you’re looking for.
in the form of a module script, you can change as you wish

Two Types of checks are “Screen” & “PlainSight”, pretty self explanitory
Screen: The Object is within your screen view, but you don’t have to be able to see it directly
PlainSight: The Object has to be in your screen view with nothing blocking it (perfect for what you’re trying to accomplish)

function module.ItemInView(plr, camera, checksubject, type_) --type can be “Screen” “PlainSight”
local partToCheck = checksubject
if game.Workspace:FindFirstChild(“”…plr.Name) then
local char=game.Workspace:FindFirstChild(“”…plr.Name)

	if char:FindFirstChild("HumanoidRootPart") and char:FindFirstChild("Humanoid") then
		-- Check if on screen:
		local rootPart = char:FindFirstChild("HumanoidRootPart")
		local humanid = char:FindFirstChild("Humanoid")

		local vector, inViewport = camera:WorldToViewportPoint(partToCheck.Position)
		local onScreen = inViewport and vector.Z > 0

		-- Raycast outward:
		local ray = camera:ViewportPointToRay(vector.X, vector.Y, 0)
		local raycastParams = RaycastParams.new()
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
		raycastParams.FilterDescendantsInstances = {partToCheck, char}


		--Big Difference


		--stretch is the distance between the player & the part to check
		local stretch = (partToCheck.Position - rootPart.Position).Magnitude

		local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * stretch, raycastParams)

		-- Final check:
		local isVisible
		if type_ == "Screen" then
			isVisible = onScreen
		elseif type_ == "PlainSight" then
			isVisible = onScreen and not raycastResult
		end


		--Big Difference



		if isVisible then
			-- Do something
			local mag_distance = (partToCheck.Position - rootPart.Position).Magnitude
			print("In Vision is true")
			return {"true", mag_distance}
		else
			local mag_distance = (partToCheck.Position - rootPart.Position).Magnitude
			print("In Vision is false")
			return {"false", mag_distance}
		end
		--end the operation here
	else
		
		return {"false"}
		
	end
	
else
	return {"false"}
end

end

2 Likes

The proper way to execute this, if you were to place it into a module script

local inview = backendmodule.ItemInView(game.Players.LocalPlayer, workspace.CurrentCamera, workspace:FindFirstChild(“Part”), “PlainSight”)

if inview[1] == “true” then
–can be seen
elseif inview[1] == “false” then
–can’t be seen
end

1 Like