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

How to approach a Player Offscreen indicator?

Asked by 6 years ago

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

Log in to vote
0
Answered by 6 years ago
Edited 6 years ago

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) 
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, :) wantsome555 0 — 6y
Ad

Answer this question