is there a way to rename the surfacegui that the module makes?
I also use custom camera script and had similar issue, I think it happens when you’re using RenderStepped to move the camera instead of BindToRenderStep (and the engine just randomly picks either to move the screen3d or the camera first)
-- Instead of this
RunService.RenderStepped:Connect(camera)
-- Use this
RunService:BindToRenderStep("camera",250,camera)
The difference I got
grrr the new version uses BindToRenderStep!!!
also wait arent you the guy who made that one rhythm game
if component.surfaceGui then
component.surfaceGui.Name = 'thingy'
end
Hello, I love your module and have used it for my game. But I do have a question, I have an ImageLabel thats on top of all the 3D UI and I’m wondering if there is a way to get them in front or brighter then the helmet UI.
The two selected in this image are the 3D Frames
Thanks :3
Ok, I just added a 3D component to it with a farther display distance lol
Hello! This module is absolutely wonderful, and every developer should know about it. But… I did find a glitch when I set up some UI in the place you provided.
A few of the smaller frames are shaking wildly and sometimes flinging out of place, which is incorrect. I was hoping you could take a look! Only the UI was changed.
Screen 3D Bug.rbxl (63.4 KB)
I’ll try debugging it on my own for now.
(edit): The bug happens even if you only Enable the components, not move them. If you don’t enable them, they stay in place as 2D UI.
.this is fixed in the latest version of screen3d!!!
this is cause we used renderstepped instead of bindtorenderstep and that had a bunch of weird side effects
edit: nevermind
The issue is caused inside the pivotAroundPoint
method in Component3D. Even when the rotation from self.offset
is a blank CFrame, it returns incorrect results when it should have returned the original point. All of the frames experience the glitch, but it adds up and becomes visible around 8 frames in.
Continue to consider using BindToRenderstep
though. I’m surprised it doesn’t stutter when you turn your camera without this, but that’s not important right now.
grrr the new version switched to BindToRenderstep!!!
i’ll fix the pivotAroundPoint
method eventually
Cool, but would this actually fix the issue? It wouldn’t make sense to me if it did.
update: seems to be a floating point precision issue
It’s more complicated than it seems. There are floating point issues that get scaled dramatically, to the point where there’s shaking caused by minute changes in how the CPU does math on small numbers over time. Using Quaternions may or may not fix the issue, but it may be too imperformant.
Anyways, if this is not possible to fix, it’s not a huge deal. I doubt anybody needs that many frames nested for their UI. I’m trying to refine this module and add more features that it missed, like accounting for frame.Visible
.
These are actually used in the VR UI apparently.
Where were you finding that these were supposedly beta features at one point?
Hello, I can’t wait to start using this resource in my games! The module was quite messy though, so I felt the need to improve it first. I’m sharing my improved version here!
CHANGES:
- Fixes OOP approach
- Cleans entire codebase
- Adds better type definitions
- All public fields are PascalCase
- Adds
screen:BindToRenderStep
- Adds
component.ZOffsetBehavior
[0] - Reduces unnecessary methods and code
- Screen3D instances can now be destroyed
-
component.Offset.Position
behavior change [1] - Condenses wasted space in code (excessive new lines)
- A ScreenGui cannot be controlled by two Screen3D at once
- Errors when calling methods of a destroyed Screen3D instance
- Layers render in the correct order even when parts are far back [2]
- SurfaceGui becomes invisible when an ancestor GuiObject is invisible
Footnote 0
You may now choose how the ZOffset is calculated. When set to Ancestory
, it determines the offset based on the hierarchy. When set to Position
, the ZOffset is 0, leaving the UI’s 3D position to determine the rendering order.
Footnote 1
offset.X
and offset.Y
are scales to the component’s part stud size. offset.Z
is a scale to the display distance. This ensures that the 3D UI looks the same at all distances, making it more intuitive to use.
Screen3D.rbxl (64.9 KB)
local time = 0
screen:BindToRenderStep(function(dt)
for i, v in screen.Components do
if i == 1 then continue end
v.Offset = CFrame.new(math.sin(time) / 2, 0, 0)
end
time += dt
end)
Footnote 2
The previous ZOffset
calculation was correct, but it didn’t account for the depth of each surfacePart. When one part had a higher offset than another, it would render above it. However, if it was further back, it will render behind it. The ZOffset is in studs, not a ZIndex. These values are now multiplied by a high number to undo this behavior.
Here’s the original example using this version.
Screen3D.rbxl (64.5 KB)
Source Code
local Replicated = game:GetService("ReplicatedStorage")
--\\ Modules
local Screen3D = require(Replicated:WaitForChild("Screen3D"))
--\\ Private Fields
local screen = Screen3D.new(script.Parent,5)
--\\ Connections
for _, component in screen.Components do
component:Enable()
end
screen:BindToRenderStep(function()
local angle = -(math.sin(tick() * 2) * 0.5 + 0.5) * 0.1
for _, component in screen.Components do
component.Offset = CFrame.Angles(angle, angle, angle)
end
end)
The floating-point issue is not fixed here. Don’t nest too many components and you won’t see it! :3
that is a REALLY cool improved version
it be perfect if it had the 2.0 screen3d’s features like support for 2D UI modifiers (this is toggleable on my version since it comes with the tradeoff of the descendant tree looking a little weird) and fov independence for component.Offset.Rotation
, kinda like what you did with the component.Offset.Position
behavior change
May you explain these with a bit more detail? I wouldn’t mind adding anything since I want to use this module for my own game!
Added a RotationBehavior
enum to this module! The videos below show the different behaviors on the button.
Local
Screen
World
for _, component in screen.Components do
if component.Object:IsA("TextButton") then
component.RotationBehavior = Screen3D.RotationBehavior.Screen
end
component:Enable()
end
Screen3D Rev 2.rbxl (68.4 KB)
(edit 4/16/2025): Here’s another version with a small change.
It adds ZOffsetBehavior.ZIndex
to predictably fix layering issues without needing to restructure your hierarchy. Could have sworn I added this before, but that change must have gotten lost when I was trying and failing to do the rotation behavior…! The SurfaceGui’s ZOffset will be the object’s ZIndex. Who knew!
Screen3D Rev 3.rbxm (6.2 KB)
On the original screen3d you can enable UI elements in “compatibility mode”:
When a 2d element gets turned 3d using :EnableCompatibility(), screen3d creates a new frame in the place of that old 2d element to keep stuff like UIListLayout and UIPadding from completely falling apart
This workflow isn’t intuitive to me. Can’t you just nest the 3D component with a frame yourself? It would be much more predictable this way, and it doesn’t change the UI hierarchy further. You would have to manually code to get AutomaticSize behavior this way, but that’s not a big deal.