Screen3D - A 3D UI framework that just works

Do you want to create awesome, phighting-esque 3D UI without the hassle of Renderstepped loops, arbitrary offsets, or god forbid UI resizing issues? Well boy, do I have the solution for you!

Screen3D was made with the goal of bridging the gap between 2D and 3D UI.

Usage:

First, create your Screen3D object.

This can be done for any 2D ScreenGui, but I’ll be using one that looks like this:
Screenshot 2024-11-25 231729

local mainUI : ScreenGui = path.to.your.ScreenGui

local screenGen = require(path.to.Screen3D.module)

local screen3D = screenGen.new(mainUI,5) --the second argument will be the distance of all surfaceguis from the camera

It looks like it did nothing, but after creating a Screen3D, every GuiObject inside it will be transformable into 3D UI.

Next up, using a 2D GuiObject (Frame, TextLabel, TextButton, ect…), get its corresponding Component3D:

local GuiObject2D = mainUI:WaitForChild('Frame')

local Component3D = screen3D:GetComponent3D(GuiObject2D)

Component3D:Enable()

For performance reasons, 2D UI objects aren’t converted into 3D UI until explicitly told to, with Component3D:Enable(). This should only be called once and can be reversed with Component3D:Disable().

Although the UI looks the same, if you look at the explorer you’ll notice that your GuiObject is now a SurfaceGui!

Screenshot 2024-11-25 231709

OFFSETS 101

Let’s mess with our new 3D component a little!

All component3Ds have an offset which can be both used to rotate and move them relative to their parent UI elements.

NOTE: offset pivots UI elements on their anchorpoint.

For example:
You can set the offset to some static value:

local mainUI : ScreenGui = path.to.your.ScreenGui
local screenGen = require(path.to.Screen3D.module)
local screen3D = screenGen.new(mainUI,5)

local GuiObject2D = mainUI:WaitForChild('Frame')
local Component3D = screen3D:GetComponent3D(GuiObject2D)
Component3D:Enable()


Component3D.offset = CFrame.Angles(0,math.rad(10),0) --rotates your 3D UI element by 10 degrees

image

and/or change it to your heart’s content:

local mainUI : ScreenGui = path.to.your.ScreenGui
local screenGen = require(path.to.Screen3D.module)
local screen3D = screenGen.new(mainUI,5)

local GuiObject2D = mainUI:WaitForChild('Frame')
local Component3D = screen3D:GetComponent3D(GuiObject2D)
Component3D:Enable()


game:GetService('RunService').RenderStepped:Connect(function()
    Component3D.offset = CFrame.Angles(0,math.sin(tick())/2,0) --spins your UI from -45 to +45 degrees
end)

3D UI NESTING

Now this is cool and all… But what if you put a SECOND 3D UI element inside of it and changed ITS offset too?

local mainUI : ScreenGui = path.to.your.ScreenGui
local screenGen = require(path.to.Screen3D.module)
local screen3D = screenGen.new(mainUI,5)

local GuiObject2D = mainUI:WaitForChild('Frame')
local InnerGuiObject = GuiObject2D:WaitForChild("A")

local Component3D = screen3D:GetComponent3D(GuiObject2D)
Component3D:Enable()

local InnerGuiObject3D = screen3D:GetComponent3D(InnerGuiObject)
InnerGuiObject3D:Enable()

InnerGuiObject3D.offset = CFrame.Angles(0,math.rad(25),0) --i chose to keep the offset static here for the sake of simplicity

game:GetService('RunService').RenderStepped:Connect(function()
	Component3D.offset = CFrame.Angles(0,math.sin(tick()*5)/4,0) --sped up for the sake of the demo
end)

Nothing breaks down and everything works as expected!

The framework also tries to match the original 2D tree as much as possible, without compromising on usefulness:
image

This UI nesting can also be scaled essentially forever:

You can also nest 3D components within 2D elements, meaning you don’t have to :Enable() an entire tree of instances just to rotate one 3D frame.

play around with the demo world here:

3d_v2.rbxl (65.9 KB)
[OUTDATED]

or just grab the rbxm from github:

