Creating An Efficient* Viewport Minimap

*Efficient relative to other viewport-based minimaps.

What’s a minimap?

You’ve seen these in popular games before such as Jailbreak. They look similar to:
image
A lot of people want to make these but don’t know where to start. There are a few good tools out there such as Widgeon’s Isometric Map Maker. The issue is these tools have long setups and take outside resources to use. The method this tutorial uses is entirely based around viewport frames and Region3s.

Creating the minimap

First you should create the actual minimap GUI. Mine looks like so:

image

Coding the minimap

Within the controller script we now have to define a few variables:

-----// SERVICES //-----
local PLRS = game:GetService("Players");
local RUN = game:GetService("RunService");

-----// VARIABLES //-----
local player = PLRS.LocalPlayer;
repeat RUN.RenderStepped:Wait() until player.Character
local character = player.Character;

local screen = script.Parent;
local viewport = screen:WaitForChild("ViewportFrame");
local arrow = viewport:WaitForChild("Arrow");

local camera = Instance.new("Camera", viewport);

local sizeX, sizeY, sizeZ = 25, 200, 25;

-----// CODE //-----

Now within our code section we can initialize the camera.

camera.FieldOfView = 10;
viewport.CurrentCamera = camera;

The viewport frame needs to be updated every frame based on the player’s position. This will create an accurate minimap. To do this we can use the RunService.RenderStepped event.

RUN.RenderStepped:Connect(function()
  -- Clear the previous viewport frame
  for _, child in pairs(viewport:GetChildren()) do
    if child:IsA("BasePart") then child:Destroy() end
  end
end)

So we now have code to clear the viewport frame once we fill it every frame. But how do we actually fill it? Rather than simply copying and pasting the entire map into our viewport frame we will use region3s.

-- Current character position
local currentPos = character.HumanoidRootPart.Position;
-- Create a new Region3 based off the current position and offsets defined by our size variables
local r3 = Region3.new(Vector3.new(currentPos.X - sizeX, currentPos.Y - sizeY, currentPos.Z - sizeZ), Vector3.new(currentPos.X + sizeX, currentPos.Y + sizeY, currentPos.Z + sizeZ));
-- Now we can get all the objects within the Region3
-- We pass the character to ignore it and all its descendants so it will not appear
-- Finally we pass math.huge to have an infinite number of parts detected
---- You can change the third parameter for better performance but your map might not fully render based on its size
local objects = workspace:FindPartsInRegion3(r3, character, math.huge);

Once we have the objects near the player we can actually render them onto the viewport frame.

for _, obj in pairs(objects) do
  local _c = obj:Clone();
  _c.Parent = viewport;
end

All we have left to do now is update the arrow and the camera’s CFrame.

-- Rotate the arrow (this assumes your original image is facing to the right)
--- You can apply an offset of x degrees to fix it if it is facing another direction
local lookVect = character.HumanoidRootPart.CFrame.lookVector;
local newRot = math.deg(math.atan2(lookVect.x, lookVect.z));
arrow.Rotation = -(newRot) + 90; -- I'm applying an offset of 90 degrees since my arrow's image is facing up

Lastly the camera CFrame:

-- I use 1.2 here to slightly increase the cameras height compared to the Region3
-- Any higher and you will begin to notice objects not rendering at your viewports edges
-- The CFrame itself is just taking the HumanoidRootPart's position with an offset height and then looking down at the HumandRootPart
camera.CFrame = CFrame.new(Vector3.new(character.HumanoidRootPart.Position.X, character.HumanoidRootPart.Position.Y + sizeY * 1.2, character.HumanoidRootPart.Position.Z), currentPos);

I’ve found this method to be fairly efficient depending on how high quality your game’s map is.

Final Thoughts

If you have any feedback or suggestions on this feel free to let me know. Once again, the process this tutorial uses is significantly less efficient than generating an image yet when compared to other viewport methods I have found this one to come out on top.

14 Likes

I like the idea behind this resource but it is not efficient performance-wise. What map did you test this on?

With this implementation, you could potentially be (needlessly) deleting and re-cloning hundreds of parts a frame, and that’s ignoring all of the performance implications of a ViewportFrame or querying a Region3 every frame.

While I like the idea behind this, the implementation could be vastly improved. In its current state, it should not be used in a game since it will be a huge source of lag and general performance issues. While image-based solutions (like Widgeon’s Isometric Map Maker) are more work to setup, they will pay off a lot more in the long-run.

5 Likes

How in the world is cloning the map more efficient than a rendered image, which IS what Jailbreak uses if I recall correctly?

2 Likes

There are instances where you can not import an uploaded image of the map.

In procedural maps, where terrain or possibly different parts of the map are updated and created every time the server runs, it would be nearly impossible to upload an image for every new server that gets created, especially considering that every image uploaded would still need to be approved by moderation.

I mentioned multiple times that this was efficient relative to other viewport-based systems. Read the post before commenting next time thanks.

I fully agree with you there. I tested this in a place with a total of 17,315 baseparts. The heaviest test was in a region containing 734 baseparts. During this test my CPU saw extremely minor spikes in usage but nothing worrisome. While I do agree that images are infinitely better than viewport frames I see no reason to not use a viewport frame in games that contain smaller maps.

Might gotta share this with so me of my friends cause this is very helpful.

1 Like

Then your topic should be titled “Creating an Efficient Viewport Minimap” not “Creating an Efficient Minimap”.

I’ll change it now but literally reading the first line of the post specifies that.

1 Like