2d player viewcone and line of sight. Please help!

Hi, I need some help. I need to make it so that players locally can’t see objects they don’t see. To do this, I have written functions to check what a player can and cannot see. There is a problem: some parts start to flicker and I don’t know how to fix it. According to my logic everything should work properly. Please help.

local function isPointInView(position)
	local characterToPosition = (position - humanoidRootPart.Position).Unit
	local forwardVector = humanoidRootPart.CFrame.LookVector

	local dotProduct = forwardVector:Dot(characterToPosition)
	local angle = math.acos(dotProduct) * (180 / math.pi)

	-- Минимальное и максимальное расстояние для видимости объекта
	local minDistance = 5 -- Минимальное расстояние для видимости объекта
	local distanceToObject = (position - humanoidRootPart.Position).Magnitude

	-- Если объект на очень близком расстоянии (меньше minDistance), то он всегда видим
	if distanceToObject <= minDistance then
		return true
	end

	-- Проверяем, что объект в пределах угла обзора и на подходящем расстоянии
	if angle <= fieldOfView / 2 and distanceToObject <= visibilityDistance then
		-- Проверка на наличие препятствий
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {humanoidRootPart.Parent} -- Игнорируем персонажа игрока
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

		local rayOrigin = humanoidRootPart.Position
		local rayDirection = (position - humanoidRootPart.Position)

		local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		-- Если луч пересёк объект, проверяем его прозрачность
		if raycastResult and raycastResult.Instance then
			local hitPart = raycastResult.Instance

			-- Если объект не является прозрачным, то он блокирует видимость
			if hitPart:IsA("BasePart") and hitPart.Transparency == 0 then
				return false -- Непрозрачное препятствие, объект не виден
			end
		end

		-- Если объект не пересекается с прозрачными объектами, считаем его видимым
		return true
	end

	return false
end

local function updateBlindZonesAndRoofs()
	for _, object in pairs(workspace:GetDescendants()) do
		if object:IsA("BasePart") then
			if CollectionService:HasTag(object, "AlwaysVisible") then
				-- Если объект всегда видим, ничего не меняем
			elseif CollectionService:HasTag(object, "AlwaysInvisible") then
				object.Transparency = 1 -- Объект всегда скрыт
			else
				local isVisible = isPointInView(object.Position)

				if isVisible then
					object.Transparency = 0 -- Объект видим
				else
					object.Transparency = 1 -- Объект скрыт
				end

				-- Теперь ищем декали внутри BasePart и делаем их невидимыми
				for _, child in pairs(object:GetChildren()) do
					if child:IsA("Decal") then
						child.Transparency = object.Transparency
					end
				end
			end
		end
	end

	local proximityThreshold = 15 -- Расстояние для приближения

	for _, model in pairs(workspace.Buildings:GetChildren()) do
		if model:IsA("Model") and model:FindFirstChild("Roof") then
			local roof = model:FindFirstChild("Roof")

			-- Получаем границы всей крыши как модели
			local roofCFrame, roofSize = roof:GetBoundingBox()

			-- Определяем минимальные и максимальные границы
			local roofMin = Vector3.new(
				roofCFrame.Position.X - roofSize.X / 2,
				-10000, -- Игнорируем Y
				roofCFrame.Position.Z - roofSize.Z / 2
			)
			local roofMax = Vector3.new(
				roofCFrame.Position.X + roofSize.X / 2,
				10000, -- Игнорируем Y
				roofCFrame.Position.Z + roofSize.Z / 2
			)

			-- Проверяем, находится ли игрок рядом с крышей
			local isNearby = humanoidRootPart.Position.X > (roofMin.X - proximityThreshold) and humanoidRootPart.Position.X < (roofMax.X + proximityThreshold) and
				humanoidRootPart.Position.Z > (roofMin.Z - proximityThreshold) and humanoidRootPart.Position.Z < (roofMax.Z + proximityThreshold)

			-- Если игрок близко, делаем всю крышу прозрачной
			if isNearby then
				for _, part in pairs(roof:GetDescendants()) do
					if part:IsA("BasePart") or part:IsA("Texture") then
						part.Transparency = 1 -- Прозрачный
					end
				end
			else
				-- Если игрок не рядом, проверяем видимость
				for _, part in pairs(roof:GetDescendants()) do
					if part:IsA("BasePart") then
						local isVisible = isPointInView(part.Position)

						if isVisible then
							part.Transparency = 0 -- Полностью видимый
						else
							part.Transparency = 1 -- Полностью скрытый
						end
					end
				end
			end
		end
	end
