A solution could be to make a new part every time RunService.RenderStepped is called. (You would obviously delete the old part every time it’s called as well.)
Then, you’d run part:GetTouchingParts() and see if the part is touching any parts not parented to the player character.
If a part is detected, move the part backwards until it is no longer touching that part and have the camera’s offset be re-calculated using the parts new position.
I should probably note that this method isn’t the most efficient, just the easiest to code (in my opinion).
I’m saying the Part will need to be positioned in a way to Prevent Camera from Clipping, when doing specific things whilst the Part is Positions in front of the Player, It would disrupt that State.
If you look in the camera modules, you’ll find a specific module which I don’t know if you’ve review already, called “ZoomController”. Inside that, “Popper”. That module is responsible for the clipping of the camera.
You can look in this module to see if anything here helps. You said in first person so the module might deactivate itself when the player is in first person. Try disabling that.
I solved this by preventing the player from moving if a raycast from the camera hits a wall, this is inside PlayerModule > ControlModule in the :OnRenderStepped function:
local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Blacklist
local function getGraphEquation(point1: Vector2, point2: Vector2)
local slope = (point1.Y - point2.Y) / (point1.X - point2.X)
local yIntercept = -slope * point1.X + point1.Y
-- local function getX(y: number)
-- return (y - yIntercept) / slope
-- end
local function getY(x: number)
return slope * x + yIntercept
end
return getY
-- return getX, getY
end
local x2 = 90 -- The point we'll use for if statment
local getY1 = getGraphEquation(Vector2.new(0, 2), Vector2.new(x2, 4))
local getY2 = getGraphEquation(Vector2.new(x2, 4), Vector2.new(180, 4))
local function getY(x)
if x > 90 then
return getY2(x)
else
return getY1(x)
end
end
if moveVector ~= Vector3.zero then
local angle = math.acos(math.clamp(char:GetPivot().LookVector:Dot(moveVector), -1, 1))
local ray = workspace:Raycast(camCf.Position, moveVector * getY(math.deg(angle)), rayParams)
if ray then moveVector = Vector3.zero end
end
Basically what the getY function does is changes the length of the raycast direction vector depending on where the player is moving, so if they were moving forward, the vector would be 2 studs long, so if anyway exists at 2 studs in front of the player, they will stop moving, if the player was moving to the side, the vector would be 4 studs, and if they were moving backwards the vector would be 2 studs. https://streamable.com/tt22y8