From in game camera to frame position on a minimap

I’ve recently created a mini map where I use a ViewPortFrame object to render all the parts in the world. I have a baseplate in the size of 500,1,500 and the camera cframe is at the Baseplate’s CFrame *CFrame.new(0,250,0) to get an overview of the map. Here is what I have:

https://i.gyazo.com/15e92b334a61bb3e6b39bac5e2252111.mp4

So my game is based off a popular game as you may now League Of Legends and for those who never played it here’s a picture of their mini map:

As you can see, a frame is used to show what part of the map you are currenty looking on which is based on the camera. My question is how would I replicate something like this?

I’m clueless and have no idea how I would begin with this since I usually never deal with the Camera object.

EDIT: there has been progress please go to the latest post do an updated situation.

8 Likes

So there are 2 options, you can hire an artist (or do it yourself) to draw a minimap that matches your map to the very detail.

You would then have to simply slide the image around whenever you character moves.

Or you can use a viewport frame to clone all the parts and then simply tween the camera (viewport frame’s camera) to be above your character as you move around the map

From the looks of it you’re trying to go by option 1. I’ve never made a minimap but I know that’s how they’re sometimes done.

I think you misunderstood, I already made the mini map.in the league of legends picture, on the minimap you’ll notice a small white frame, the frame represents what part of the map you are looking at and that is what I’m trying to replicate. As you can see in my gif, I’ve already made the mini map and want to somehow get the camera view into the UI

So you’re trying to only show what the camera can visibly see from it’s point of view?

Yea I’m tryna make this

Ah so I’m pretty sure you’re gonna need to use:

local camera = workspace.CurrentCamera
local worldPoint = Vector3.new(0, 10, 0)
local vector, onScreen = camera:WorldToScreenPoint(worldPoint)

local screenPoint = Vector2.new(vector.X, vector.Y)
local depth = vector.Z

^ Sample code from the wiki.

Could you explain in a more simplified manner of how this would work? I’m quite confused on how this works and what I should be doing to accomplish my goal.

So from what I can read you’d do something I suppose like this:

local TweenServ = game:GetService("TweenService")
local RunService = game:GetService("RunService")

local Camera = ViewPortFrame.CurrentCamera
local WorldPoint = game.Players.LocalPlayer.Character.PrimaryPart.Position
local Vector, onScreen = Camera:WorldToScreenPoint(WorldPoint)

--//: Functions

local function TweenInstance(Instance, Info, Goal)
	if Instance and Info and Goal then
		return TweenServ:Create(Instance, Info, Goal)
	end
end

--//: Main Scripting

local BoxFrame = UI.BoxFrame -- The white box the squares in the Player
local TweenSpeed = 0.15 -- Basically the speed in which it moves to the player, Make it 0 if you want it to snap onto the player

RunService.RenderStepped:Connect(function()
	TweenInstance(BoxFrame, TweenInfo.new(TweenSpeed), {Position = UDim2.new(0, Vector.X, 0, Vector.Y)}):Play()
end)

But I could be wrong.

Edit:

I forgot to render step loop it yikes.

Positioning the outline

To position the white outline, you’ll need to convert the player’s position in the game world to a different coordinate space, corresponding to the player’s position on the minimap. You’ll need to know:

  • The player’s position in the game world
  • The size of the level (the side length, in studs)

This assumes that the level is square, centered on (0, 0, 0) (although the Y coordinate doesn’t matter), and that the level and minimap are rotated the same. The camera should also point in a direction such that pressing W (or whatever corresponds to moving “into” the screen in your game) makes the character move in the -Z direction, and pressing D should make the player’s X coordinate increase.

Converting between world- space and minimap- space coordinates is as simple as
local levelScale = 100 --however many studs long the side of your level is

function toMinimapSpace(worldPoint)
	--Convert from a world- space Vector3 point to a minimap- space UDim2 Position
	local minimapV3 = worldPoint / levelScale
	return UDim2.new(
		0.5 + minimapV3.X, 0, 	--minimap X scale coordinate 
		0.5 + minimapV3.Z, 0	--minimap Y scale coordinate
	)
end

function toWorldSpace(minimapPoint)
	--Convert from a  minimap- space UDim2 Position to a world- space Vector3 point
	return Vector3.new(
		minimapPoint.X.Scale * (levelScale - 0.5), --world X coordinate
		0,
		minimapPoint.Y.Scale * (levelScale - 0.5) --world Z coordinate
	)
end
You'll probably want to update the white outline every frame, which you can do like so:
local player = game.Players.LocalPlayer
local camera = game.Workspace.CurrentCamera
local character = player.Character or player.CharacterAdded:Wait()
local cameraOutline = script.Parent.Frame

game:GetService("RunService").RenderStepped:Connect(function( dt )
	local playerPosition = character.HumanoidRootPart.Position --or some other way of getting the player's position
	cameraOutline.Position = toMinimapSpace(playerPosition) 
end)

This also assumes that CameraOutline is a GuiElement whose center corresponds to the point on the minimap where the outline should be centered. That means its’ AnchorPoint should be 0.5, 0.5. You can make the edges of the outline with 1-pixel wide frames. It also assumes that the position (0, 0, 0, 0) corresponds exactly to the upper-left corner of the image of the map, and that (1, 0, 1, 0) is exactly the lower right corner.

