Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

How to approach a Player Offscreen indicator?

Its work until the camera is behind you, i honestly don't know how to re approach this, so i'm asking the people!

this is the script below,

testLabel is the indicator testLabel2 is where this object is on the screen

local runService = game:GetService("RunService")
local players = game:GetService("Players")

local player = players.LocalPlayer
local mouse = player:GetMouse()

local camera = game.Workspace.CurrentCamera

local gui = script.Parent
local frame = gui:WaitForChild("Frame")

local testObject = game.Workspace:WaitForChild("Player 123")
local testLabel = frame:WaitForChild("TextLabel")
local testLabel2 = frame:WaitForChild("OnScreenPos")

runService.RenderStepped:connect(function()
	local screenPos, show = camera:WorldToScreenPoint(testObject.CFrame.p)
	local editedPos = UDim2.new(0,0,0,0) --z = the side of screen
	local behiden = screenPos.z < 0 and true or false

	if behiden then
		--screenPos = screenPos * -1
	end
	
	local y = screenPos.y
	y = math.clamp(y, 0,gui.AbsoluteSize.Y)
	local x = screenPos.x	
	x = math.clamp(x, 0,gui.AbsoluteSize.X-10)
	
	if not show then
		if screenPos.X <= 0 then
			editedPos = UDim2.new(0,0,0,y)
		elseif screenPos.X >= gui.AbsoluteSize.X then
			editedPos = UDim2.new(1,-10,0,y)
		end
		if screenPos.Y <= 0 then
			editedPos = UDim2.new(0, x, 0, 0)
		elseif screenPos.Y >= gui.AbsoluteSize.Y then
			editedPos = UDim2.new(0, x, 1, -10)
		end
	end
	
	testLabel.Position = editedPos
	testLabel2.Position = UDim2.new(0,screenPos.x,0,screenPos.y)
end)

1 answer

✓ Accepted answer

I did some looking around and found this Unity forum post https://forum.unity.com/threads/camera-worldtoscreenpoint-bug.85311/ The final post by Alturis2 had some C# code that looked like it would do what you need, so I converted it to Lua and tried it out and it seems to work really well. The ScreenPointEdgeClamp function even returns a rotation value so that you can rotate the offscreen indicator to point to the object being tracked as well as follow it around the edge of the screen.

In the following code, I set things up how I think you have them - ScreenGui/Frame/2 TextLabels, with the LocalScript parented to the ScreenGui. You can obviously use ImageLabels or whatever you want. I also commented in the code where you can adjust how far from the edge of the screen you want the offscreen indicator. Hope this helps.

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local mouse = Players.LocalPlayer:GetMouse()
local camera = workspace.CurrentCamera
local gui = script.Parent
local frame = gui:WaitForChild("Frame")
local testObject = workspace:WaitForChild("Part")
local onScreenLabel = frame:WaitForChild("OnScreenPos")
local offScreenLabel = frame:WaitForChild("OffScreenPos")
-- Change this value to determine how far the offscreen indicator remains from the sides of the screen
local edgeBuffer = 30

local function Dot(v1, v2)
	return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z
end

local function WorldToScreenPointProjected(worldPos)
	local camNormal = camera.CFrame.lookVector
	local vectorFromCam = worldPos - camera.CFrame.p
	local camNormDot = Dot(camNormal, vectorFromCam)
	if camNormDot <= 0 then
		-- Object is behind the camera forward facing place. Project the position in front of the plane
		local proj = camNormal * camNormDot * 1.01
		worldPos = camera.CFrame.p + (vectorFromCam - proj)
	end
	return camera:WorldToScreenPoint(worldPos)
end

local function ScreenPointEdgeClamp(screenPos, buffer)
	-- Take the direction of the screen point from the screen center to push it out to the edge of the screen
	-- Use the shortest distance from projecting it along the height and width
	local screenCenter = Vector2.new(gui.AbsoluteSize.X / 2, gui.AbsoluteSize.Y / 2)
	local screenDir = (screenPos - screenCenter).Unit
	local angleRad = math.atan2(screenDir.X, screenDir.Y)
	local distHeight = math.abs((screenCenter.Y - buffer) / math.cos(angleRad))
	local distWidth = math.abs((screenCenter.X - buffer) / math.cos(angleRad + (math.pi * 0.5)))
	local dist = math.min(distHeight, distWidth)
	local angleDeg = -math.deg(angleRad)
	return screenCenter + (screenDir * dist), angleDeg
end

RunService.RenderStepped:connect(function()
	local position, isVisible = WorldToScreenPointProjected(testObject.Position)
	if not isVisible then
		onScreenLabel.Visible = false
		offScreenLabel.Visible = true
		local clampedPosition, angle = ScreenPointEdgeClamp(Vector2.new(position.X, position.Y), edgeBuffer)
		offScreenLabel.Position = UDim2.new(0, clampedPosition.X - offScreenLabel.Size.X.Offset / 2, 0, clampedPosition.Y - offScreenLabel.Size.Y.Offset / 2)
		offScreenLabel.Rotation = angle
	else
		onScreenLabel.Visible = true
		offScreenLabel.Visible = false
		onScreenLabel.Position = UDim2.new(0, position.X - onScreenLabel.Size.X.Offset / 2, 0, position.Y - onScreenLabel.Size.Y.Offset / 2)
	end
end) 

1 comment

0

See i found this as well, i just wasnt smart enough to convert it over, but with your help i understand now, Thank you so much, :)

This discussion is preserved as it was. New answers, comments, and votes are closed.