end

Full code here:

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local character = game.Players.LocalPlayer.Character
local head = character:FindFirstChild("Head")
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
local distance = script.DistanceFromHead.Value
local CollectionService = game:GetService("CollectionService")

wait(0.5)
while not game.Players.LocalPlayer.Character:FindFirstChild("Head") do wait() end

local cam = workspace.CurrentCamera
cam.CameraType = "Scriptable"

local debug = false

local fieldOfView = 90 -- Угол обзора в градусах
local visibilityDistance = 35 -- Дистанция видимости

local function setRoofTransparency(roof, transparency)
	for _, part in pairs(roof:GetDescendants()) do
		if part:IsA("BasePart") then
			part.Transparency = transparency
		end
	end
end

local function isPointInView(position)
	local characterToPosition = (position - humanoidRootPart.Position).Unit
	local forwardVector = humanoidRootPart.CFrame.LookVector

	local dotProduct = forwardVector:Dot(characterToPosition)
	local angle = math.acos(dotProduct) * (180 / math.pi)

	-- Минимальное и максимальное расстояние для видимости объекта
	local minDistance = 5 -- Минимальное расстояние для видимости объекта
	local distanceToObject = (position - humanoidRootPart.Position).Magnitude

	-- Если объект на очень близком расстоянии (меньше minDistance), то он всегда видим
	if distanceToObject <= minDistance then
		return true
	end

	-- Проверяем, что объект в пределах угла обзора и на подходящем расстоянии
	if angle <= fieldOfView / 2 and distanceToObject <= visibilityDistance then
		-- Проверка на наличие препятствий
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {humanoidRootPart.Parent} -- Игнорируем персонажа игрока
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

		local rayOrigin = humanoidRootPart.Position
		local rayDirection = (position - humanoidRootPart.Position)

		local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		-- Если луч пересёк объект, проверяем его прозрачность
		if raycastResult and raycastResult.Instance then
			local hitPart = raycastResult.Instance

			-- Если объект не является прозрачным, то он блокирует видимость
			if hitPart:IsA("BasePart") and hitPart.Transparency == 0 then
				return false -- Непрозрачное препятствие, объект не виден
			end
		end

		-- Если объект не пересекается с прозрачными объектами, считаем его видимым
		return true
	end

	return false
end

local function updateBlindZonesAndRoofs()
	for _, object in pairs(workspace:GetDescendants()) do
		if object:IsA("BasePart") then
			if CollectionService:HasTag(object, "AlwaysVisible") then
				-- Если объект всегда видим, ничего не меняем
			elseif CollectionService:HasTag(object, "AlwaysInvisible") then
				object.Transparency = 1 -- Объект всегда скрыт
			else
				local isVisible = isPointInView(object.Position)

				if isVisible then
					object.Transparency = 0 -- Объект видим
				else
					object.Transparency = 1 -- Объект скрыт
				end

				-- Теперь ищем декали внутри BasePart и делаем их невидимыми
				for _, child in pairs(object:GetChildren()) do
					if child:IsA("Decal") then
						child.Transparency = object.Transparency
					end
				end
			end
		end
	end

	local proximityThreshold = 15 -- Расстояние для приближения

	for _, model in pairs(workspace.Buildings:GetChildren()) do
		if model:IsA("Model") and model:FindFirstChild("Roof") then
			local roof = model:FindFirstChild("Roof")

			-- Получаем границы всей крыши как модели
			local roofCFrame, roofSize = roof:GetBoundingBox()

			-- Определяем минимальные и максимальные границы
			local roofMin = Vector3.new(
				roofCFrame.Position.X - roofSize.X / 2,
				-10000, -- Игнорируем Y
				roofCFrame.Position.Z - roofSize.Z / 2
			)
			local roofMax = Vector3.new(
				roofCFrame.Position.X + roofSize.X / 2,
				10000, -- Игнорируем Y
				roofCFrame.Position.Z + roofSize.Z / 2
			)

			-- Проверяем, находится ли игрок рядом с крышей
			local isNearby = humanoidRootPart.Position.X > (roofMin.X - proximityThreshold) and humanoidRootPart.Position.X < (roofMax.X + proximityThreshold) and
				humanoidRootPart.Position.Z > (roofMin.Z - proximityThreshold) and humanoidRootPart.Position.Z < (roofMax.Z + proximityThreshold)

			-- Если игрок близко, делаем всю крышу прозрачной
			if isNearby then
				for _, part in pairs(roof:GetDescendants()) do
					if part:IsA("BasePart") or part:IsA("Texture") then
						part.Transparency = 1 -- Прозрачный
					end
				end
			else
				-- Если игрок не рядом, проверяем видимость
				for _, part in pairs(roof:GetDescendants()) do
					if part:IsA("BasePart") then
						local isVisible = isPointInView(part.Position)

						if isVisible then
							part.Transparency = 0 -- Полностью видимый
						else
							part.Transparency = 1 -- Полностью скрытый
						end
					end
				end
			end
		end
	end
