LazyCam - Isometric: For when you're too lazy to write your own Diablo-style CameraScript

I started writing a camera system for a game I’m and decided to share it around for people to use for themselves.

I’ll call it LazyCam because it barely took me 2 hours to make, and it’s a very basic script that can be expanded upon or used as-is in a bunch of different projects that need a similar style of camera.


The camera is set to track the player from overhead at ~45 degree angle, and will follow slightly ahead of where they’re moving to get a better view of what’s directly ahead of them.


If the player isn’t moving, the camera will eventually center itself back to the player.

You can also zoom in and out by altering the FOV with the mouse wheel.


Here’s the script for whoever would like to use it in whatever they’re working on:

--[[CameraScript]]--
local Players = game:GetService("Players")
local RunSVC = game:GetService("RunService")

local player = Players.LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera
local character, humanoid, rootPart

local focusPoint : CFrame
local camGoal : CFrame
local camDistFromGoal : number
local minZoom = 30
local maxZoom = 75
local currentZoom = 50
local zoomIn, zoomOut

camera.CameraType = Enum.CameraType.Scriptable
camera.FieldOfView = currentZoom

-- determines whether the player is trying to zoom past the min/max zoom FOVs
local function isAtMaxZoom(isZoomingOut) : boolean
	return isZoomingOut and currentZoom >= maxZoom or not isZoomingOut and currentZoom <= minZoom
end

-- To be used for adjusting FOV when called using a Connection
local function zoom(isZoomingOut)
	return function()
		currentZoom += isAtMaxZoom(isZoomingOut) and 0 or isZoomingOut and 5 or -5
		camera.FieldOfView = currentZoom
	end
end

-- Gives the camera a goal position and focus point to move to
local function setCamGoal() : CFrame
	return CFrame.new(focusPoint.Position + Vector3.new(-30,60,-30), focusPoint.Position)
end

local function getDist(object, goal) : number
	return (object.CFrame.Position - goal).Magnitude
end

-- sets new camera focus point depending on current camera/player behavior
local function setNewFocusPoint() : CFrame
	return camDistFromGoal <= 3 and CFrame.new(rootPart.CFrame.Position)
		or humanoid.MoveDirection.Magnitude == 0 and focusPoint
		or CFrame.new(rootPart.CFrame.Position + (humanoid.MoveDirection * 20))
end

-- main function, lerps camera toward goal depending on player position and movement.
local function OverheadCam()
	camDistFromGoal = getDist(camera, camGoal.Position)
	camera.CFrame = camera.CFrame:Lerp(camGoal, math.clamp(0.002*camDistFromGoal, 0.0005, 0.025))
	
	if getDist(rootPart, focusPoint.Position) < 22 and camDistFromGoal <= 2 then return end
	
	focusPoint = setNewFocusPoint()
	camGoal = setCamGoal()
end

-- to be called when the character dies/is removed
local function UnbindCamera()
	character, humanoid, rootPart = nil
	RunSVC:UnbindFromRenderStep("Camera")
	zoomIn:Disconnect()
	zoomOut:Disconnect()
end

player.CharacterAdded:Connect(function(char)
	character = char
	humanoid = char:WaitForChild("Humanoid")
	rootPart = char:WaitForChild("HumanoidRootPart")
	
	focusPoint = rootPart.CFrame -- camera focuses on character position when spawning
	camGoal = setCamGoal()
	camera.CFrame = camGoal
	
	RunSVC:BindToRenderStep("Camera", 0, OverheadCam)
	zoomIn = mouse.WheelForward:Connect(zoom(false))
	zoomOut = mouse.WheelBackward:Connect(zoom(true))
	
	humanoid.Died:Connect(UnbindCamera)
end)

player.CharacterRemoving:Connect(UnbindCamera)

I’d also greatly appreciate any feedback y’all can give me on what I’ve written so far.

I’m looking at ways of letting the camera linger on specific focus points near areas of interest in the future (monster dens, NPCs/shops, etc.) so if anyone has any tips on how to go about it, feel free to leave them in the replies!

Edit(s): Some formatting and minor adjustments/comments to the script to make it a bit more readable (old script included below)

Old Script
local Players = game:GetService("Players")
local RunSVC = game:GetService("RunService")

local player = Players.LocalPlayer

local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable

local maxZoom = 35
local minZoom = 75
local currentZoom = 50

local character, humanoid, rootPart

local focusPoint : CFrame
local camGoal : CFrame
local camDistFromGoal : number

local function setCamGoal() : CFrame
	return CFrame.new(focusPoint.Position + Vector3.new(-30,60,-30), focusPoint.Position)
end

local function getDist(object, goal) : number
	return (object.CFrame.Position - goal).Magnitude
end

local function setNewFocusPoint() : CFrame
	return camDistFromGoal <= 3.1 and CFrame.new(rootPart.CFrame.Position)
		or humanoid.MoveDirection.Magnitude == 0 and focusPoint
		or CFrame.new(rootPart.CFrame.Position + (humanoid.MoveDirection * 20))
end

local function OverheadCam()
	
	camDistFromGoal = getDist(camera, camGoal.Position)
	camera.CFrame = camera.CFrame:Lerp(camGoal, math.clamp(0.002*camDistFromGoal, 0.0005, 0.025))
	
	if getDist(rootPart, focusPoint.Position) > 22 or camDistFromGoal > 3 then
		focusPoint = setNewFocusPoint()
		camGoal = setCamGoal()
	end
	
end

local zoomIn, zoomOut

local function zoom(out, zoom)
	return function()
		currentZoom += (out and currentZoom >= zoom or not out and currentZoom <= zoom) and 0 or out and 5 or -5
		camera.FieldOfView = currentZoom
	end
end

player.CharacterAdded:Connect(function(char)
	character = char
	humanoid = char:WaitForChild("Humanoid")
	rootPart = char:WaitForChild("HumanoidRootPart")
	
	focusPoint = rootPart.CFrame
	camGoal = setCamGoal()
	camera.CFrame = camGoal
	
	RunSVC:BindToRenderStep("Camera", 0, OverheadCam)
	
	zoomIn = player:GetMouse().WheelForward:Connect(zoom(false, maxZoom))
	
	zoomOut = player:GetMouse().WheelBackward:Connect(zoom(true, minZoom))
	
end)

player.CharacterRemoving:Connect(function(char)
	character, humanoid, rootPart = nil
	RunSVC:UnbindFromRenderStep("Camera")
	zoomIn:Disconnect()
	zoomOut:Disconnect()
end)
6 Likes