Radar system on ScreenGui

Let’s create a RadarFrame, which ClipsDescendant (so it cuts off points outside the frame).
Let’s also create a RadarPoint, which indicates a ‘thing’ (but also, the center of the radar to indicate the player).

Time for some maths and logic. Here’s what we’ll need to do.

  1. We need to store all objects which should show up on the radar, including the origin (the player)
  2. We need to get the offset from the origin to every point, every frame
  3. We need to convert from 3 dimensions (the world) to 2 dimensions (the radar). We’re going to do this by simply ignoring the up-axis. Note: Roblox uses Y as the up axis, so from the 3D world we’ll be using X,Z positions but in the 2D radar we’ll be using X,Y where Y is exchanged for our Z (but still called Y)
  4. We need to multiply this distance by some kind of ratio. For instance, our radar could be 1:200, which means we should multiply by 200. This is our final offset.
  5. We need to set every point to the originPoint’s position, plus the offset

Let’s create a folder in workspace called Points, in which each child (parts) will be a point. Let’s also store our character, and add a wait (for loading purposes, this is a very crude solution)

Let’s write a function getOffset where we get the offset from origin to object. To do this, we have to get the object’s position and subtract the origin position

Let’s also add a radarPoint for every object. Of course we already have 1 radarpoint for the player, in the RadarFrame by default. This point is just centered.

wait(3)
local Objects = game.Workspace.Points:GetChildren()
local Origin = game.Players.LocalPlayer.Character.HumanoidRootPart

local RadarFrame = game.Players.LocalPlayer.PlayerGui.ScreenGui.RadarFrame
local RadarPoints = {}
for i,v in ipairs(Objects) do
	local newPoint = game.ReplicatedStorage.RadarPoint:Clone()
	newPoint.Parent = RadarFrame
	table.insert(RadarPoints,newPoint)
end

local function getOffset(obj)
	local xOrigin,yOrigin = Origin.Position.X,Origin.Position.Z -- Note we can use the Z position as our Y, so we don't have to deal with it later
	local xTarget,yTarget = obj.Position.X,obj.Position.Z
	
	local xOffset,yOffset = xTarget-xOrigin,yTarget-yOrigin
	return Vector2.new(xOffset,yOffset)
end

Let’s add the gameloop. In the loop we will get every object, and print their offset. For now I will just use 1 object, so that it’s easy to debug.

game:GetService("RunService").RenderStepped:Connect(function(dt)
	for i,v in ipairs(Objects) do
		print(getOffset(v))
	end
end)


So it works, and you can imagine that if my character is the dot, then we have to move offset to get to the point. Let’s translate this to the UI.

First, we’ll need to decide on a ratio. Let’s do 1:2. So our convertedOffset = offset*2. Then we get the radarPoint, and add this to it’s position. Of course, UI elements work with UDim’s (not Vector2’s) so we’ll need to keep that in mind. A centered point will have a position of Udim2.new(0.5,0,0.5,0). We can simply replace the offset values with our new calculated ones.

game:GetService("RunService").RenderStepped:Connect(function(dt)
	for i,v in ipairs(Objects) do
		print(getOffset(v))
		local convertedOffset = getOffset(v)
		local radarPoint = RadarPoints[i]
		radarPoint.Position = UDim2.new(0.5,convertedOffset.X,0.5,convertedOffset.Y)
	end
end)

Uploading: 6721e95735e4efc88c6dba6876566706.gif…

Now a problem arises: the radar needs to be rotated in the direction of the camera, otherwise it’s not very useful. Okay, time for some more math.

We’re going to get the angle between the up-vector and the direction you’re looking in. The red is the up vector. The blue is what we’re going to calculate. At angle 0, the radar should be pointing directly ‘north’. At 180, it’s pointing south. In the example, the blue is at 130 degrees (this is just to give you an example)

We do this by getting the vector from the camera to the player. This is the direction you’re looking in, because the player is always in the center of the screen (if you move your camera from the player in a cutscene, this’ll break until it’s back).

local function updateRadar()
	local relativePosition = Origin.Position - Camera.CFrame.Position
	local angle = math.atan2(relativePosition.z, relativePosition.x) -- Calculate angle

	RadarFrame.Rotation = -(math.deg(angle) + 90) -- Set UI rotation 


end

So after ever radarPoint has been calculated we updateRadar().
The result is this.
6b0f9c11448fd6c6616b704d10e61bea

Edit:
As per personal request, here’s the file. I did make some adjustments later, namely that the dots are the same size as the objects, and take their colour.

RadarTest.rbxl (59.5 KB)

30 Likes

You’re a life saver. Thank you for skipping me some head aches.

5 Likes

Thank you man, you have no idea how long I’ve been looking for something like this

2 Likes

Sorry to necrobump but Thank you!

Is there any additional tutorial for this as how to clamp an Icon to the border if within a Magnitude?

wait(1)  -- Wait for 1 second to ensure all necessary elements are loaded

local Objects = game.Workspace.Points:GetChildren()  -- Get all the child objects under the Points folder in the workspace
local Origin = game.Players.LocalPlayer.Character.HumanoidRootPart  -- Get the player's character HumanoidRootPart
local Camera = workspace.CurrentCamera  -- Get the current camera in the workspace

