Custom Poppercam

I just need someone to tell me how I could script a Popper like the one roblox uses.

These are accessible by running the game and looking inside StarterPlayer - you will see that PlayerModule and CameraModule gets added.

Otherwise you can view the source online:

  • The most recent version of PopperCam found here
  • The legacy version of PopperCam found here

The general idea is that for each frame:

  1. Cast 1 or more rays - learn about raycasting here - from CameraSubject to the desired position of the camera, or use the in built Camera::GetPartsObscuringTarget() found here - ignore any intersections that belong to the player or have some other condition

  2. Use the intersection point as the new camera position such that:

    • if the new camera position is less than some desired distance you could force the player into first person here
    • otherwise, you should interpolate from the last camera position to the new position over a few frames so there isn’t a big jump visible to the player
  3. Repeat

3 Likes

After fixing many bugs that my script has, i’ve come up with this:

local Poppercam = {}

local camera = workspace.CurrentCamera
local character = game.Players.LocalPlayer.Character or game.Players.LocalPlayer.CharacterAdded:Wait()
local rootPart = character:WaitForChild("HumanoidRootPart")

local CameraVariable = require(script.Parent.CameraVariables)

local function RotateVector2(vector, angle)
	local x = vector.X * math.cos(angle) - vector.Y * math.sin(angle)
	local y = vector.X * math.sin(angle) + vector.Y * math.cos(angle)
	return Vector2.new(x, y)
end

local function RaycastFromCamera(direction)
	local viewportSize = camera.ViewportSize
	local screenCenter = Vector2.new(viewportSize.X / 2, viewportSize.Y / 2)
	local ray = camera:ScreenPointToRay(screenCenter.X, screenCenter.Y)
	local rotatedDirection = RotateVector2(Vector2.new(ray.Direction.X, ray.Direction.Z), direction)
	local rotatedRay = Ray.new(ray.Origin, Vector3.new(rotatedDirection.X, ray.Direction.Y, rotatedDirection.Y))
	local hitPart, hitPosition, hitNormal = workspace:FindPartOnRay(rotatedRay)
	return rotatedRay, hitPart, hitPosition, hitNormal
end

function Poppercam.Raycast()
	local numRays = 8
	local maxDistance = 1000
	local closestDistance = math.huge
	local closestRay = nil
	
	for i = 1, numRays do
		local angle = math.pi * 2 * (i - 1) / numRays
		local ray, hitPart, hitPosition, hitNormal = RaycastFromCamera(angle)

		if hitPart then
			local distance = (hitPosition - camera.CFrame.Position).Magnitude
			if distance < closestDistance then
				closestDistance = distance
				closestRay = ray
			end
		end
	end
	
	if closestRay then
		local newCameraPosition = closestRay.Origin + closestRay.Direction * (closestDistance - 1) 
		local cameraFocus = newCameraPosition + camera.CFrame.LookVector * 100

		camera.CFrame = CFrame.lookAt(newCameraPosition, cameraFocus)
	end
end

return Poppercam

The problem is that it for some reason still lets the camera pass through the wall

force to zoom in if the focus (workspace.currentcamera.Focus) position cant find the camera.CFrame.p like a raycast from the head to the camera, if theres something on the way then you zoom (forced), you can use the magnitude between each other for length

but idk how to also help with this since u prob need it instant

1 Like

I can probably use my custom zoom to achieve this

alr but it still would cause the bug, the camera still would noclip unless idk you mess up with the zoom distance, like if the wall is 4 stud long from the character and u put ur camera on it, it’ll offset idk -4 ???
(im not good at this lol)

im not good at this either, im still trying to figure out the math to actually do this xD

1 Like

idk how to implement the thing you said, but I found out that when the camera clips the wall the rays just stop detecting the wall, so that might be the issue

I’m not sure if I’m able to understand your code fully, but are you casting the ray from the camera to the focus, or from in front of the camera to the camera? If it’s the former, the ray won’t collide with some objects, since it’s being casted from inside the object.