How would I make a dynamic top down view camera?

I would like to mimic camera work from those games.



Now, how do i do it ?

I didnt find any usefull information that would set me in the right direction, no guide nor topic talking about this stuff.

So I am asking you for help, tips and tricks would be much appreciated.

9 Likes

The first two examples offset the camera from being centered on the player depending on the position of the mouse cursor on the screen, so if the mouse is far away the camera is offset more.The third example seems to offset it to always be far away in the aim direction.

You can get the mouse screen position using mouse.X and mouse.Y. You can get the screen width and height by having a ScreenGui and getting ScreenGui.AbsoluteSize

You can overwrite the default camera behavior by putting a LocalScript called “CameraScript” inside StarterPlayerScripts. The name needs to be exact. You can also overwrite ControlScript. Now you can make your own camera system from scratch. Just make sure to set Camera.CameraType to “Scriptable”, and watch out because the game automatically sets it to “Custom” like a second after the game runs (annoying AF) so you need to set it twice :confused:

You can set the camera to be some constant offset from the character every RenderStepped. You can add to the X and Z coordinates of the camera CFrame depending on the mouse X and Y coords to get the desired effect.

2 Likes

I did that before using this video:

And changed the Cframe so that instead of showing the player from the side, it shows them from above, and i also changed the fov.

2 Likes

After closer inspection, its a pretty simple code most of it can be found online in much better form.

2 Likes

You don’t actually need it to act as any kind of GUI, it’s just the only way I know of to get the screen size in pixels :stuck_out_tongue: Just insert one into StarterGui, and look up screenGui.AbsolutePosition to get a Vector2 where the X and Y coordinates are the width and height of the screen.

You’ve got the basics of it! Does it work like you want it to, or are there some things that aren’t right? Post another reply and I’ll see if I can help :slight_smile:

1 Like

At its base everything works fine, the camera stares at the player from above, and that’s it, it doesn’t follow the cursor nor takes into account mouse inputs.
I’m just not that good at setting more advanced calculations.

So at my level of experience, I would make a part that follows the mouse and then tell the camera to stay in between the player and that part.

I’m still not sure if it would work but that’s the only way I could think of doing it or at least the only way i know how to write.

Hey, it’s me again, sadly i just could not figure it out, so i started working on an idea from earlier.
Here is an example

local Player = game:GetService("Players").LocalPlayer	
local player = game.Players.LocalPlayer
local mouse = Player:GetMouse()
local camera = workspace.CurrentCamera
local RS = game:GetService("RunService")

local offset = Vector3.new(-0.1,25,0)

camera.CameraType = Enum.CameraType.Scriptable
---Preset Parts
local LAM=game.Workspace.LookAtMe
local FC=game.Workspace.FakeCamera
local MP=game.Workspace.MouseP

mouse.TargetFilter = MP

local function onRenderStep() 
	
if player.Character then
	local playerposition = player.Character.HumanoidRootPart.Position 
	local playerrootpart = player.Character.HumanoidRootPart
		
		MP.CFrame = CFrame.new(mouse.Hit.p) --- where mouse points "MouseP"
		LAM.CFrame = playerrootpart.CFrame:Lerp(MP.CFrame, 0.2) --- white brick in the middle "LookAtMe"
		FC.CFrame = CFrame.new (LAM.Position + offset) --- part that hovers above white brick "FakeCamera"
		
    --- camera stares at "LookAtMe" from above thanks to the flying "FakeCamera"
		camera.CFrame = CFrame.new (FC.Position, LAM.Position)
	end
end

RS:BindToRenderStep("Camera", Enum.RenderPriority.Camera.Value, onRenderStep)

I still wanna try to do it “harder” way, thats how we learn
but i still have no idea how to do it, need more clues.

I don’t know what that is :stuck_out_tongue:


One way you could improve your script tho:

Instead of using Parts to represent CFrames... just use CFrames.

This…

local LAM=game.Workspace.LookAtMe
local FC=game.Workspace.FakeCamera
local MP=game.Workspace.MouseP
mouse.TargetFilter = MP

… becomes this:

local LAM, FC, MP

And this:

