Issue With Checking Part On Camera

Hello, I’ve been working on a Script that lets me check if a parts on the screen and I’m having a issue well I can’t figure a way to check what face it is because I want to be able to check if the front face is on the screen not just the main part.

– Thank you.

repeat
	task.wait(2.5)
until game.Loaded

local Player = game:GetService("Players").LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local RunServiceKey = "OnScreenKey"
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Remotes = ReplicatedStorage:WaitForChild("Remotes")
local LookedEvent = Remotes:WaitForChild("Looked")

local Camera = workspace.CurrentCamera
local Part = workspace:WaitForChild("Roblox").Head

local HasLooked = false

local function FireServer()
	local Humanoid = Character:FindFirstChild("Humanoid")
	if not Humanoid then return end
	if Humanoid.Health <= 0 then return end
	if HasLooked == true then return end
	
	HasLooked = true
	
	warn("Uh oh.")
	LookedEvent:FireServer()
end

local function Main()
	local ScreenVector, OnScreen = Camera:WorldToViewportPoint(Part.Position)
	
	if OnScreen then
		FireServer()
		workspace.Baseplate.Color = Color3.new(0, 1, 0)
	else
		local CornerOnScreen = false
		
		for X = -1, 1, 2 do
			for Y = -1, 1, 2 do
				for Z = -1, 1, 2 do
					local Position = Part.CFrame:ToWorldSpace(CFrame.new(X * Part.Size.X / 2, Y * Part.Size.Y / 2, Z * Part.Size.Z / 2)).Position
					local ScreenVector, OnScreen = Camera:WorldToViewportPoint(Position)
					
					if OnScreen then
						CornerOnScreen = true
						break
					end
				end
				if CornerOnScreen then
					break
				end
			end
			if CornerOnScreen then
				break
			end
		end
		
		if CornerOnScreen then
			FireServer()
			workspace.Baseplate.Color = Color3.new(0, 1, 0)
		else
			workspace.Baseplate.Color = Color3.new(1, 0, 0)
		end
	end
end

RunService:BindToRenderStep(RunServiceKey, 1, Main)

Player.CharacterAdded:Connect(function(NewCharacter)
	Character = NewCharacter
	
	repeat task.wait(5) until NewCharacter:FindFirstChild("Humanoid")
	HasLooked = false
	print("Reset HasLooked")
end)

You could replace this with the following:
Camera:WorldToViewportPoint(Part.Position + Part.CFrame.LookVector * Part.Size.Z / 2)

This might work for your use case. If 2 doesn’t work, try 1.9.

It didn’t work. Should I try to do that with the corners?

I think I missed something, the “OnScreen” part doesn’t actually check whether the part is occluded or not. Just if the camera is facing it. So, one thing you can try is to fire a raycast towards that point and see if it results in the part, but in order to make it accurate, you will have to exclude transparent parts.

