Implementing a zoom function

I’ve been trying for a week straight to implement a zoom function to my custom camera but nothing works. I don’t want to use FOV because that looks weird.

In theory, how should I implement a zoom function?

1 Like

In theory that should work, but there’s much more complex math behind that. You don’t want it to zoom the exact same way in every angle. Like you can’t just say camera.CFrame + Vector3.new(2, 2, 2).

Read a post about animating a camera. You can read it and make use of it in a way. I have not tried it myself but I think it will help you.

The math isn’t that much more complex. You just take the targetPos + (targetPos - cameraPos).Unit * zoomDistance. That accounts for every angle assuming the camera is initially in the correct place since you are effectively just moving the camera back further along the axis it already is on.

I have made systems in the past that use curves to describe a more complex camera path to take with the zoom, but further modifications depend heavily on your exact intent.

Is the RunService function not allowing the camera to zoom or is it some other problem, because it never wants to change the CFrame of the camera no matter what I try

local NormalCamera = {}

local Players = game:GetService("Players")
local ContextActionService = game:GetService("ContextActionService")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")

local camera = workspace.CurrentCamera
local player = Players.LocalPlayer

local cameraOffset = Vector3.new(2, 2, 8)

local MIN_ZOOM = 1
local MAX_ZOOM = 20

camera.CameraType = Enum.CameraType.Scriptable

local isMouseButtonDown = false
local sensitivity = 0.1

local cameraAngles = require(script.Parent.CameraAngles)

local function BaseDefaultCamera(character)
	if not character then
		return
	end

	local humanoid = character:WaitForChild("Humanoid")
	local rootPart = character:WaitForChild("HumanoidRootPart")
	
	
	local function playerInput(actionName, inputState, inputObject)
		if inputObject.UserInputType == Enum.UserInputType.MouseButton2 then
			isMouseButtonDown = inputState == Enum.UserInputState.Begin
			if isMouseButtonDown then
				UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
			else
				isMouseButtonDown = false
				UserInputService.MouseBehavior = Enum.MouseBehavior.Default
			end
		end
	end

	RunService:BindToRenderStep("CameraUpdateN", Enum.RenderPriority.Camera.Value, function()
		if isMouseButtonDown then
			cameraAngles.cameraAngleX -= UserInputService:GetMouseDelta().X * sensitivity
			cameraAngles.cameraAngleY = math.clamp(cameraAngles.cameraAngleY - UserInputService:GetMouseDelta().Y * sensitivity, -75, 75)
		end

		local startCFrame = CFrame.new(rootPart.CFrame.Position) * CFrame.Angles(0, math.rad(cameraAngles.cameraAngleX), 0) * CFrame.Angles(math.rad(cameraAngles.cameraAngleY), 0, 0)
		local cameraCFrame = startCFrame:PointToWorldSpace(cameraOffset)
		local cameraFocus = startCFrame:PointToWorldSpace(Vector3.new(cameraOffset.X, cameraOffset.Y, -100000))

		camera.CFrame = CFrame.lookAt(cameraCFrame, cameraFocus)
	end)
	
	local function Zoom(actionName, inputState, input)
		local scrollDelta = input.Position.Z
		local zoomDistance = (camera.CFrame.Position - camera.Focus.Position).Magnitude
		zoomDistance = zoomDistance + scrollDelta
		zoomDistance = math.clamp(zoomDistance, MIN_ZOOM, MAX_ZOOM)

		camera.CFrame = CFrame.new(camera.Focus.Position + (camera.CFrame.Position - camera.Focus.Position).Unit * zoomDistance)
	end

	
	ContextActionService:BindAction("PlayerInputN", playerInput, false, Enum.UserInputType.MouseButton2, Enum.UserInputType.Touch)
	ContextActionService:BindAction("Zoom", Zoom, false, Enum.UserInputType.MouseWheel)
end