local RadarFrame = game.Players.LocalPlayer.PlayerGui.ScreenGui.RadarFrame  -- Get the RadarFrame UI element from the player's ScreenGui
local RadarPoints = {}  -- Initialize an empty table to store radar points
local RadarRadius = math.min(RadarFrame.Size.X.Offset, RadarFrame.Size.Y.Offset) / 2  -- Calculate the radar's radius based on the smallest dimension of the RadarFrame

-- Loop through each object in the Points folder
for i, v in ipairs(Objects) do
    local newPoint = game.ReplicatedStorage.RadarPoint:Clone()  -- Clone a RadarPoint from ReplicatedStorage
    newPoint.Parent = RadarFrame  -- Set the RadarFrame as the parent of the new radar point
    newPoint.Size = UDim2.new(0, v.Size.X * 5, 0, v.Size.Z * 5)  -- Set the size of the radar point based on the object's size
    newPoint.BackgroundColor3 = v.Color  -- Set the radar point's color to match the object's color

    table.insert(RadarPoints, newPoint)  -- Add the new radar point to the RadarPoints table
end

-- Function to calculate the offset of an object relative to the origin
local function getOffset(obj)
    local xOrigin, yOrigin = Origin.Position.X, Origin.Position.Z  -- Get the X and Z positions of the origin
    local xTarget, yTarget = obj.Position.X, obj.Position.Z  -- Get the X and Z positions of the target object

    local xOffset, yOffset = xTarget - xOrigin, yTarget - yOrigin  -- Calculate the offset between the target and the origin
    return Vector2.new(xOffset, yOffset)  -- Return the offset as a Vector2
end

-- Function to clamp the offset to the radar border if it exceeds the maximum distance
local function clampToRadarBorder(offset, maxDistance)
    local distance = offset.magnitude  -- Calculate the magnitude (distance) of the offset
    if distance > maxDistance then  -- If the distance exceeds the maximum distance
        offset = offset.unit * maxDistance  -- Clamp the offset to the maximum distance
    end
    return offset  -- Return the clamped offset
end

-- Function to update the radar's rotation based on the camera's position
local function updateRadar()
    local relativePosition = Origin.Position - Camera.CFrame.Position  -- Calculate the relative position of the origin to the camera
    local angle = math.atan2(relativePosition.z, relativePosition.x)  -- Calculate the angle based on the relative position

    RadarFrame.Rotation = -(math.deg(angle) + 90)  -- Set the radar frame's rotation (adjusted as needed)
end

-- Connect a function to the RenderStepped event, which runs every frame
game:GetService("RunService").RenderStepped:Connect(function(dt)
    -- Loop through each object in the Points folder
    for i, v in ipairs(Objects) do
        local convertedOffset = getOffset(v) * 2  -- Get the offset and scale it
        local radarPoint = RadarPoints[i]  -- Get the corresponding radar point

        local withinDistance = false  -- Initialize a flag to check if the object is within display distance

        local displayMagnitude = v:GetAttribute("DisplayWithinMagnitude") or RadarRadius  -- Get the display magnitude attribute or use the radar radius
        if convertedOffset.magnitude <= displayMagnitude then  -- If the object is within display distance
            withinDistance = true  -- Set the flag to true
        end

        -- If the offset magnitude exceeds the radar radius, clamp it
        if convertedOffset.magnitude > RadarRadius then
            convertedOffset = clampToRadarBorder(convertedOffset, RadarRadius)
        end
        
        radarPoint.Visible = withinDistance  -- Set the visibility of the radar point
        radarPoint.Position = UDim2.new(0.5, convertedOffset.X, 0.5, convertedOffset.Y)  -- Update the radar point's position
    end
    
    updateRadar()  -- Update the radar's rotation
end)

So points that are displayed, even when not in range, they require an attribute ‘DisplayWithinMagnitude’. This is the max distance. The radar radius is 150, so it should be higher than that. In my case it’s 200.

  1. Check if the length of convertedOffset (that is, the offset from the origin of the radar, the humanoidrootpart, to the object) is smaller/equal with the displaymagnitude. The displaymagnitude is either the attribute, or it defaults to the radar radius (which means it won’t be displayed outside the radar).
  2. Check if the length of the convertedOffset is larger than the radar radius. If it is, we clamp convertedOffset so that the resulting position is exactly radar radius away from the origin (or in other words: it’s exactly on the edge of the radius).
  3. Set the point to be visible or invisible, depending on if it’s further out than it’s displaymagnitude (or the default value of radar radius).
3 Likes

Brilliant concept, thank you!
Is there any way of adding this to the existing file? I can’t seem to get it working on my system.

I mean, yea, I added it into a fresh copy of the place. So you can just replace the code. But also make sure to set the attribute on the object, or it won’t work.

Hello,

When I checked out your .rbxl file, the red dot if further away, goes outside of the radar circle.

Is that normal?

Also is there a way, if you have teams, where lets say it is a team of 2, can you have the radar show where your teammate is at, but only on my radar… … basically a pointer to the direction / position of my teammate. ?

  1. Also does this update thinks it has on the radar real time?

Like if you delete it, will it take it off?

Can you in game add thinks to the radar, like a chest drop mid game, and it will show up on the radar?

Thanks

The red showing up outside the frame is a bug that I fixed in the updated version.

  1. Yes, there’s a way. And it wouldn’t be very difficult either. You already have to put radar visible objects in a specific folder. You can just add more folders, for team specific radar objects.

  2. No, not quite. But if you move the line which declares Objects into the RenderStepped event, it’ll be updated per frame