The size of the outline

If you think about it, the area of the ground (a horizontal plane) that is visible to the camera (assuming there is line of sight) the shape of the intersection of the camera’s view frustum and that plane. If the camera is pointed straight down, that shape is a rectangle. Otherwise, it’s some weird wonky shape. That means we can’t make the outline correspond exactly to what’s visible from the camera. That’s probably fine though, or at least it looks like that’s what LoL does too.

Basically, we’ll have to find some decent approximation of the size of the area. Here’s an illustration of the camera from a side view. If you plan on only ever allowing one camera field of view, and never move the camera up or down, then you can just set the height manually to something that’s close enough.

You can’t rely on the width though, because as player’s screen aspect ratios increase, so will their horizontal field of view.

We can get away with just multiplying the height of the outline by the screen aspect ratio, like so:
local outlineHeight = 0.12 --the Scale Y part of the Size of the outline, adjust to fit your needs

function aspectRatio( camera )
	return camera.ViewportSize.X / camera.ViewportSize.Y
end

game:GetService("RunService").RenderSteped:Connect(function( dt )
	local playerPosition = character.HumanoidRootPart.Position --or some other way of getting the player's position
	cameraOutline.Position = toMinimapSpace(playerPosition) 
	cameraOutline.Size = UDim2.new(outlineHeight * aspectRatio(), 0, outlineHeight, 0)
end)

If you need to do more accurate calculations, or if anything is unclear, then let me know.

7 Likes

I don’t get what you mean by “to your level” does that mean the camera view in x ? If so the problem is idk how much the camera can actually see.

You’re right, I completely forgot about setting the size of the outline. I’ll edit my original reply. EDIT: EDIT’ed

If the anchor point is 0.5,0.5 wouldn’t 0,0,0,0 means it would be off the grid?

https://gyazo.com/35eaf65e408b9bb3debfaa7134dd9cf4

So this is my result, it’s off as you can see and I do not know how big the size of the outliner should be. The anchor point of the outliner is 0.5,0.5.

Here’s my code:

--|| SERVICES ||--
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

--|| VARIABLES ||--
local Player = Players.LocalPlayer
local Mouse = Player:GetMouse()

--|| VARIABLES ||--
local worldX, worldZ = 500, 500 -- Map Size
local frameX, frameY = 0.15, 0.12

local Map = script.Parent
local OutlineFrame = Map.Outliner

local cameraPart = workspace.visualAssets:WaitForChild("Camera Part")

local function toMinimapSpace(worldPoint)
	--Convert from a world- space Vector3 point to a minimap- space UDim2 Position
	local minimapV3 = worldPoint / worldX
	return UDim2.new(
		0.5 + minimapV3.X, 0, 	--minimap X scale coordinate 
		0.5 + minimapV3.Z, 0	--minimap Y scale coordinate
	)
end

local function toWorldSpace(minimapPoint)
	--Convert from a  minimap- space UDim2 Position to a world- space Vector3 point
	return Vector3.new(
		minimapPoint.X.Scale * (worldX - 0.5), --world X coordinate
		0,
		minimapPoint.Y.Scale * (worldZ - 0.5) --world Z coordinate
	)
end

local function aspectRatio(camera)
	return camera.ViewportSize.X / camera.ViewportSize.Y
end

RunService.RenderStepped:Connect(function(dt)
	OutlineFrame.Position = toMinimapSpace(cameraPart.Position)
	OutlineFrame.Size = UDim2.new(frameX * aspectRatio(workspace.CurrentCamera), 0, frameY, 0)
end)

EDIT: The outliner is an imagelabel. Please reply ASAP thanks.

1 Like

I think for the size you will just need to do it manually. It won’t be very hard to get pretty good accuracy; you might just need a little bit of trial and error. Just make sure you clearly define the ‘rules’ so that you can keep this organized and therefore effective every time (instead of randomly jumping around) – for example, edge of the frame needs to meet the edge of a part of the same size.

The reason your current system is off is because your camera rotation is different from the world’s (or the frame’s) rotation. Try rotating your camera around 90 degrees clockwise, that should improve things a bit… but it looks like there might still be issues so you will need to find and fix those too.

What do you mean by rotated odd? It’s focused on a part with a y of like 20 with a small x offset of 5

Rotated off. The camera is in the wrong direction.

“Up” (on the screen) from the camera’s position is different from “up” on the minimap. So your X offset should probably be a Z offset.

Seems like you just need to make the values negative. That’s really it.

I found a simpler solution thanks for your reply though!

Would you be willing to share your fix so others may benefit from your finding?

The solution is rather simple but not as accurate. I’ve created a rough part with the transparency of 1 with a outline decal on top. Next I just update it’s position in the viewport and I got this:
https://gyazo.com/078346a19e968493806808e5ed562873

EDIT: If you are using the example shown above with the conversion of vectors to udim2, I believe the issue was the camera that was looking down on the entire map was rotated wrong which caused a confusion.