end

local redSphere = Instance.new("Part")
redSphere.Shape = Enum.PartType.Ball
redSphere.Color = Color3.new(1, 0, 0)
redSphere.Material = Enum.Material.Neon
redSphere.Size = Vector3.new(0.2, 0.2, 0.2)
redSphere.Anchored = true
redSphere.CanCollide = false
redSphere.Parent = workspace

if not debug then
	redSphere:Destroy()
end

local offsetY = -57

local function checkAndSetRoofTransparency()
	local proximityThreshold = 15 -- Расстояние для приближения

	for _, model in pairs(workspace.Buildings:GetChildren()) do
		if model:IsA("Model") and model:FindFirstChild("Roof") then
			local roof = model:FindFirstChild("Roof")
			local roofCFrame, roofSize = roof:GetBoundingBox()

			local roofMin = roofCFrame.Position - roofSize / 2
			local roofMax = roofCFrame.Position + roofSize / 2

			local isNearby = humanoidRootPart.Position.X > (roofMin.X - proximityThreshold) and humanoidRootPart.Position.X < (roofMax.X + proximityThreshold) and
				humanoidRootPart.Position.Z > (roofMin.Z - proximityThreshold) and humanoidRootPart.Position.Z < (roofMax.Z + proximityThreshold) and
				humanoidRootPart.Position.Y < (roofMax.Y + proximityThreshold)

			if isNearby then
				setRoofTransparency(roof, 1) -- Прозрачный
			else
				setRoofTransparency(roof, 0) -- Полностью видимый
			end
		end
	end
end

RunService.Heartbeat:Connect(function()
	cam.CFrame = CFrame.new(Vector3.new(head.Position.X - 0.001, head.Position.Y + distance, head.Position.Z), head.Position)

	local mouseLocation = UserInputService:GetMouseLocation()
	local ray = cam:ScreenPointToRay(mouseLocation.X, mouseLocation.Y + offsetY)

	local groundY = 0
	local distanceToGround = (head.Position.Y - groundY) / -ray.Direction.Y
	local groundPosition = ray.Origin + ray.Direction * distanceToGround

	if debug then
		redSphere.Position = groundPosition
	end

	local characterPosition = Vector3.new(humanoidRootPart.Position.X, groundY, humanoidRootPart.Position.Z)
	local directionToCursor = (groundPosition - characterPosition).Unit
	humanoidRootPart.CFrame = CFrame.new(humanoidRootPart.Position, humanoidRootPart.Position + Vector3.new(directionToCursor.X, 0, directionToCursor.Z))

	updateBlindZonesAndRoofs()
	--checkAndSetRoofTransparency()
end)

And it would be nice to blur the area on the screen that the character can’t see

The error most likely lies here, where you are checking for the proximity of the roof. I suggest using an XZ box check for this instead, since you are making a 2D game.

local roofMin = roofCFrame.Position - roofSize / 2
local roofMax = roofCFrame.Position + roofSize / 2

local isNearby = humanoidRootPart.Position.X > (roofMin.X - proximityThreshold) and humanoidRootPart.Position.X < (roofMax.X + proximityThreshold) and
				humanoidRootPart.Position.Z > (roofMin.Z - proximityThreshold) and humanoidRootPart.Position.Z < (roofMax.Z + proximityThreshold) and
				humanoidRootPart.Position.Y < (roofMax.Y + proximityThreshold)

if isNearby then
	setRoofTransparency(roof, 1) -- Прозрачный
