*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:
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:
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.