MP.CFrame = CFrame.new(mouse.Hit.p) --- where mouse points "MouseP"
LAM.CFrame = playerrootpart.CFrame:Lerp(MP.CFrame, 0.2) --- white brick in the middle "LookAtMe"
FC.CFrame = CFrame.new (LAM.Position + offset) --- part that hovers above white brick "FakeCamera"

--- camera stares at "LookAtMe" from above thanks to the flying "FakeCamera"
camera.CFrame = CFrame.new (FC.Position, LAM.Position)

becomes this:

MP = CFrame.new(mouse.Hit.p) --- where mouse points "MouseP"
LAM = playerrootpart.CFrame:Lerp(MP, 0.2) --- white brick in the middle "LookAtMe"
FC = CFrame.new(LAM.p + offset) --- part that hovers above white brick "FakeCamera"

--- camera stares at "LookAtMe" from above thanks to the flying "FakeCamera"
camera.CFrame = CFrame.new(FC.p, LAM.p)

I would also use much more verbose variable names, but that’s just a matter of coding style.

1 Like

Well harder for me, I really wanna try to do it your way, using absolute size and mouse z,x.
Because im really curious how that would work.

1 Like

Oh, right. Something like this?


local DOWN = Vector3.new(0, -1, 0)

local cameraMouseSensitivity = 10
local cameraOffset = Vector3.new(0, 10, 0)

function getMouseScreenPositionCentered()
	return Vector2.new(
		mouse.X - screenGui.AbsoluteSize.X/2,
		mouse.Y - screenGui.AbsoluteSize.Y/2
	)
end

function pixelToFraction(pixelV2)
	--Turns a coordinate in pixels into a coordinate in some fraction of the size of the screen
	return pixelV2 / screenGui.AbsoluteSize
end

function onRenderStep() 	
	if player.Character then
		local rootPart = player.Character.HumanoidRootPart
		
		local mouseScreenPos = pixelToFraction( getMouseScreenPositionCentered() )
		local cameraPos = cameraOffset + Vector3.new(mouseScreenPos.X, mouseScreenPos.Y) * cameraMouseSensitivity

		camera.CFrame = CFrame.new(cameraPos, cameraPos + DOWN)
	end
end
10 Likes

Yes exactly!
but …

  • seems like you forgot to put 0 in
    Vector3.new(mouseScreenPos.X, 0 , mouseScreenPos.Y)

  • and here is a fix for opacity.

	return Vector2.new(
		mouse.Y + ScreenGui.AbsoluteSize.X/2,
		mouse.X - ScreenGui.AbsoluteSize.Y/2)
end 

other than that, great knowledge mine, I didn’t know that you can use vector3 to face a certain direction and those functions that translate mouse movement on-screen is just amazing.

I’m gonna tinker with it some more.

1 Like

Ok, I may have crost the line.


Returning to my opacity fix, it doesn’t work.

I started messing around with everything and figured out that “DOWN” is causing all this trouble. After a while, I found a somewhat working position -
local DOWN = Vector3.new(0, -90, -45)
here is the effect

Aight found a perfect spot and calibrated it.

local offset = Vector3.new(0,25,5.35)
local DOWN = Vector3.new(0, -100, -15)
local cameraMouseSensitivity = 20

If you could somehow rotate “DOWN” a bit counter-clockwise it should give you that perfect top-down view but I didn’t find any other way then to just add an angle to it,
darn thing doesn’t want to move with smaller numbers.

Once Again I can’t express how thankfull I am for your help. :grinning:

1 Like

Do you mean the walking controls stop working? This is because the default character controller uses the camera’s LookVector to determine which horizontal direction corresponds to each movement direction. When the camera is looking straight up/down, there is no horizontal component to the LookVector so there’s no obvious way of figuring out which direction is forwards just from the LookVector…

…except it seems pretty obvious to us what direction the character should move in, it’s just a small flaw in the default character controller that pretty much never comes up when using the default camera controller, because looking straight up or down is impossible (there’s a +85 / -85 degree limit to looking up/down).

Rotating the camera as little as 10^-7 radians was enough to allow normal walking:
local RunS = game:GetService("RunService")

local DOWN = Vector3.new(0, -1, 0)
local MIN_CAM_ANGLE_FOR_WALKING = 10e-7

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local camera = game.Workspace.CurrentCamera