else
	setRoofTransparency(roof, 0) -- Полностью видимый
end

Replace this part:

local roofMin = roofCFrame.Position - roofSize / 2
local roofMax = roofCFrame.Position + roofSize / 2

local isNearby = humanoidRootPart.Position.X > (roofMin.X - proximityThreshold) and humanoidRootPart.Position.X < (roofMax.X + proximityThreshold) and
	humanoidRootPart.Position.Z > (roofMin.Z - proximityThreshold) and humanoidRootPart.Position.Z < (roofMax.Z + proximityThreshold) and
	humanoidRootPart.Position.Y < (roofMax.Y + proximityThreshold)

with this:

local hw = roofSize / 2
local isNearby = isInXZProximity(humanoidRootPart, roofCFrame, Vector2.new(hw.X + proximityThreshold, hw.Z + proximityThreshold))

and add these functions to the base-level scope of your code:

local function isNotWithin(x, a, b)
	if x < a then return true end
	if x > b then return true end
	return false
end
local function isInXZProximity(checkP, boundary, threshold)
	if isNotWithin(checkP.CFrame.X, boundary.CFrame.X-threshold.X, boundary.CFrame.X+threshold.X) then
		return false
	end
	if isNotWithin(checkP.CFrame.Z, boundary.CFrame.Z-threshold.Z, boundary.CFrame.Z+threshold.Z) then
		return false
	end
	return true
end

Let me know if it helps at all

Also I see that you use a similar method of chaining conditionals when you do other proximity checks. It is best practice to use functions instead, as there is less room for error

Thank yu for your answer! I replaced the code you suggested, but still nothing changed. The problem is not in the roofs, but in how the ray passes through the objects and why the objects flicker. If you change that

-- Если луч пересёк объект, проверяем его прозрачность
		if raycastResult and raycastResult.Instance then
			local hitPart = raycastResult.Instance

			-- Если объект не является прозрачным, то он блокирует видимость
			if hitPart:IsA("BasePart") and hitPart.Transparency == 0 then
				return false -- Непрозрачное препятствие, объект не виден
			end
		end

to this

-- Если луч пересёк объект, проверяем его прозрачность
		if raycastResult and raycastResult.Instance then
			local hitPart = raycastResult.Instance

			-- Если объект не является прозрачным, то он блокирует видимость
			if hitPart:IsA("BasePart") and hitPart.Transparency == 0 then
				return true -- Непрозрачное препятствие, объект не виден
			end
		end

only the objects in the character’s field of view will be visible, but it was important to me to make the objects that were obstructed invisible.

add the objects you want rays to pass through to the ignore list, when you are specifiying the RaycastParams

to make it more performant, maybe use recursive calls. Send out a cast, and if it hits glass (or an item with pass-through tag or something), add it to the blacklist and re-cast. This would be much better than adding every single pass-through part to the blacklist

Thank you for your answer!


Added tagged objects to the blacklist. Still flicker. Please test script in your place before suggesting something.
Here is my updated version:

wait(0.5)
while not game.Players.LocalPlayer.Character:FindFirstChild("Head") do wait() end

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local character = game.Players.LocalPlayer.Character
local head = character:FindFirstChild("Head")
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
local distance = script.DistanceFromHead.Value
local CollectionService = game:GetService("CollectionService")

local cam = workspace.CurrentCamera
cam.CameraType = "Scriptable"

local debug = false

local fieldOfView = 90 -- Угол обзора в градусах
local visibilityDistance = 35 -- Дистанция видимости

local function setRoofTransparency(roof, transparency)
	for _, part in pairs(roof:GetDescendants()) do
		if part:IsA("BasePart") then
			part.Transparency = transparency
		end
	end
end

