Zooming Problem With Mouse Lock Script

I’ve been using a mouse lock script similar to the default shift lock in roblox, but I have encountered a problem with the script. The cameras position always stays the same and prevents the player from zooming in or out, this also makes being next to walls or inside of small spaces difficult because you wouldn’t be able to see your player. How do I add the default roblox zooming function to this script?

Being Next To Walls Without Script (Camera automatically adjusts):

Being Next To Walls With Script(Camera does not adjust):

Script:

local xAngle = 0
local yAngle = 0
local cameraPos = Vector3.new(2,0,10)
local toggled = true

local function FocusToggle()
	wait(1)
	if toggled == true then
Camera.CameraType = Enum.CameraType.Scriptable
userInputService.MouseBehavior = Enum.MouseBehavior.LockCenter

userInputService.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		xAngle = xAngle-input.Delta.x*0.4
		--Clamp the vertical axis so it doesn't go upside down or glitch.
		yAngle = math.clamp(yAngle-input.Delta.y*0.4,-80,80)
	end
end)

runService.Heartbeat:Connect(function()
	local Character = localPlayer.Character
	local rootPart = Character:FindFirstChild("HumanoidRootPart")
	if Character and rootPart then
		--Set rotated start cframe inside head
		local startCFrame = CFrame.new((rootPart.CFrame.p + Vector3.new(0,2,0)))*CFrame.Angles(0, math.rad(xAngle), 0)*CFrame.Angles(math.rad(yAngle), 0, 0)
		
		--Set camera focus and cframe
		local cameraCFrame = startCFrame + startCFrame:vectorToWorldSpace(Vector3.new(cameraPos.X,cameraPos.Y,cameraPos.Z))
		local cameraFocus = startCFrame + startCFrame:vectorToWorldSpace(Vector3.new(cameraPos.X,cameraPos.Y,-50000))
		
		Camera.CFrame = CFrame.new(cameraCFrame.p,cameraFocus.p)

		
	end
	end)
	end
	end
	

localPlayer.CharacterAdded:Connect(function(char) --When Player Spawns
	
if userInputService.KeyboardEnabled==false and userInputService.TouchEnabled==true then --If player is on mobile
	return end
	
	FocusToggle()
		
	end)
	
localPlayer.CharacterRemoving:Connect(function(char) --When Player Despawns
	toggled = false
end)

This is done by raycasting with a ray to check for parts that collide with it. You’ll want to do a raycast from the players head to the camera, which can be done with this code:

local xAngle = 0
local yAngle = 0
local cameraPos = Vector3.new(2,0,10)
local toggled = true
local Camera = workspace.CurrentCamera
local userInputService = game:GetService("UserInputService")
local localPlayer = game.Players.LocalPlayer
local runService = game:GetService("RunService")

local function FocusToggle()
	wait(1)
	if toggled then
		Camera.CameraType = Enum.CameraType.Scriptable
		userInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
		
		userInputService.InputChanged:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseMovement then
				xAngle = xAngle-input.Delta.x*0.4
				--Clamp the vertical axis so it doesn't go upside down or glitch.
				yAngle = math.clamp(yAngle-input.Delta.y*0.4,-80,80)
			end
		end)
		
		local IgnoreList = {}
		for _,instance in pairs(localPlayer.Character:GetDescendants()) do
			if instance:IsA("BasePart") then
				table.insert(IgnoreList, instance)
			end
		end
        --this was changed to RenderStepped, as that is what is most commonly used for camera scripts.
		runService.RenderStepped:Connect(function()
			if userInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
				userInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
			end
			local Character = localPlayer.Character
			local rootPart = Character:FindFirstChild("HumanoidRootPart")
			if Character and rootPart then
		
				
				--Set rotated start cframe inside head
				local startCFrame = CFrame.new((rootPart.CFrame.p + Vector3.new(0,2,0)))*CFrame.Angles(math.rad(yAngle), math.rad(xAngle), 0)
				
				--Set camera focus and cframe
				local cameraCFrame = startCFrame + startCFrame:vectorToWorldSpace(Vector3.new(cameraPos.X,cameraPos.Y,cameraPos.Z))
				local cameraFocus = startCFrame + startCFrame:vectorToWorldSpace(Vector3.new(cameraPos.X,cameraPos.Y,-50000))
				
				Camera.CFrame = CFrame.new(cameraCFrame.p,cameraFocus.p)
				--all of this code was changed
				local Cf = CFrame.new(rootPart.CFrame.p + Vector3.new(0,2,0), Camera.CFrame.Position)
				local ray = Ray.new(Character.Head.Position, Cf.LookVector * 15) -- the * 15 makes the ray have a length of 15,
				 --so 15 studs away is the max it will detect parts and collide
				local Hitpart, Hitpoint, Normal, Material = workspace:FindPartOnRayWithIgnoreList(ray, IgnoreList)
				
				if Hitpart then
					--collide camera
					print("colliding camera")
					Camera.CFrame = Camera.CFrame * CFrame.new(0,0, -(Camera.CFrame.Position - Hitpoint).Magnitude)
				end
			end
		end)
	end
end
	

localPlayer.CharacterAdded:Connect(function(char) --When Player Spawns
	
	if not userInputService.KeyboardEnabled and userInputService.TouchEnabled then --If player is on mobile
		return 
	end
	
	FocusToggle()
		
	end)
	
localPlayer.CharacterRemoving:Connect(function(char) --When Player Despawns
	toggled = false
end)

There are a few weird things about it, excuse my sub par CFrame skills, but with a little work it can work exactly how you want it to.
Hope it helped!

1 Like

Hey! Thanks for this, it does what I want it to do, the only problem is it messes up the camera movement severely. I dunno much about raycasting so i’ll have to learn that. Do you have any idea how I could fix that camera problem? Its fine if you cant help ill just try my best to figure it out :sweat_smile:

I noticed it messed it up as well, I spent a small amount of time trying to fix it, but I’m not sure how to fix it. The idea is just moving the camera closer to the humanoid when a collision is detected, so if your camera script has a way to move it closer that would likely work better than my hacky way.

I’ll try to fix but thanks so much for the help :heart: