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.
https://cdn.discordapp.com/attachments/715245143418208256/1051695031544709120/RobloxStudioBeta_mImLHSAuc4.gif
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.
https://cdn.discordapp.com/attachments/1044363286587641888/1053088473642451064/RobloxStudioBeta_RsLSCKVkfc.gif
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.
https://cdn.discordapp.com/attachments/297459692240764929/1053831437721288744/RobloxStudioBeta_zdOY9pFmb4.gif
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 = Ray.new(origin.Position, 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 * CFrame.new(i * 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
				end
			end
		end
	end
	local alpha = magnitude / originalMagnitude
	return alpha
end

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

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.

2 Likes

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