ScreentoViewPoint Markers

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

Video:

Download

Tutorial:


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.
image

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).

image

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.

image

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
    table.insert(Markers,{Sample,v})
end

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
        else
            return math.clamp(x,0,Screen.X-Absolute.X),Screen.Y - Absolute.Y
        end
    else
        if x < (Screen.X - x) then
            return 0,math.clamp(y,0,Screen.Y-Absolute.Y)
        else
            return Screen.X - Absolute.X,math.clamp(y,0,Screen.Y-Absolute.Y)
        end
    end
end
 
game:GetService("RunService").RenderStepped:Connect(function()
    for i,v in pairs(Markers) do
        local Marker = v[1]
        local Location = v[2]
        if not Marker == nil or Location == nil then
            table.remove(Markers,i)
        end
        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)
            else
                if MarkerPositionX < 0 then
                    MarkerPositionX = 0
                elseif MarkerPositionX > (Screen.X -MarkerAbsolute.X)  then
                    MarkerPositionX = Screen.X - MarkerAbsolute.X
                end
                if MarkerPositionY < 0 then
                    MarkerPositionY = 0
                elseif MarkerPositionY > (Screen.Y -MarkerAbsolute.Y)  then
                    MarkerPositionY = Screen.Y - MarkerAbsolute.Y
                end
            end
               
            Marker.Position = UDim2.new(0,MarkerPositionX,0,MarkerPositionY)
            Marker.RotateLabel.Visible = not inViewport
            Marker.RotateLabel.Rotation = 90+math.deg(math.atan2(MarkerRotation.Z, MarkerRotation.X))
        end
    end
end)

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.

89 Likes

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

5 Likes

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

2 Likes

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

2 Likes

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

1 Like

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

2 Likes

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

4 Likes

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

1 Like

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
1 Like

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!

2 Likes

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.

I know I’m kind of late but I really dislike how I’m forced to subscribe to your YT channel or follow your Twitter to get a repro. In fact, I absolutely despise people who do this.

Since this is a public tutorial I think the repro should be public too. I’m finding it hard to follow along without being able to see exactly how you set everything up.

I don’t want to be rude-- this looks like a great resource. But I shouldn’t be forced to follow you to get a repro.

4 Likes

I agree with this completely. If it is a community resource, why should we be forced to subscribe to his channel and follow his twitter to access this.

2 Likes

I actually didn’t notice at first he had a repo, even moreso you had to sub to get it which should be against the rules.

I do this so people that watch the video are more prone to watch and listen to me how I explain the different functions. Aswell people that are in my discord can just access the files. But the boost.ink was just a test to see if it would increase the count