function NormalCamera.NormalCamera()
	local character = player.Character
	if character then
		BaseDefaultCamera(character)
	else
		player.CharacterAdded:Connect(function(char)
			BaseDefaultCamera(char)
		end)
	end
end

function NormalCamera.ReleaseNormCam()
	ContextActionService:UnbindAction("PlayerInputN")
	RunService:UnbindFromRenderStep("CameraUpdateN")
end

return NormalCamera

I’ve just noticed. When I walk and try to scroll it rotates my character for some reason

The best solution is to adjust the FOV. I’m not sure what application you’re using where physically moving the camera forward is better.

Doing anything else and you’ll have to take into account the static perspective, which doesn’t feel natural for most zooming applications.

Anyway, your solution:

Zooming is just decreasing the camera angle.

Since you have a set camera with a set FOV this becomes more complicated.

“width” is our target screen width, let’s set it to an arbitrary number for now, you should tweak it to your liking when implementing.

tan(camFov) * camD = width/2
camD = width/2 / tan(camFov)

tan(zoomFov) * zoomD = width/2
zoomD = width/2 / tan(zoomFov)

d = zoomD - camD

d is how far you need to shift your camera forward from the original position to achieve the desired zoom angle.

-- put script in character as a local script

local function getZoomDistance(zoomFOV, width)
	-- NOTE: zoomFOV must be less than the camera's FOV or you will receive a negative number
	local camFOV = workspace.CurrentCamera.FieldOfView

	zoomFOV = math.rad(zoomFOV) -- convert to radian
	camFOV = math.rad(camFOV)

	local camD = width / 2 / math.tan(camFOV)
	local zoomD = width / 2 / math.tan(zoomFOV)

	return zoomD - camD
end

local humanoidRoot = script.Parent.PrimaryPart
workspace.CurrentCamera.CameraType = "Scriptable"

local n = 0
game["Run Service"].RenderStepped:Connect(function(dt)
	n = (n + dt/5) % 1
	local zoomValue = math.sin(n * math.pi * 2) * 32.5 + 37.5 -- smoothly slide between 5 and 70 every 5 seconds
	print(zoomValue)
	workspace.CurrentCamera.CFrame = humanoidRoot.CFrame * CFrame.new(0,0,-2) * CFrame.new(0,0,-getZoomDistance(zoomValue, 10))
end)

Good luck!

1 Like

Nice post, I was going to say, how do you expect to zoom without FOV since that is basically the definition of zoom.

It depends on what zoom is defined as by the OP. The definition is a bit ambiguous right now until we know what he’s using it for. He mentions that he doesn’t want to use FOV but still wants the effects of zooming. My interpretation is that he’s looking to see things far away without changing the FOV/perspective.

Yeah, FOV is the defenition of the zoom. But the FOV that the roblox has works in a weird way. So i’m kind of looking to replicate the default roblox zoom function since I made a custom camera for the character. It works the exact same way the normal roblox camera works but I just can’t implement the zoom function.

Can you provide an example or a video of what you’re looking for?

Seems like he is trying to make his own ‘I’ ‘O’ (keyboard) or mouse scroll wheel effect. That isn’t really a zoom that is just moving the camera back or into the character’s head.

I was just about to get a video, but this is what I’m trying to do

Maybe change the title to ‘implementing a 3rd person to 1st person view control’ or something, idk

Yeah, but it is basically a zoom function, it zooms in on the character’s head

Thx for the help but i’ve found how to do it

local function Zoom(a,b, input)
	local zoomAmount = input.Position.Z * ZOOM_SPEED
	local newZoom = math.clamp(cameraOffset.Z - zoomAmount, MIN_ZOOM, MAX_ZOOM)
	cameraOffset = Vector3.new(cameraOffset.X, cameraOffset.Y, newZoom)
end

ContextActionService:BindAction("Zoom", Zoom, false, Enum.UserInputType.MouseWheel)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.