RunS.RenderStepped:Connect(function()
	local cameraPos = character.PrimaryPart.Position + Vector3.new(0, 25, 0)
	camera.CFrame = CFrame.new(
		cameraPos,
		cameraPos + DOWN
	) * CFrame.Angles(MIN_CAM_ANGLE_FOR_WALKING, 0, 0)
end)

You could also fix the default camera controller (test the game and copy it from the PlayerScripts folder), or make your own from scratch that falls back to using the UpVector to figure out direction in case the LookVector is straight up/down.

1 Like

Goddamn, another well explained trick I had no idea even existed.
Thanks for that Mr. RoBama.

1 Like

Since Robama started sharing this post im gonna leave best version of it here.

-- Made by ThanksRoBama.
-- Explained and polished by Czlopek

local Player = game:GetService("Players").LocalPlayer	
local player = game.Players.LocalPlayer
local mouse = Player:GetMouse()
local camera = workspace.CurrentCamera
local runService = game:GetService("RunService")
local screenGui = game.StarterGui.ScreenGui

-- here is your template for Vector3 axis (x y z)

local Distance = Vector3.new(0,25,0) -- Distance from player
local Direction = Vector3.new(0,-1,0) -- Direction from which camera will be looking at player
local CameraSensitivity = 20 -- change it if you want that camera to lean further (give it a bigger number) 
-- or if want an effect to be more subtle decrease the number. (if you set it as a negative, camera will lean in the opposite direction)

function getMouseScreenPositionCentered() 
	return Vector2.new(
		mouse.X - screenGui.AbsoluteSize.X/2,
		mouse.Y - screenGui.AbsoluteSize.Y/2)
end

function pixelToFraction(pixelV2)
	--Turns a coordinate in pixels into a coordinate in some fraction of the size of the screen
	return pixelV2 / screenGui.AbsoluteSize
end

function onRenderStep() 	
	if player.Character then
		local rootPart = player.Character.HumanoidRootPart
		local playerposition = player.Character.HumanoidRootPart.Position + Vector3.new(0,0,3) -- sometimes camera will be offset to your character, 
		-- thats why you need to add few studs to it so it would stay in middle of the screen.
		local cameraoffset = Distance + playerposition

		local mouseScreenPos = pixelToFraction( getMouseScreenPositionCentered() )
		local Axis = Vector3.new(-mouseScreenPos.Y,0,mouseScreenPos.X)

		local cameraPos = cameraoffset + Axis * CameraSensitivity 


		-- depending on what direction you set the camera to be facing remember that you will need to change it accurately to direction of the camera. 
		-- top down view will have Direction = Vector3.new(0,-1,0) and Axis = Vector3.new(-mouseScreenPos.Y,0,mouseScreenPos.X)
		-- side ways Direction = Vector3.new(0,0,-1) Axis = Vector3.new(mouseScreenPos.X,-mouseScreenPos.Y,0)

		camera.CFrame = CFrame.new(cameraPos, cameraPos + Direction)
	end
end

runService:BindToRenderStep("Camera", Enum.RenderPriority.Camera.Value, onRenderStep)

I strongly suggest using this thing:
Square
it shows what 2 axis you will need to change for it to work.

Script It self should be put in “StarterCharacterScripts”,
this thing.
Nowy obraz mapy bitowej (2)

7 Likes

Hey, so this works great but im having issues with the y axis. If I go up, the Y axis increases however if I go down the Y axis goes down.

https://gyazo.com/a80df34a95db17a723e90f4e93f69ab9

Sorry for taking so long was busy doing real-life stuff,

So scrip I posted was remade for a 2D Side view camera, that’s why one side goes down-up and the other left-right to fix this you need to change variables called

Direction = Vector3.new(0,-1,0)

and

Axis = Vector3.new(-mouseScreenPos.Y,0,mouseScreenPos.X)

to those above.
Settings for cameras were explained in the lower part of the code.

1 Like

Help the camera does not change when i move my mouse…
i’ve put the script into the StarterPlayerScripts but it just acts like a top down view camera.
i am not a scripter so i dont really know what to do.