Goal: I am trying to have a gui show over a part using world to screen point. It cant be a billboard because I plan to do something else with it after that a billboard would not allow me to do.
Problem: When the part goes off my screen, the GUI should also go off my screen, which it does sort of. However, as I get further away from the block, it reappears at the center of my screen opposite the part. I cant seem to figure out why.
Code:
local camera = workspace.CurrentCamera
local worldPoint = workspace.Target.Position
while wait() do
local vector = camera:WorldToScreenPoint(worldPoint)
script.Parent.ImageLabel.Position = UDim2.new(0,vector.X,0,vector.Y)
end
Update: So I now understand why it is behaving like this, but I still dont know how to eliminate this behavior without just hiding the UI when the part is behind me.
That would work for this, but it would be problematic for what im adding. I was going to clamp the values of the frame’s position to make it appear around the border of the screen when the part was off the screen. This would show the player what direction they were facing in relation to the part. If I hide the UI when the part is behind them, this wont work.
WorldToScreenPoint returns 2 values, the screen space vector, and a bool that tells you if the 3d space position is on the screen. You could check if OnScreen is false and then do the clamping math for when the part is off screen
local vector, onScreen = camera:WorldToScreenPoint(worldPoint)
if onScreen then
script.Parent.ImageLabel.Position = UDim2.new(0,vector.X,0,vector.Y)
else
-- clamp to edge
end
It would probably have to be a little bit more complex than just clamping the values
I would do a ray-AABB intersection from the middle of the screen like this:
red dot is WorldToScreenPoint position
black rect is the screen
blue dot is what you would need to compute
There are lots of resources online for line-AABB intersection code, this video for example or this article (but drop the check on the “arrow end” of the line), or just google “2d ray aabb intersection”
This behavior is intended. Think about it, if you dont have the z axis anymore (depth), how can you determine if its infront or behind? This code simply checks which pixel the position of that object collides with the camera viewport. In order to accomplish that effect you’re going for, its going to require a little bit more math so I’ll get back to you when I get it working.
I believe this works, however im on mobile so you’ll have to test it for yourself:
local vector, onScreen = camera:WorldToViewportPoint(worldPoint)
if vector.Z <= 0 then
vector *= -1
end
local viewportSize = workspace.CurrentCamera.ViewportSize
vector.X = math.clamp(vector.X, 0, viewportSize.X)
vector.Y = math.clamp(vector.Y, 0, viewportSize.Y)
script.Parent.ImageLabel.Position = UDim2.new(0, vector.X, 0, vector.Y)
end
note: I HIGHLY suggest you run this in a BindToRenderStep function with a RenderPriority that runs AFTER the camera has been CFramed, or Enum.RenderPriority.Camera.Value + 1 to remove the lagging effect visible in the gifs you posted.