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
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
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
it came up with this error any idea what it means?
attempt to perform arithmetic (mul) on Ray and number
Try it now. I used ray.Unit
on accident. Should have been ray.Direction
.
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
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
thanks a ton. This really helped me.
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
– 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)
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
local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 1000, raycastParams)
the “* 1000” is the distance in studs
It didn’t work for me, there was nothing blocking the way
The Block should be moving upwards
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
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
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