ScreentoViewPoint Markers


I’ve made a really useful video explaining how Mad City created their point markers!



How the markers work:
In ReplicatedStorage there’s a folder called ‘Markers’ and in the folder theirs a part, this part is the position of the marker. In each location there’s a folder called Config, this holds the information about each marker such as if it’s enabled, the icon and the icon colour.

In the ScreenGui there is one local script and a frame called ‘Holder’, the ‘Holder’ is placed in the centre of the screen and has it provides the distance from each of the sides of the screen (screen size).


The sample is a 0.05 sized circle with a UIAspectRatioConstraint to keep it square. Then, we’ve got a frame called ‘RotateLabel’. This contains the image with the arrow pointing toward the location. As well, it contains the icon of the location that you’ve set in the ‘Config’ folder.


This goes through the folder ‘Markers’ and makes a label in the ‘Holder’ frame and stores the location and label inside a table called ‘Markers’.

local Markers = {}
local Screen = script.Parent.Holder.AbsoluteSize
local T = game:GetService("TweenService")
for i,v in pairs(game.ReplicatedStorage.Markers:GetChildren()) do
    local Sample = script.Sample:Clone()
    Sample.Parent = script.Parent.Holder
    Sample.ImageColor3 = v.Config.IconCollor.Value
    Sample.RotateLabel.Arrow.ImageColor3 = v.Config.IconCollor.Value
    Sample.Icon.Image = v.Config.Icon.Value

We’re using a RenderStepped loop and go through the list of markers. First, we’re checking if the marker is enabled, if it isn’t it will not render. Then we’re calculating where the position is into 2D space. If you look away normally it would still show the marker as it was there. That’s why we’re checking the Z value of the Vector3 we’re receiving. Then we’re putting the X, Y and absolute size into the function ‘findClosestBorderPoint’. This will clamp the value to the left, right, top or bottom sides so it will not show inside the middle of your screen. If we’re facing the marker then we’re preventing it from going away from the edge of the screen. As well, we’re using the value called ‘InViewport’ to show the arrow when you’re not facing the location.

function findClosestBorderPoint(x,y,Absolute)
    x = Screen.X - x
    y = Screen.Y - y
    local distanceToYBorder = math.min(y,Screen.Y-y)
    local distanceToXBorder = math.min(x,Screen.X-x)
    if distanceToYBorder < distanceToXBorder then
        if y < (Screen.Y - y) then
            return math.clamp(x,0,Screen.X-Absolute.X),0
            return math.clamp(x,0,Screen.X-Absolute.X),Screen.Y - Absolute.Y
        if x < (Screen.X - x) then
            return 0,math.clamp(y,0,Screen.Y-Absolute.Y)
            return Screen.X - Absolute.X,math.clamp(y,0,Screen.Y-Absolute.Y)
    for i,v in pairs(Markers) do
        local Marker = v[1]
        local Location = v[2]
        if not Marker == nil or Location == nil then
        Marker.Visible = Location.Config.Enabled.Value
        if Location.Config.Enabled.Value then
            local MarkerPosition, inViewport = workspace.CurrentCamera:WorldToScreenPoint(Location.Position)
            local MarkerRotation = workspace.CurrentCamera.CFrame:inverse()*Location.CFrame
            local MarkerAbsolute = Marker.AbsoluteSize
            local MarkerPositionX = MarkerPosition.X - MarkerAbsolute.X/2
            local MarkerPositionY = MarkerPosition.Y - MarkerAbsolute.Y/2
            if MarkerPosition.Z < 0 then
                MarkerPositionX,MarkerPositionY = findClosestBorderPoint(MarkerPositionX,MarkerPositionY,MarkerAbsolute)
                if MarkerPositionX < 0 then
                    MarkerPositionX = 0
                elseif MarkerPositionX > (Screen.X -MarkerAbsolute.X)  then
                    MarkerPositionX = Screen.X - MarkerAbsolute.X
                if MarkerPositionY < 0 then
                    MarkerPositionY = 0
                elseif MarkerPositionY > (Screen.Y -MarkerAbsolute.Y)  then
                    MarkerPositionY = Screen.Y - MarkerAbsolute.Y
            Marker.Position =,MarkerPositionX,0,MarkerPositionY)
            Marker.RotateLabel.Visible = not inViewport
            Marker.RotateLabel.Rotation = 90+math.deg(math.atan2(MarkerRotation.Z, MarkerRotation.X))

That’s a basic understanding of how this script works, if you’d like to see more about this marker system please check out the video above.

GUI Icon Constraint
closed #2

This topic was automatically closed after 1 minute. New replies are no longer allowed.

opened #3


This is really awesome. I was wondering how they did it. Thanks!


Same here, I was very curious how they did that, thank you


Awesome work and nice video dutch! I enjoyed helping you solve the bugs in the code :wink:


Thank you for sharing how Mad City created their point markers.


I am thinking of all the things I can do with this knowledge right now!!!


This is awesome. Thank you so much for sharing this tutorial.


Good job, keep up the good work man :heart:


I realize it’s not quite as smooth as the one in Mad City, although you did a great job.

For example, it doesn’t “hug” the border of your screen well. It snaps between the sides and bottom of your screen sometimes. I think it’s because of this line

 if distanceToYBorder < distanceToXBorder then


Could you explain what you mean?


In the function findClosestBorderPoint(x,y,Absolute) you provide, it will inaccurately return the math needed to move along the Markers frame you store all the indicators. Specifically, when the inViewport boolean is false while moving your camera around; the indicator will jump corners rather than a smooth transition along the Markers frame or “hugging”. Not as smooth as Mad City’s like @ninja900500 pointed out. Still a cool marker system, I absolutely love it!


You can see this effect if you on on mad city and try to get the icons to your bottom right corner of your screen. You can see that it smoothly slides along the edge of your screen, where as yours does not replicate this effect.

I’m still trying to figure out why.