local Params = RaycastParams.new()
Params.FilterDescendantsInstances = {}
Params.IgnoreCanCollide = false
Params.FilterType = Enum.FilterType.Exclude
--if OnScreen is true, fire a raycast as well
local RaycastResult = workspace:Raycast(Camera.CFrame.Position, CFrame.lookAt(Camera.Position, Part.Position + Part.CFrame.LookVector * Part.Size.Z / 2, Params)

if RaycastResult and RaycastResult.Instance == Part then
--It is on the screen and is visible in the viewport.

@SubtotalAnt8185 The raycasting isn’t working.

I think I missed another thing. We have to check the normal of the raycast after it’s been done.

--if OnScreen is true, fire a raycast as well
local RaycastResult = workspace:Raycast(Camera.CFrame.Position, CFrame.lookAt(Camera.Position, Part.Position, Params)

if RaycastResult and RaycastResult.Instance == Part and Part.CFrame.LookVector:FuzzyEq(RaycastResult.Normal) then
--It is on the screen and is visible in the viewport.

Now, this will fail if the part you’re checking isn’t a rectangular prism or has a flat front surface. For a MeshPart, the collision should be set to Box.

I hate to say it but it errors because Position isn’t a valid thing from CurrentCamera. So, I did CurrentCamera.CFrame.Position but it errors again.

That seems to have been a typo. Yes, Camera.CFrame.Position should be correct, but it seems like after I deleted some of the previous code it messed up the syntax. Sorry about that.

--if OnScreen is true, fire a raycast as well
local RaycastResult = workspace:Raycast(Camera.CFrame.Position, CFrame.lookAt(Camera.CFrame.Position, Part.Position).LookVector * 1000, Params)

if RaycastResult and RaycastResult.Instance == Part and Part.CFrame.LookVector:FuzzyEq(RaycastResult.Normal) then
--It is on the screen and is visible in the viewport.

It works but how can I make it so if the part is taller like a Rig and the Head is taller than the player that it will work?

PS: I’m not good at cframes.

What do you mean by this? Provide an example.

Currently, this should work perfectly fine however if there’s any occluding parts like accessories or “Hair” in the character, this will not run, even if the part is clearly visible. So, you may need to adjust that later by adding more instances into the filter.

Never mind I need to add something to the RaycastParams to exclude the Player’s character. Thank you for all your help

local Params = RaycastParams.new()
Params.FilterDescendantsInstances = {Player.Character}
Params.FilterType = Enum.RaycastFilterType.Exclude

I found a weird bug…

Heres the video

Here’s my code:

repeat
	task.wait(2.5)
until game.Loaded

print("LOADED")

local Player = game:GetService("Players").LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local RunServiceKey = "OnScreenKey"
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Remotes = ReplicatedStorage:WaitForChild("Remotes")
local LookedEvent = Remotes:WaitForChild("Looked")

local Camera = workspace.CurrentCamera
local Part = workspace:WaitForChild("Roblox").Head

local HasLooked = false

local Params = RaycastParams.new()
Params.FilterDescendantsInstances = {Player.Character}
Params.FilterType = Enum.RaycastFilterType.Exclude

local function FireServer()
	local Humanoid = Character:FindFirstChild("Humanoid")
	if not Humanoid then return end
	if Humanoid.Health <= 0 then return end
	if HasLooked == true then return end
	
	HasLooked = true
	
	warn("Uh oh.")
	LookedEvent:FireServer()
end

local function Main()
	local ScreenVector, OnScreen = Camera:WorldToViewportPoint(Part.Position)
	
	if OnScreen then
		local RaycastResult = workspace:Raycast(Camera.CFrame.Position, CFrame.lookAt(Camera.CFrame.Position, Part.Position).LookVector * 1000, Params)
		
		if RaycastResult and RaycastResult.Instance == Part and Part.CFrame.LookVector:FuzzyEq(RaycastResult.Normal) then
			FireServer()
			workspace.Baseplate.Color = Color3.new(0, 1, 0)
		end	
	else
		local CornerOnScreen = false
		
		for X = -1, 1, 2 do
			for Y = -1, 1, 2 do
				for Z = -1, 1, 2 do
					local Position = Part.CFrame:ToWorldSpace(CFrame.new(X * Part.Size.X / 2, Y * Part.Size.Y / 2, Z * Part.Size.Z / 2)).Position
					local ScreenVector, OnScreen = Camera:WorldToViewportPoint(Position)
					
					if OnScreen then
						CornerOnScreen = true
						break
					end
				end
				if CornerOnScreen then
					break
				end
			end
			if CornerOnScreen then
				break
			end
		end
		
		if CornerOnScreen then
			local RaycastResult = workspace:Raycast(Camera.CFrame.Position, CFrame.lookAt(Camera.CFrame.Position, Part.Position).LookVector * 1000, Params)
			
			if RaycastResult and RaycastResult.Instance == Part and Part.CFrame.LookVector:FuzzyEq(RaycastResult.Normal) then
				FireServer()
				workspace.Baseplate.Color = Color3.new(0, 1, 0)
			end
		else
			workspace.Baseplate.Color = Color3.new(1, 0, 0)
		end
	end
end

RunService:BindToRenderStep(RunServiceKey, 1, Main)

Player.CharacterAdded:Connect(function(NewCharacter)
	Character = NewCharacter
	
	repeat task.wait(5) until NewCharacter:FindFirstChild("Humanoid")
	HasLooked = false
	print("Reset HasLooked")
end)

Or should I use an attachment and check if you hover over the attachment?

That slow…

if not game:IsLoaded() then game.Loaded:Wait() end

Thank you I’ll use that for my script.

1 Like

This is very weird.

I think what you should do is put the code inside the else inside this if OnScreen then, and before you do that remove these in the if OnScreen then because there’s already one inside.

So,

if OnScreen then
local CornerOnScreen = false
		
		for X = -1, 1, 2 do
			for Y = -1, 1, 2 do
				for Z = -1, 1, 2 do
					local Position = Part.CFrame:ToWorldSpace(CFrame.new(X * Part.Size.X / 2, Y * Part.Size.Y / 2, Z * Part.Size.Z / 2)).Position
					local ScreenVector, OnScreen = Camera:WorldToViewportPoint(Position)
					
					if OnScreen then
						CornerOnScreen = true
						break
					end
				end
				if CornerOnScreen then
					break
				end
			end
			if CornerOnScreen then
				break
			end
		end
		
		if CornerOnScreen then
			local RaycastResult = workspace:Raycast(Camera.CFrame.Position, CFrame.lookAt(Camera.CFrame.Position, Part.Position).LookVector * 1000, Params)
			
			if RaycastResult and RaycastResult.Instance == Part and Part.CFrame.LookVector:FuzzyEq(RaycastResult.Normal) then
				FireServer()
				workspace.Baseplate.Color = Color3.new(0, 1, 0)
			end
		else
			workspace.Baseplate.Color = Color3.new(1, 0, 0)
		end
end

It’s not properly formatted, but this is generally what you’d want.

I’ll try it but thank you for helping

@SubtotalAnt8185 I have a question can you help me with something