How does Proximity Prompt's RequiresLineOfSight work?

I am trying to make a interaction system myself and was wondering if I could make it have the same mechanics as the Proximity Prompt’s RequiresLineOfSight.

In other words, how would I go about making something similar to this?

1 Like

AFAIK internally the implementation is the same as this:
Camera:WorldToViewportPoint (roblox.com)
ProximityPrompts seem to just query whether the adornee is on-screen or not, as returned as the second variable (inViewport) from this function. You can use it to see if something is on-screen or not similarly.

But here’s the thing, that finds if it is purely in the middle. I’m trying to make it to where for example; If there are two parts on the screen, find which one is closer to the middle of the screen. I’ve tried multiple methods, although failing to succeed every time.

1 Like

You could do that with that function as well, it returns the UI offset, so you could capture both parts’ UI offsets and deduce which one is nearest by math. Or maybe, check to find out that both are on-screen, and then grab the magnitude between the player and the two parts you are using as adornees to your interaction system to find which is closer in world space?

I’ve done this before. Can you share your script and I might be able to see what went wrong?

As for your first post, it seems to cast a ray from your camera in the direction of which ever part (goal - origin), if the ray doesn’t hit said part, it won’t appear.


Ok, so in the picture provided there are two blocks. Both at different positions. (yes i know the text is extremally wrong and same with the middle line, although it is just an example). As you can see, the black block is closer to the middle line, so it will be chosen.

So with that example, how would I go about doing that?

Right now I’m using WorldToScreenPoint with a raycast, although that only works if the center of the screen is on the part.

Try checking out the Roblox API.

1 Like

That doesn’t provide any documentation about how the RequireLineOfSight works.

Thought it could’ve sparked some ideas for you.

The video does show a “line” and similarly to this you could raycast from the player’s camera to whatever it is your trying to view. If the raycast hits anything before the specified object, then you’ll know the player doesn’t have sight of it, and if it returns the object, they’re able to see it.

Gotcha, so you’re absolutely on the right track with WorldToScreenPoint.

So what you need to do first is narrow down the parts you want to check to only those within a certain proximity, you can probably use a table of interactable parts.

local interactableParts = {} -- should be all baseparts

Now, iterate through the interactable parts and find whether they’re close enough to the character to be interacted with. I’ll use the humanoidRootPart but you could use the character’s PrimaryPart or another body part, doesn’t really matter. In this part, assume humanoidRootPart is the character’s HumanoidRootPart. My code is weirdly formatted but it should get the point across

local maxInteractionDistance = 10 -- let's say they can interact with parts that are up to 10 studs away

local partsOnScreenWithinProximity = {}

for i,v in ipairs(interactableParts) do
    if (v.Position - humanoidRootPart.Position).Magnitude <= maxInteractionDifficulty then -- subtract the position from the other position to get a position that is relative to one another, and then use the .Magnitude property to get the distance from the origin (basically the distance from one position to the other
        -- next, let's check if the part is directly visible by the camera with nothing obstructing it:
        -- to get the direction from the camera to the part, we subtract the camera's position from the goal position (goal - origin)
        local ray = workspace:Raycast(v.Position, v.Position - workspace.CurrentCamera.Position)
        -- now, we can check that the ray's result is v, which is the part in the current iteration
        if ray and ray.Instance == v then -- if the ray isn't nil (it hit a part) and the ray's result is the same as v,
            local screenPoint, isOnScreen = workspace.CurrentCamera:WorldToViewportPoint(v.Position) -- okay, this is where the more interesting stuff happens, since we first calculated any obstructions to the part in question, we now have to find whether the part is within the screen's bounds which is what this does
            if not isOnScreen then
                continue
            end
            -- okay, it's on screen so we now can add it to the partsOnScreenWithinProximity table
            table.insert(partsOnScreenWithinProximity, {v, Vector2.new(screenPoint.X, screenPoint.Y)}) -- index 1 is the part, index 2 is the point on the screen. let's save the screen point so we don't have to make a duplicate call to camera:WorldToViewportPoint
        end
    end
end
-- okay, now we find which part is the closest to the centre of the screen which we can do by comparing all of the parts' magnitudes from the centre of the screen:
local screenCentre = workspace.CurrentCamera.ViewportSize / 2 -- this point is the middle of the screen
local closestMagnitude, closestPart = math.huge, nil -- we need to have it as math.huge so any comparisons made will be smaller than what it is currently
for i,v in ipairs(partsOnScreenWithinProximity) do -- remember, v is a table containing 1, the part, and 2, the position on screen
    local magnitude = (screenCentre - v[2]).Magnitude -- like Vector3's, Vector2's also have a magnitude property
    if magnitude < closestMagnitude then -- if the magnitude is smaller than the current smallest magnitude then assign the closestMagnitude and closestPart variables to be the ones from this iteration
        closestMagnitude, closestPart = magnitude, v[1]
    end
end

Now that the loop is over, the closestPart variable will be equal to the closest part to the middle of the centre, that is on the screen, and that is not obstructed by any other parts.

1 Like

This works extremally well, and explains the point perfectly. Thank you so much for your time!

1 Like