Checking if part is in the camera is being super inaccurate

Basically, I’m trying to check if a part(the small cube in the image, not the spawn) is within my camera’s view and is unblocked by anything via checking every corner of the part and seeing if its on screen and then checking if its blocked or not through raycasting. It works somewhat, although, it will say its blocked even if several corners are very clearly unblocked(see picture)

My script is within the starter character scripts and is as following:

local RunService = game:GetService("RunService")

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local camera = workspace.CurrentCamera

function getCorners(part)	
	local cf = part.CFrame
	local size = part.Size

	local corners = {}

	local frontFaceCenter = (cf + cf.LookVector * size.Z/2)
	local backFaceCenter = (cf - cf.LookVector * size.Z/2)

	-- edge centers - 4 of 12 edges referenced
	local topFrontEdgeCenter = frontFaceCenter + frontFaceCenter.UpVector * size.Y/2
	local bottomFrontEdgeCenter = frontFaceCenter - frontFaceCenter.UpVector * size.Y/2
	local topBackEdgeCenter = backFaceCenter + backFaceCenter.UpVector * size.Y/2
	local bottomBackEdgeCenter = backFaceCenter - backFaceCenter.UpVector * size.Y/2

	-- corners
	corners.topFrontRight = (topFrontEdgeCenter + topFrontEdgeCenter.RightVector * size.X/2).Position
	corners.topFrontLeft = (topFrontEdgeCenter - topFrontEdgeCenter.RightVector * size.X/2).Position

	corners.bottomFrontRight = (bottomFrontEdgeCenter + bottomFrontEdgeCenter.RightVector * size.X/2).Position
	corners.bottomFrontLeft = (bottomFrontEdgeCenter - bottomFrontEdgeCenter.RightVector * size.X/2).Position

	corners.topBackRight = (topBackEdgeCenter + topBackEdgeCenter.RightVector * size.X/2).Position
	corners.topBackLeft = (topBackEdgeCenter - topBackEdgeCenter.RightVector * size.X/2).Position

	corners.bottomBackRight = (bottomBackEdgeCenter + bottomBackEdgeCenter.RightVector * size.X/2).Position
	corners.bottomBackLeft = (bottomBackEdgeCenter - bottomBackEdgeCenter.RightVector * size.X/2).Position

	return corners
end

function checkVisibility(target)
	local corners = getCorners(target)

	for corner, cornerPos in pairs(corners) do
		
		local vector, onScreen = camera:WorldToScreenPoint(cornerPos)

		if not onScreen then print("target not onscreen") return false end

		local screenPoint = Vector2.new(vector.X, vector.Y)
		local depth = vector.Z

		local raystart = camera.CFrame.Position
		local raydestination = target.Position
		local raydirection = raydestination - raystart

		local raycastParams = RaycastParams.new()

		raycastParams.FilterDescendantsInstances = {camera}

		raycastParams.FilterType = Enum.RaycastFilterType.Exclude

		local result = workspace:Raycast(raystart, raydirection, raycastParams)


		---checking to see if we hit anything
		if result then
			local part = result.Instance
			print(part.Name)
			if result.Instance == target then
				print("target unblocked")
				return true
			end
		end
		
	end
		print("target blocked or not there")
		return false

	end

	RunService.RenderStepped:Connect(function()

	if checkVisibility(workspace:FindFirstChild("Part")) then
		workspace:FindFirstChild("SpawnLocation").BrickColor = BrickColor.new("Lime green")
	else
		workspace:FindFirstChild("SpawnLocation").BrickColor = BrickColor.new("Really red")
	end

	end)

The corner’s script is pretty much directly taken from another post:

I’m no expert, but you seem to be returning false if any of the corners aren’t visible before you do the check.

	for corner, cornerPos in pairs(corners) do
		
		local vector, onScreen = camera:WorldToScreenPoint(cornerPos)
		if not onScreen then print("target not onscreen") return false end

Shouldn’t you be going through all the corner in the pairs loop, putting their OnScreen result into a table, then checking to see if any of them are true? If even one out of the 8 corners returns true then the object is visible.

1 Like

I believe the method youre looking for is called GetPartsObscuringTarget

It finds all the parts in between your target part and the camera.

You can also use it in combination with WorldToViewportPoint

If you want to you may also be able to use WorldToScreenPoint
Then you can get the size of the players screen and check if its between the edges of the screen.

local screenSize = workspace.CurrentCamera.ViewportSize
3 Likes

oh yeah you seem to be right. i removed the
if not onScreen then print("target not onscreen") return false end
as well as add a check if its onsceen at the final portion and combined it with smashman65’s solutions and works really well now:

local RunService = game:GetService("RunService")

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local camera = workspace.CurrentCamera

function getCorners(part)	
	local cf = part.CFrame
	local size = part.Size

	local corners = {}

	local frontFaceCenter = (cf + cf.LookVector * size.Z/2)
	local backFaceCenter = (cf - cf.LookVector * size.Z/2)

	-- edge centers - 4 of 12 edges referenced
	local topFrontEdgeCenter = frontFaceCenter + frontFaceCenter.UpVector * size.Y/2
	local bottomFrontEdgeCenter = frontFaceCenter - frontFaceCenter.UpVector * size.Y/2
	local topBackEdgeCenter = backFaceCenter + backFaceCenter.UpVector * size.Y/2
	local bottomBackEdgeCenter = backFaceCenter - backFaceCenter.UpVector * size.Y/2

	-- corners
	corners.topFrontRight = (topFrontEdgeCenter + topFrontEdgeCenter.RightVector * size.X/2).Position
	corners.topFrontLeft = (topFrontEdgeCenter - topFrontEdgeCenter.RightVector * size.X/2).Position

	corners.bottomFrontRight = (bottomFrontEdgeCenter + bottomFrontEdgeCenter.RightVector * size.X/2).Position
	corners.bottomFrontLeft = (bottomFrontEdgeCenter - bottomFrontEdgeCenter.RightVector * size.X/2).Position

	corners.topBackRight = (topBackEdgeCenter + topBackEdgeCenter.RightVector * size.X/2).Position
	corners.topBackLeft = (topBackEdgeCenter - topBackEdgeCenter.RightVector * size.X/2).Position

	corners.bottomBackRight = (bottomBackEdgeCenter + bottomBackEdgeCenter.RightVector * size.X/2).Position
	corners.bottomBackLeft = (bottomBackEdgeCenter - bottomBackEdgeCenter.RightVector * size.X/2).Position

	return corners
end

function checkVisibility(target)
	local corners = getCorners(target)

	for corner, cornerPos in pairs(corners) do

		local vector, onScreen = camera:WorldToViewportPoint(cornerPos)
		if not onScreen then continue end


		local blockingParts = camera:GetPartsObscuringTarget({cornerPos}, {target})
		print(blockingParts)
		if #blockingParts == 0 and onScreen then
			print("target unblocked")
			return true
		end
		---checking to see if we hit anything

	end
	print("target blocked or not there")
	return false

end

RunService.RenderStepped:Connect(function()

	if checkVisibility(workspace:FindFirstChild("Part")) then
		workspace:FindFirstChild("SpawnLocation").BrickColor = BrickColor.new("Lime green")
	else
		workspace:FindFirstChild("SpawnLocation").BrickColor = BrickColor.new("Really red")
	end

end)
1 Like

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