This module (along with an older, messier 3D module) is regularly used in unorthodox accounting and im finally allowed to publish it yippeee

Glory to goog.
-sun_6x and yqat

68 Likes

This looks so cool! Ill keep this in mind for my next project!

3 Likes

i love this! now it’s even easier to make 3d ui. and give it and the games that use it some recognition! I always wanted to learn to make 3d ui and now i dont have to learn (im lazy)

2 Likes

I wish I could like, nominate this for best resource of the year :sob:

5 Likes

is there any way we can curve the gui like even a little bit. like slightly indent it

1 Like

if you mean something like this:
image

-Set your 2D UI up. You can make it look however you want.
image

Since we’re gonna turn our UI element on the top right corner, SET THE ANCHORPOINT OF THE 3D PART YOU WANT TO ROTATE (InfoContainer) TO 1,0
image

Then, just turn it into a 3D component and rotate its offset:

local mainUI : ScreenGui = script.Parent
local screenGen = require(game.ReplicatedStorage:WaitForChild('Screen3D')) --can be any path
local screen3D = screenGen.new(mainUI,5)

local GuiObject2D = mainUI:WaitForChild('InfoContainer')
local Component3D = screen3D:GetComponent3D(GuiObject2D)
Component3D:Enable()


Component3D.offset = CFrame.Angles(0,math.rad(-8),0) --rotates your 3D UI element by 8 degrees

The end result looks something like this:
image

You can tweak the offset to get different “curvatures” or add a position component to it for “indentations”:

local mainUI : ScreenGui = script.Parent
local screenGen = require(game.ReplicatedStorage:WaitForChild('Screen3D')) --can be any path
local screen3D = screenGen.new(mainUI,5)

local GuiObject2D = mainUI:WaitForChild('InfoContainer')
local Component3D = screen3D:GetComponent3D(GuiObject2D)
Component3D:Enable()


Component3D.offset = CFrame.new(0,0,-0.1) * CFrame.Angles(0,math.rad(-6),0) -- indents your UI by 0.1 studs, then rotates it by 6 degrees

You can mess around a little and see how different indentations affect your UI:

local screenGen = require(game.ReplicatedStorage:WaitForChild('Screen3D'))
local screen3D = screenGen.new(mainUI,5)

local GuiObject2D = mainUI:WaitForChild('InfoContainer')
local Component3D = screen3D:GetComponent3D(GuiObject2D)
Component3D:Enable()

game:GetService('RunService').RenderStepped:Connect(function()
	Component3D.offset = CFrame.new(0,0,math.sin(tick())/2) * CFrame.Angles(0,math.rad(-6),0)
end)```
1 Like

it doesn’t seem curved unfortunately

1 Like

Not sure what you mean by “curved”. Can you show me an example?

2 Likes
1 Like

Unfortunately, that can’t be done on Roblox without a lot of trickery. The engine CAN support it, but Roblox has it locked behind a high level of ScriptSecurity.

2 Likes

this reminds me of my attempt to make main menu GUI in 3D form, honestly I’ve been wanting to make 3D GUI move along with camera as that’s what i was doing back then but lerping was pain,…,.,.,…,.

i can def giv this a try

2 Likes

how trickery

1 Like

Create a bunch of parts to represent the ui. Get ui that you want to curve, and slice it up. Use surfaceguis to paste those slices onto the parts. Boom, you have psuedo-curved gui, now good luck with the math needed for making the curved gui interactive, and have fun choosing between performance+ease of use vs fidelity.

2 Likes

wouldnt that need like a big ton of parts?

at that point the whole game would just be the curved gui :sob:

1 Like

Cool, cool stuff! Good job on this, very helpful!

2 Likes

patched a bunch of offset inconsistencies:

1 Like

What’s the high context level API you’re referring to?

1 Like

Most likely they are referring to SurfaceGui.HorizontalCurvature and SurfaceGui.Shape

3 Likes

Ow im glad you found an unique thing , spread it out so you have to power Roblox’s imagination again
This was avaliable on some apps
I bookmark this :saluting_face:

2 Likes

more bugfixes:

1 Like