How to provent camera from going thought a wall

hello my fellow robloxians. i’ve got an question. i am currently making some sort of hoverpod.
but how could i prevent my camera from going through walls. as shown here

i would be thankfull if someone has an solution to my problem.

local SENSETIVITY = 1
local SCROLL_SENSETIVITY = 5

local Camera = workspace.CurrentCamera
local RunService = game:GetService("RunService")
local player = game.Players.LocalPlayer
local InputService = game:GetService("UserInputService")
local camDist = 50
local x, y = 0,0

local stepped = game:GetService("RunService").RenderStepped
CameraCoordinateFrame = Camera.CoordinateFrame
Camera.CameraType = "Scriptable"
Camera.CameraSubject = player.Character.Head

local rel = Camera.CFrame.p - player.Character.HumanoidRootPart.Position

camDist = rel.magnitude
y = math.atan2(rel.X,rel.Z)
x = -math.atan2(rel.Y,Vector2.new(rel.X,rel.Z).magnitude)


local function CFrameFromVectors(pos,vx,vy,vz) -- CFrame constructor given ordinal vectors
    pos = pos or Vector3.new()
    vz = vz or vx:Cross(vy)
    return CFrame.new(pos.X, pos.Y, pos.Z, vx.X, vy.X, vz.X, vx.Y, vy.Y, vz.Y, vx.Z, vy.Z, vz.Z)
end

RunService:BindToRenderStep("Camera",200,function(step)
	if not Camera.CameraSubject then warn("No camera subject!") return end
	
	local subject = Camera.CameraSubject
	local posOffset = Vector3.new()
	
	if Camera.CameraSubject:IsA("Humanoid") then
		subject = subject.Parent.HumanoidRootPart
		posOffset = Vector3.new(0,2.5,0)
	end
	
	Camera.CFrame = subject.CFrame * CFrame.Angles(0,y,0) * CFrame.Angles(x,0,0) * CFrame.new(0,0,camDist)
	warn(camDist)
	local transparency = 1 - math.min(1,math.max(0,(camDist-.5)/10))
	for i,v in pairs(player.Character:GetChildren()) do
		if v.Name ~= "HumanoidRootPart" then
			if v:IsA("BasePart") then
				v.Transparency = transparency
			elseif v:IsA("Accessory") then
				v.Handle.Transparency = transparency
			end
		end
	end
end)

local RMB = false

InputService.InputBegan:connect(function(input,processed)
	if input.UserInputType == Enum.UserInputType.MouseButton2 then
		InputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
		while true do
			local input, processed = InputService.InputEnded:wait()
			if not processed and input.UserInputType == Enum.UserInputType.MouseButton2 then
				break
			end
			wait()
		end
		
		InputService.MouseBehavior = Enum.MouseBehavior.Default
	end
end)

InputService.InputChanged:connect(function(input,processed)
	if processed then return end
	
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		y = y - input.Delta.X * 0.004 * SENSETIVITY
		y = y % (math.pi * 2)
		x = x - input.Delta.Y * 0.004 * SENSETIVITY
		x = math.min(math.max(x,-1.4),1.4)
	elseif input.UserInputType == Enum.UserInputType.MouseWheel then
		local scrollAmount = input.Position.Z
		camDist = camDist + scrollAmount * -SCROLL_SENSETIVITY
		
		camDist = math.min(math.max(camDist,player.CameraMinZoomDistance),player.CameraMaxZoomDistance)
	end
end)

3 Likes

What you could do is cast a ray from your camera’s focus point (player’s head) to the camera’s cframe - if anything is hit by the ray (a part or wall), pull the camera in.

Note - I haven’t tested this (nor done rays for a while) so the raycast may be pointing the wrong way. Just put the ‘hit’ variable into a print and see if it is detecting a wall.
If not - simply invert the pointing direct on line 47:
local ray = Ray.new(camPos,(camFocus-camPos).unit*camDist)
to
local ray = Ray.new(camPos,(camPos-camFocus).unit*camDist)

local SENSETIVITY = 1
local SCROLL_SENSETIVITY = 5

local Camera = workspace.CurrentCamera
local RunService = game:GetService("RunService")
local player = game.Players.LocalPlayer
local InputService = game:GetService("UserInputService")
local camDist = 50
local x, y = 0,0

local stepped = game:GetService("RunService").RenderStepped
CameraCoordinateFrame = Camera.CoordinateFrame
Camera.CameraType = "Scriptable"
Camera.CameraSubject = player.Character.Head

