Need some help with raycasting from the camera

Hello!

I am currently trying to make an interaction system similar to Welcome to Bloxburg’s.

The player has to be within 20 studs and somewhat facing the item they want to interact with. The problem is that raycasting from the camera’s lookvector is extremely unreliable and seems not to work at all unless shift lock is being used. And that is unreliable as well.

Basically, if the player is looking at the interactable object with the camera and the camera only, the interaction prompt should show up.

Video raycasting from the camera:

Video raycasting from the HumanoidRootPart:

Here’s my script currently:

local raycastParameters = RaycastParams.new()
raycastParameters.CollisionGroup = 'RayCollisionGroup'

runService.RenderStepped:Connect(function()
    local closestDistance = math.huge
    local closestObject = nil
    for i,interactionObject in pairs(interactableObjects) do
        if interactionObject:IsA('Model') then
            interactionObject = interactionObject.PrimaryPart
        end
        local ray = workspace:Raycast(currentCamera.CFrame.Position, currentCamera.CFrame.LookVector * minimumInteractionDistance, raycastParameters)
        if ray then
            if interactionObject == ray.Instance or interactionObject.Parent == ray.Instance.Parent then
                if (humanoidRootPart.Position - interactionObject.Position).Magnitude < minimumInteractionDistance then
                    if (humanoidRootPart.Position - interactionObject.Position).Magnitude < closestDistance then
                        closestDistance = (humanoidRootPart.Position - interactionObject.Position).Magnitude
                        closestObject = interactionObject
                    end
                end
            end
        end
    end
    if not closestObject then
        template.Parent = nil
        template.Adornee = nil
        if focusedInteraction then
            focusedInteraction:Disconnect()
        end
        if currentConnection then
            currentConnection:Disconnect()
        end
    elseif closestObject ~= openInteraction then
        promptInteraction(closestObject)
    end
end)

Variables:
currentCamera = workspace.CurrentCamera
humanoidRootPart = game.Players.LocalPlayer.HumanoidRootPart.HumanoidRootPart

For your case, you may want to use WorldToScreenPoint instead.

local vector, onScreen = camera:WorldToScreenPoint(interactableObject.Position)
if (HumanoidRootPart.Position - interactableObject.Position).Magnitude <= 20 and onScreen then
    -- item is on screen and player is within 20 studs
end
2 Likes

Hey there!
I’m not sure if I understood it right, but if I did, maybe calculating the dot product and getting what is in player’s fov may be a better solution.

If you want, check out this video:

(you would have to do this with the camera)

1 Like

Thanks! What would I use as the worldPoint variable though? The PrimaryPart’s position?

@GAFFAL_1236 - I’m looking to raycast from the localplayer’s camera. I think WorldToScreenPoint would be the easiest though.

1 Like

Wow, that’s awesome, I didn’t know about that. Thank you a lot, pretty easier.

1 Like

When I initially posted my response, I made the mistake of treating the first variable as a Vector3 positional coordinate instead of a 2D screen coordinate. I edited with the correct use.

You would want to use the position of the interactable object as the world point. How I would use this in your system is to loop through all interactable objects like you’ve already done. First, check what objects are close enough to the player (<20 studs), and add those objects to a list. Then loop through those objects and find which is the closest and on screen, and display the UI for that one part.

1 Like

Ah okay, that would work out perfectly! Thanks so much!

1 Like