local function isPointInView(position)
	local characterToPosition = (position - humanoidRootPart.Position).Unit
	local forwardVector = humanoidRootPart.CFrame.LookVector

	local dotProduct = forwardVector:Dot(characterToPosition)
	local angle = math.acos(dotProduct) * (180 / math.pi)

	-- Минимальное и максимальное расстояние для видимости объекта
	local minDistance = 5 -- Минимальное расстояние для видимости объекта
	local distanceToObject = (position - humanoidRootPart.Position).Magnitude

	-- Если объект на очень близком расстоянии (меньше minDistance), то он всегда видим
	if distanceToObject <= minDistance then
		return true
	end

	-- Проверяем, что объект в пределах угла обзора и на подходящем расстоянии
	if angle <= fieldOfView / 2 and distanceToObject <= visibilityDistance then
		-- Проверка на наличие препятствий
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {humanoidRootPart.Parent} -- Игнорируем персонажа игрока
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

		local rayOrigin = humanoidRootPart.Position
		local rayDirection = (position - humanoidRootPart.Position)

		local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		-- Если луч пересёк объект, проверяем его прозрачность
		if raycastResult and raycastResult.Instance then
			local hitPart = raycastResult.Instance

			-- Если объект не является прозрачным, то он блокирует видимость
			if hitPart:IsA("BasePart") and hitPart.Transparency == 0 then
				return false -- Непрозрачное препятствие, объект не виден
			end
		end

		-- Если объект не пересекается с прозрачными объектами, считаем его видимым
		return true
	end

	return false
end

local function updateBlindZonesAndRoofs()
	for _, object in pairs(workspace:GetDescendants()) do
		if object:IsA("BasePart") then
			if CollectionService:HasTag(object, "AlwaysVisible") then
				-- Если объект всегда видим, ничего не меняем
			elseif CollectionService:HasTag(object, "AlwaysInvisible") then
				object.Transparency = 1 -- Объект всегда скрыт
			else
				local isVisible = isPointInView(object.Position)

				if isVisible then
					object.Transparency = 0 -- Объект видим
				else
					object.Transparency = 1 -- Объект скрыт
				end

				-- Теперь ищем декали внутри BasePart и делаем их невидимыми
				for _, child in pairs(object:GetChildren()) do
					if child:IsA("Decal") then
						child.Transparency = object.Transparency
					end
				end
			end
		end
	end

	local proximityThreshold = 15 -- Расстояние для приближения

	for _, model in pairs(workspace.Buildings:GetChildren()) do
		if model:IsA("Model") and model:FindFirstChild("Roof") then
			local roof = model:FindFirstChild("Roof")

			-- Получаем границы всей крыши как модели
			local roofCFrame, roofSize = roof:GetBoundingBox()

			-- Определяем минимальные и максимальные границы
			local roofMin = Vector3.new(
				roofCFrame.Position.X - roofSize.X / 2,
				-10000, -- Игнорируем Y
				roofCFrame.Position.Z - roofSize.Z / 2
			)
			local roofMax = Vector3.new(
				roofCFrame.Position.X + roofSize.X / 2,
				10000, -- Игнорируем Y
				roofCFrame.Position.Z + roofSize.Z / 2
			)

			-- Проверяем, находится ли игрок рядом с крышей
			local isNearby = humanoidRootPart.Position.X > (roofMin.X - proximityThreshold) and humanoidRootPart.Position.X < (roofMax.X + proximityThreshold) and
				humanoidRootPart.Position.Z > (roofMin.Z - proximityThreshold) and humanoidRootPart.Position.Z < (roofMax.Z + proximityThreshold)

			-- Если игрок близко, делаем всю крышу прозрачной
			if isNearby then
				for _, part in pairs(roof:GetDescendants()) do
					if part:IsA("BasePart") or part:IsA("Texture") then
						part.Transparency = 1 -- Прозрачный
					end
				end
			else
				-- Если игрок не рядом, проверяем видимость
				for _, part in pairs(roof:GetDescendants()) do
					if part:IsA("BasePart") then
						local isVisible = isPointInView(part.Position)

						if isVisible then
							part.Transparency = 0 -- Полностью видимый
						else
							part.Transparency = 1 -- Полностью скрытый
						end
					end
				end
			end
		end
	end
end

local redSphere = Instance.new("Part")
redSphere.Shape = Enum.PartType.Ball
redSphere.Color = Color3.new(1, 0, 0)
redSphere.Material = Enum.Material.Neon
redSphere.Size = Vector3.new(0.2, 0.2, 0.2)
redSphere.Anchored = true
redSphere.CanCollide = false
redSphere.Parent = workspace

if not debug then
	redSphere:Destroy()
end

local offsetY = -57

