How To Zoom Into Gun Scope

Heya there!

I’ve made a gun recently, and I wanted to ask. How would I be able to zoom into the gun? The gun I have has a part called “Scope” which is where the camera is supposed to look into.

Currently, I’m trying to change the CFrame of the viewmodel itself but I’m not good with CFrames so I have no idea what I’m doing. You can see what my current attempt into making this work looks like on the video down below.

Here is the current code I have for the viewmodel:

local RNS = game:GetService("RunService")
local RS = game:GetService("ReplicatedStorage")
local PS = game:GetService("Players")
local PYS = game:GetService("PhysicsService")

local camera = workspace.CurrentCamera
local viewmodelCollision: string = "ViewmodelCollision"
local char = script.Parent
local player = PS:GetPlayerFromCharacter(char)
local mouse = player:GetMouse()
local torso = char:WaitForChild("Torso")
local head = char:WaitForChild("Head")
local hum = char:WaitForChild("Humanoid")
local rShoulder, lShoulder = torso:WaitForChild("Right Shoulder"), torso:WaitForChild("Left Shoulder")
local viewmodelChar = RS.Viewmodel:Clone() do
	viewmodelChar.Parent = camera
	viewmodelChar.Name = "Viewmodel"
end
local viewmodelTorso = viewmodelChar:WaitForChild("Torso")
local con = {}
local aiming = false

local function IsPlayerFirstPerson(): boolean
	local distance: number = (camera.CFrame.Position - head.Position).Magnitude
	
	return distance < 1
end

local function SetCollisionGroup(base: any, group: string)
	for _,v: BasePart in ipairs(base:GetDescendants()) do
		if v:IsA("BasePart") then
			PYS:SetPartCollisionGroup(v, group)
		end
	end
end

local function SetTransparency(base: any, num: number)
	local blacklist = {"Torso", "HumanoidRootPart", "Head"}
	
	for _,v: BasePart in ipairs(base:GetDescendants()) do
		if v:IsA("BasePart") and not table.find(blacklist, v.Name) then
			v.LocalTransparencyModifier = num
		end
	end
end

local function GetScopeOffset(): CFrame?
	if char:FindFirstChildWhichIsA("Tool") and char:FindFirstChildWhichIsA("Tool"):FindFirstChild("Scope") then
		local tool = char:FindFirstChildWhichIsA("Tool")
		local scope = tool.Scope
		
		-- i have absolutely no idea what im doing
		return camera.CFrame * tool.Handle.CFrame:Inverse() * scope.CFrame
	end
	return nil
end

con[1] = player.CharacterAppearanceLoaded:Connect(function()
	if viewmodelChar then
		local blacklist = {"left leg", "right leg"}

		for _,v in ipairs(char:GetDescendants()) do
			if v:IsA("Shirt") or v:IsA("Pants") or v:IsA("BodyColors") then
				local clone = v:Clone() do
					clone.Parent = viewmodelChar
				end
			end
		end
		
		SetCollisionGroup(viewmodelChar, viewmodelCollision)
		
		con[1]:Disconnect()
	end
end)

con[2] = RNS.RenderStepped:Connect(function()
	viewmodelChar:SetPrimaryPartCFrame(if aiming and GetScopeOffset() then GetScopeOffset() else camera.CFrame)
	
	if head.LocalTransparencyModifier == 1 then
		rShoulder.Part0 = viewmodelTorso
		lShoulder.Part0 = viewmodelTorso
	else
		rShoulder.Part0 = torso
		lShoulder.Part0 = torso
	end
	
	char["Right Arm"].LocalTransparencyModifier = 0
	char["Left Arm"].LocalTransparencyModifier = 0
end)

con[3] = hum.Died:Connect(function()
	for _,v: RBXScriptConnection in ipairs(con) do
		v:Disconnect()
	end
end)

con[4] = mouse.Button2Down:Connect(function()
	aiming = true
end)

con[5] = mouse.Button2Up:Connect(function()
	aiming = false
end)

Solved.

I followed a tutorial that thankfully gives you the code for the CFrame math (and a lot more stuff like recoil).