I’ve made a really useful video explaining how Mad City created their point markers!
Video:
Download
https://www.dropbox.com/s/0v2er3159sfh7ma/Tutorial.rbxl
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.
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
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.