local function checkAndSetRoofTransparency()
	local proximityThreshold = 15 -- Расстояние для приближения

	for _, model in pairs(workspace.Buildings:GetChildren()) do
		if model:IsA("Model") and model:FindFirstChild("Roof") then
			local roof = model:FindFirstChild("Roof")
			local roofCFrame, roofSize = roof:GetBoundingBox()

			local roofMin = roofCFrame.Position - roofSize / 2
			local roofMax = roofCFrame.Position + roofSize / 2

			local isNearby = humanoidRootPart.Position.X > (roofMin.X - proximityThreshold) and humanoidRootPart.Position.X < (roofMax.X + proximityThreshold) and
				humanoidRootPart.Position.Z > (roofMin.Z - proximityThreshold) and humanoidRootPart.Position.Z < (roofMax.Z + proximityThreshold) and
				humanoidRootPart.Position.Y < (roofMax.Y + proximityThreshold)

			if isNearby then
				setRoofTransparency(roof, 1) -- Прозрачный
			else
				setRoofTransparency(roof, 0) -- Полностью видимый
			end
		end
	end
end

RunService.Heartbeat:Connect(function()
	cam.CFrame = CFrame.new(Vector3.new(head.Position.X - 0.001, head.Position.Y + distance, head.Position.Z), head.Position)

	local mouseLocation = UserInputService:GetMouseLocation()
	local ray = cam:ScreenPointToRay(mouseLocation.X, mouseLocation.Y + offsetY)

	local groundY = 0
	local distanceToGround = (head.Position.Y - groundY) / -ray.Direction.Y
	local groundPosition = ray.Origin + ray.Direction * distanceToGround

	if debug then
		redSphere.Position = groundPosition
	end

	local characterPosition = Vector3.new(humanoidRootPart.Position.X, groundY, humanoidRootPart.Position.Z)
	local directionToCursor = (groundPosition - characterPosition).Unit
	humanoidRootPart.CFrame = CFrame.new(humanoidRootPart.Position, humanoidRootPart.Position + Vector3.new(directionToCursor.X, 0, directionToCursor.Z))

	updateBlindZonesAndRoofs()
end)

In your updated script, you are not adding the tagged items to the blacklist, i cant even find what you changed to be honest.

within the isPointInView function, you are Ray casting if the point is within distance and in the field of view. that’s where you should add the parts to FilterDescendantsInstances

Like this?

local function isPointInView(position)
	local characterToPosition = (position - humanoidRootPart.Position).Unit
	local forwardVector = humanoidRootPart.CFrame.LookVector

	local dotProduct = forwardVector:Dot(characterToPosition)
	local angle = math.acos(dotProduct) * (180 / math.pi)

	-- Минимальное и максимальное расстояние для видимости объекта
	local minDistance = 5 -- Минимальное расстояние для видимости объекта
	local distanceToObject = (position - humanoidRootPart.Position).Magnitude

	-- Если объект на очень близком расстоянии (меньше minDistance), то он всегда видим
	if distanceToObject <= minDistance then
		return true
	end

	-- Проверяем, что объект в пределах угла обзора и на подходящем расстоянии
	if angle <= fieldOfView / 2 and distanceToObject <= visibilityDistance then
		-- Подготовка RaycastParams с учетом тегов
		local raycastParams = RaycastParams.new()

		-- Добавляем HumanoidRootPart.Parent (игрока) в исключения
		local excludedParts = {humanoidRootPart.Parent}

		-- Находим все части, имеющие определенные теги, и добавляем их в список исключений
		for _, tag in pairs({"AlwaysVisible", "AlwaysInvisible"}) do
			for _, taggedPart in pairs(CollectionService:GetTagged(tag)) do
				table.insert(excludedParts, taggedPart)
			end
		end

		raycastParams.FilterDescendantsInstances = excludedParts
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

		local rayOrigin = humanoidRootPart.Position
		local rayDirection = (position - humanoidRootPart.Position)

		local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		-- Если луч пересёк объект, проверяем его прозрачность
		if raycastResult and raycastResult.Instance then
			local hitPart = raycastResult.Instance

			-- Если объект не является прозрачным, то он блокирует видимость
			if hitPart:IsA("BasePart") and hitPart.Transparency == 0 then
				return false -- Непрозрачное препятствие, объект не виден
			end
		end

		-- Если объект не пересекается с прозрачными объектами, считаем его видимым
		return true
	end

	return false
end

If that’s what you really meant, it didn’t help.

Hey sorry for the late reply, I didnt see this line before. Blacklist is not a valid RaycastFilterType, change it to Exclude. You should also probably add parts you want to ignore to this list, I promise it will help