Detect if player looks at part

Hi, I am trying to create a script that checks if a player is looking at a part. How would I go about doing this? The camera is always in 1st person if that information matters.

6 Likes

cast a ray from the cameras CFrame and see if it returns any parts

5 Likes

Okay I will do that, thank you

How would I implement this? I tried doing this but it just doesn’t work

Do you want to know if a player is directly looking at a part or if it is simply on the screen? If you want the former, raycasting is probably your best bet. You can roughly implement it like this:

local part = workspace:WaitForChild'a part'; --a part to determine whether the player is looking at
local cam = workspace.CurrentCamera;
local length = 100; --a max distance in which the ray can detect objects. we multiply the camera's look vector by this number
local cfg = RaycastParams.new();
cfg.FilterDescendantsInstances = {game.Players.LocalPlayer.Character}; --specify a table of anything that the raycast should ignore (you always stare at your character, so i highly recommend you keep the player's character in here)
	
local cast = workspace:Raycast(cam.CFrame.Position, cam.CFrame.LookVector * length, cfg); --start a ray at the camera's position and extrude it in its look direction (cframe allows you to determine both position and orientation of the camera) with our specified ray parameters
	
if cast and cast.Instance == part then --make sure the ray actually hit something and make sure its the part we want to know about
	--do stuff
end

Otherwise, detecting whether an object is on the screen is even simpler:

local part = workspace:WaitForChild'whatever';
local cam = workspace.CurrentCamera;

local isOnScreen = select(2, cam:WorldToViewportPoint(part.Position)); --get the position of the object in question on the player's screen. this also tells you whether the passed in vector is actually on the player's visible viewport or not, which is the only thing we need (hence select)
24 Likes
local plr = game.Players.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local primary = char.PrimaryPart or char:WaitForChild("HumanoidRootPart")

local cam = workspace.CurrentCamera

local function OnScreen(object)
	local IsObsufcated = #cam:GetPartsObscuringTarget({object.Position},{object,char}) == 0
	local _, OnScreen = cam:WorldToScreenPoint(object.Position)
	return OnScreen and IsObsufcated 
end

OnScreen(primary) -- should print true

made a more accurate version

local function OnScreen(object : Part)
	local Positions = {
		(object.CFrame * CFrame.new(object.Size.X/2,object.Size.Y/2,object.Size.Z/2)).Position,
		(object.CFrame * CFrame.new(-object.Size.X/2,object.Size.Y/2,object.Size.Z/2)).Position,
		(object.CFrame * CFrame.new(object.Size.X/2,-object.Size.Y/2,object.Size.Z/2)).Position,
		(object.CFrame * CFrame.new(object.Size.X/2,object.Size.Y/2,-object.Size.Z/2)).Position,
		(object.CFrame * CFrame.new(-object.Size.X/2,-object.Size.Y/2,-object.Size.Z/2)).Position,
		(object.CFrame * CFrame.new(-object.Size.X/2,-object.Size.Y/2,object.Size.Z/2)).Position,
		(object.CFrame * CFrame.new(object.Size.X/2,-object.Size.Y/2,-object.Size.Z/2)).Position,
		(object.CFrame * CFrame.new(-object.Size.X/2,object.Size.Y/2,-object.Size.Z/2)).Position,
	}
	
	local IsObsufcated = #cam:GetPartsObscuringTarget(Positions,{object,char}) == 0
	local _,OnScreen = cam:WorldToScreenPoint(object.Position)
	
	for _,pos in Positions do 
		if #cam:GetPartsObscuringTarget({pos},{object,char}) == 0 then
			IsObsufcated = true
			break
		end
	end
	
	return OnScreen and IsObsufcated 
end
10 Likes

Thank you! I will try this since I haven’t come to an answer yet

Thank you so much, I will try this

hi where can i put this script?

Code that accesses CurrentCamera and LocalPlayer only works in the client. I recommend using StarterCharacter so that the ray configuration updates with a new character reference per death/reset.

Also check out the post from @kalabgs since it’s way more reliable and intricate despite its complexity.

3 Likes

How would i check if it’s on the screen???
And how fire a function then?

1 Like

isOnScreen on the second code snippet is a boolean that denotes whether part is on the local player’s screen. I retrieved that by selecting the second value of the tuple that WorldToViewportPoint returns.

You can execute code based on isOnScreen’s value. If it’s true, then run your function:

local function myCode()
    --my code
end
if isOnScreen then
    myCode();
end
2 Likes

so how is the isonscreen detected?
aka where?

1 Like

could you make a script that is pasted into a part and if its visible it gets invisible?
it would be really helpful to learn

1 Like

Hello so what I did is I made a part in the workspace which is anchored and invisible I made a local script in the startercharacterscripts made the isonscreen code in check if is on screen then print(“Is on screen”) as you can see in the following code:

local part = workspace:WaitForChild("Behind");
local cam = workspace.CurrentCamera;

local isOnScreen = select(2, cam:WorldToViewportPoint(part.Position));

if isOnScreen then
	print("isonscreen")
end

Why doesn’t it work?

1 Like

Are you aware that your provided code snippet will only perform a single check? The script will only determine whether the part is on the screen once per character load. If you want this behavior to be recursive, you can repeatedly call it with traditional loops or RunService update events.

local part: BasePart;
local cam = workspace.CurrentCamera;

game:GetService'RunService':BindToRenderStep('check_visibility', Enum.RenderPriority.Camera.Value + 1, function()
	if select(2, cam:WorldToViewportPoint(part.Position)) then
		print'on screen';
	end
end)
3 Likes