local rel = Camera.CFrame.p - player.Character.HumanoidRootPart.Position

camDist = rel.magnitude
y = math.atan2(rel.X,rel.Z)
x = -math.atan2(rel.Y,Vector2.new(rel.X,rel.Z).magnitude)


local function CFrameFromVectors(pos,vx,vy,vz) -- CFrame constructor given ordinal vectors
    pos = pos or Vector3.new()
    vz = vz or vx:Cross(vy)
    return CFrame.new(pos.X, pos.Y, pos.Z, vx.X, vy.X, vz.X, vx.Y, vy.Y, vz.Y, vx.Z, vy.Z, vz.Z)
end

RunService:BindToRenderStep("Camera",200,function(step)
	if not Camera.CameraSubject then warn("No camera subject!") return end
	
	local subject = Camera.CameraSubject
	local posOffset = Vector3.new()
	
	if Camera.CameraSubject:IsA("Humanoid") then
		subject = subject.Parent.HumanoidRootPart
		posOffset = Vector3.new(0,2.5,0)
	end


	-- Cast Ray to detect if the camera is inside a part
	-- incase camera subject is a humanoid:
	local camSubject = Camera.CameraSubject:IsA("BasePart") and Camera.CameraSubject or (Camera.CameraSubject.Parent:FindFirstChild("Head") or Camera.CameraSubject:FindFirstChildOfClass("BasePart")) 
	local camPos = Camera.CFrame.p
	-- just in case the camSubject is invalid for some reason.
	local camFocus = camSubject and camSubject.Position or player.Character.PrimaryPart.Position
	local ray = Ray.new(camPos,(camFocus-camPos).unit*camDist)

	local ignoreList = {} -- any object you put in here (such as your character or the player's ship) will be ignored in the ray cast.
	local hit,pos = game.Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)

	-- Change distance if an object is detected.
dist = hit and (pos-ray.Origin).magnitude or camDist

	Camera.CFrame = subject.CFrame * CFrame.Angles(0,y,0) * CFrame.Angles(x,0,0) * CFrame.new(0,0,dist)
	warn(camDist)
	local transparency = 1 - math.min(1,math.max(0,(camDist-.5)/10))
	for i,v in pairs(player.Character:GetChildren()) do
		if v.Name ~= "HumanoidRootPart" then
			if v:IsA("BasePart") then
				v.Transparency = transparency
			elseif v:IsA("Accessory") then
				v.Handle.Transparency = transparency
			end
		end
	end
end)

local RMB = false

InputService.InputBegan:connect(function(input,processed)
	if input.UserInputType == Enum.UserInputType.MouseButton2 then
		InputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
		while true do
			local input, processed = InputService.InputEnded:wait()
			if not processed and input.UserInputType == Enum.UserInputType.MouseButton2 then
				break
			end
			wait()
		end
		
		InputService.MouseBehavior = Enum.MouseBehavior.Default
	end
end)

InputService.InputChanged:connect(function(input,processed)
	if processed then return end
	
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		y = y - input.Delta.X * 0.004 * SENSETIVITY
		y = y % (math.pi * 2)
		x = x - input.Delta.Y * 0.004 * SENSETIVITY
		x = math.min(math.max(x,-1.4),1.4)
	elseif input.UserInputType == Enum.UserInputType.MouseWheel then
		local scrollAmount = input.Position.Z
		camDist = camDist + scrollAmount * -SCROLL_SENSETIVITY
		
		camDist = math.min(math.max(camDist,player.CameraMinZoomDistance),player.CameraMaxZoomDistance)
	end
end)
4 Likes

thanks i will try this

Just edited a little mistake - make sure to re-copy

(I had it so that the ‘camDist’ was being changed - which is not what you want obviously - it should remain the same now.)

This is the relevant existing API:

https://www.robloxdev.com/api-reference/function/Camera/GetLargestCutoffDistance

3 Likes

well tried with this but diddent work the way i wanted to

this is what i used

local cutoffdistance = Camera:GetLargestCutoffDistance({player.Character})
	if cutoffdistance > 1 then
		Camera.CFrame = subject.CFrame * CFrame.Angles(0,y,0) * CFrame.Angles(x,0,0) * CFrame.new(0,0,cutoffdistance)
	else
		Camera.CFrame = subject.CFrame * CFrame.Angles(0,y,0) * CFrame.Angles(x,0,0) * CFrame.new(0,0,camDist)
	end
2 Likes