Shiftlock camera feels weird when preventing it from clipping into walls

Hey all, I’ve been scratching my head at this for the past week or so. I have a custom camera system with shiftlock, and I don’t want players sticking their camera into walls. I’ve tried about half a dozen reworks already but all of them feel weird when going near a wall.

Attempted solution 1: Remove offset as player’s head gets near a wall.
Feels weird if your camera wasn’t gonna clip in the wall in the first place.

Attempted solution 2: Apply offset after regular checks were done. Raycast into a wall and lower offset as needed.
It both looks weird and snaps rapidly when your camera is backed up against a wall.

Attempted solution 3: Raycast from non-offset camera origin to raycasted origin, then lerp from camera origin to offset target position based on raycast result.
Similar issue where it doesn’t feel like it should be kicking into action when your camera is backed up against a wall.

Anyone have any solution to this?

Still no progress on this. I’ve tried looking for answers from other game development engine but it didn’t help.

After a further 3 days of experimenting, this is the best I’ve gotten it feeling yet.

Here is the code for those struggling with the same issue:

local function wallCast(origin, target)
	local magnitude = (origin.Position - endPoint.Position).Magnitude
	local originalMagnitude = magnitude
	local ray =, endPoint.Position - origin.Position).Unit
	for i = -0.2, 0.2, 0.4 do
		for i2 = -0.2, 0.2, 0.4 do
			local multiply = originalMagnitude / magnitude
			local targetPosition = (endPoint * * multiply, i2 * multiply, 0)).Position - origin.Position
			local result = workSpace:Raycast(origin.Position, targetPosition, cameraRaycastParams)
			if result then
				local closestPoint = ray:ClosestPoint(result.Position)
				local newMagnitude = (origin.Position - closestPoint--[[result.Position]]).Magnitude -- +/- 0.2?
				if newMagnitude < magnitude then
					magnitude = newMagnitude
	local alpha = magnitude / originalMagnitude
	return alpha

-- cameraOffset is, 0, 0) if shiftlocking
local zoomValue = zoom.Value-- CFrame value with Z set at zoom level
local origin =-- + rotation
local target, alpha
if cameraOffset ~=, 0, 0) then
	local offsetOrigin = origin *
	target = offsetOrigin *, 0, minimumZoomBaseValue - 0.01)
	alpha = wallCast(origin, target)
	if alpha == 1 then
		origin = target
		target = offsetOrigin * zoomValue
		alpha = wallCast(origin, target)
	target = origin * zoomValue
	alpha = wallCast(origin, target)

camera.CFrame = origin:Lerp(target, alpha)

I think this is just gonna have to do. It’s not perfect, but I’m not smart enough to figure out a better solution.


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