Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
3

How can I accurately convert the 2d area of a UI Frame to a Region3?

Asked by
movsb 242 Moderation Voter
6 years ago

Ok, so to start off, what I am trying to do is find all of the parts in the 3d game world that a 2d UI Frame would theoretically contain based off of the camera's rotation, field of view, and such if the 2d UI Frame was given a 3d coordinate, and a depth-axis that extends back about 1000 studs.

The method I have coded to do so involves creating two rays at the upper and lower corners of the UI Frame, and creating a Region3 from there, and using the function Workspace:FindPartsInRegion3() to get all of the parts that the UI Frame would theoretically contain if it were converted to a 3d Region3 with the conditions previously stated above.

Here is my code:

local function GetPartsInUI(ui)
    local cam = game.Workspace.CurrentCamera;
    local min_corner = ui.AbsolutePosition;
    local max_corner = ui.AbsolutePosition + ui.AbsoluteSize;
    local r1 = cam:ScreenPointToRay(min_corner.X, min_corner.Y, 5);
    local r2 = cam:ScreenPointToRay(max_corner.X, max_corner.Y, 5);
    local pos1 = CFrame.new(r1.Origin, (r1.Direction - r1.Origin).unit);
    local pos2 = CFrame.new(r2.Origin, (r2.Direction - r2.Origin).unit);
    pos1 = pos1 - (pos1.upVector * 999); --increase the imaginary depth axis by about 1000 studs
    local pos1_sum = pos1.p.X + pos1.p.Y + pos1.p.Z;
    local pos2_sum = pos2.p.X + pos2.p.Y + pos2.p.Z;
    local reg3 = Region3.new((function()
        local rt = {};
        if pos1_sum > pos2_sum then
            rt[1] = pos2.p;
            rt[2] = pos1.p;
        else
            rt[1] = pos1.p;
            rt[2] = pos2.p;
        end
        return unpack(rt);
    end)());
    local parts = game.Workspace:FindPartsInRegion3(reg3, nil, math.huge);
    return parts;
end

My problem is that half of the time the parts inside of the UI Frame are not recognized to be inside of the Region3.

I am assuming that this is either due to my setting up of the position (pos1 and pos2) variables, or an issue with the rays returned from the :ScreenPointToRay() function.

With all of that being said, please tell me what am I doing wrong, and how can I improve upon this???

Thanks

1 answer

Log in to vote
1
Answered by
EgoMoose 802 Moderation Voter
6 years ago

The first problem is that the view of a camera's in Roblox is not represented by a square/rectangle, but rather by a square viewing frustum.

The second problem is that region3's are 1) squares/rectangles, 2) cannot be rotated, and 3) have limitations on their size.

We are somewhat lucky when it comes to the frustum because Roblox actually gives us most of the tools we need to do this without getting into the math.

local draw = require(game.Workspace.draw); -- just a module I made for drawing stuff
local camera = game.Workspace.CurrentCamera;
local vps = camera.ViewportSize;

-- could adjust these as the corners for your UI
local r0 = camera:ViewportPointToRay(0, 0);
local r1 = camera:ViewportPointToRay(0, vps.y);
local r2 = camera:ViewportPointToRay(vps.x, vps.y);
local r3 = camera:ViewportPointToRay(vps.x, 0);

for k, v in next, {r0, r1, r2, r3} do
    -- this function just draws a ray
    draw.ray(v.Origin, v.Direction*10, game.Workspace);
end;

In the above we can see the frustum from our camera's view port.

Now that you know the shape and have data on it you can use something like SAT or GJK collision detection to see if a part is within the frustum. However, my recommendation if you go this route is to first use a broad region3 to lower the amount of parts you have to test.

Good luck!

Ad

Answer this question