Emulating the Legacy Follower Camera from 2007

Hello:

I am currently in the process of developing a game that emulates the legacy follower camera that was present in versions of Roblox around 2007. Unfortunately, I am not very experienced with camera manipulation, particularly the mathematical operations involved therein, so the script I have created does not presently work as intended. The current follower camera (that is, when workspace.CurrentCamera.CameraType is set to Enum.CameraType.Follow) is not a usable alternative as there is a noticeable delay in automatic camera rotation after the right mouse button is released and the zooming behavior of the current follower camera is markedly different from that in 2007.

The following shows the intended behavior:

The following shows the actual behavior:

As shown, the height of the camera in the second video changes at an exponential rate when moving in a constant direction and the zooming behavior has noticeable flaws. Neither of these are intended, as demonstrated by their absence in the first video.

The script that causes the actual behavior, as shown in the second video, is as follows:

local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
local character = player.Character
local characterHead = character:WaitForChild("Head")
player.CharacterAdded:Connect(function(newCharacter)
	character = newCharacter
	characterHead = newCharacter:WaitForChild("Head")
end)

local cameraDistance = 10 --The distance of the camera from the character's head
local currentZoom = 2 --The number of times the mouse wheel must be scrolled backward in order to achieve the desired zoom
function updateStep(direction)
	if characterHead then
		currentZoom = math.clamp(math.round(currentZoom - direction), 0, 15)
		local cameraPosY = 3^(currentZoom - 3)
		cameraDistance = ((currentZoom^2) + (cameraPosY^2))^(1/2)
		return characterHead.CFrame:ToWorldSpace(CFrame.new(currentZoom, cameraPosY, 0)).Position
	end
end
userInputService.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseWheel then
		camera.CFrame = CFrame.new(updateStep(input.Position.Z))
	end
end)

local lastHeadPosition = Vector3.new() --The world position of the character's head as of the previous heartbeat
runService:BindToRenderStep("CameraConnection", Enum.RenderPriority.Camera.Value, function()
	if character and characterHead then
		local cameraFinalCFrame = CFrame.lookAt(camera.CFrame.Position - Vector3.new(0, lastHeadPosition.Y - characterHead.Position.Y, 0) --[[+ (characterHead.Position - lastPosition)]], characterHead.Position, Vector3.new(0, 1, 0))
		local cameraOriginalCFrame = camera.CFrame
		local cameraOrientation = {cameraFinalCFrame:ToEulerAnglesXYZ()}
		local originalCameraOrientation = {cameraOriginalCFrame:ToOrientation()}
		if not userInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) then
			--cameraFinalCFrame = CFrame.new(cameraFinalCFrame.Position) * CFrame.Angles(--[[originalCameraOrientation[1]] 0, cameraOrientation[2], originalCameraOrientation[3])
		end
		local distanceFromHead = (camera.CFrame.Position - characterHead.Position).Magnitude
		--if distanceFromHead > cameraDistance then
		cameraFinalCFrame = cameraFinalCFrame:ToWorldSpace(CFrame.new(0, 0, cameraDistance - distanceFromHead))
		--end
		camera.CFrame = cameraFinalCFrame
		lastHeadPosition = characterHead.Position
	end
end)

To clarify, I understand what is causing the camera to behave in the manner shown, but I do not know how to correct that behavior.

One similar topic was found on the Developer Forum, but no solution is present.

Any help achieving the desired behavior will be greatly appreciated!

4 Likes

Pretty sure it’s just the follow camera mode you set in the settings:

1 Like

I would like to request that you read this excerpt from my original post:

1 Like

very very sorry I just skim read it. Unfortunately i can not help as I am a crap scripter with camera

1 Like

That’s alright; thanks for the reply.

2 Likes

I think you need to dampen the curve for the height (or you could you predetermined values although probably not the best idea)
Also just pointing out your implementation does not take into account collision with parts which you can probably do with a raycast from the head

Thank you for the reply:

I will definitely adjust the curve, since that seems to be inaccurate and, as you stated, it does not currently take part collisions into account. That behavior will be implemented at some point in the future, likely using raycasts, as you suggested. My main concern as of now, though, is the change in height of the camera when moving in a constant direction, but I do appreciate your advice regarding the zoom curve.

Alright just tested and using local cameraPosY = 1.4^(currentZoom - 3) makes the curve much more similar to the desired result although you’ll need to play around with the values to get it just right

2 Likes

Thank you very much; that is, indeed, much better than it was. It will need a few small adjustments, but otherwise zooming now works well.

I redid the code to move the cframe with the player before calculating the orientation and it seems to work correctly now
Give it a try

runService:BindToRenderStep("CameraConnection", Enum.RenderPriority.Camera.Value, function()
	if character and characterHead then
		local headMovement = characterHead.Position - lastHeadPosition -- calculate the movement of the head since last frame
		local newCameraPosition = camera.CFrame.Position + headMovement -- move the camera with the head an equal distance
		local cameraFinalCFrame = CFrame.lookAt(newCameraPosition, characterHead.Position, Vector3.new(0, 1, 0))

		local distanceFromHead = (newCameraPosition - characterHead.Position).Magnitude
		if distanceFromHead > cameraDistance then
			cameraFinalCFrame = cameraFinalCFrame:ToWorldSpace(CFrame.new(0, 0, cameraDistance - distanceFromHead))
		end

		camera.CFrame = cameraFinalCFrame
		lastHeadPosition = characterHead.Position
	end
end)
2 Likes

Note: The following on XZ is lost so you’ll need to readd that
I can try if you wish

Thank you again; this works quite well. However, I could not but help noticing that the camera no longer rotates to face the character’s head on the X or Z axes, as you noted. I will try to re-implement that, but you are free to do so as well if you want.

I’ve made some small modifications to my original script and the camera now appears to work as intended with the exception of some slight shaking, which I will be working to correct. Here’s the updated script:

runService:BindToRenderStep("CameraConnection", Enum.RenderPriority.Camera.Value, function()
	if character and characterHead then
		local cameraFinalCFrame = CFrame.lookAt(camera.CFrame.Position - Vector3.new(0, lastHeadPosition.Y - characterHead.Position.Y, 0) --[[+ (characterHead.Position - lastPosition)]], characterHead.Position, Vector3.new(0, 1, 0))
		local cameraOriginalCFrame = camera.CFrame
		local cameraOrientation = {cameraFinalCFrame:ToEulerAnglesXYZ()}
		local originalCameraOrientation = {cameraOriginalCFrame:ToOrientation()}
		if not userInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) then
			--cameraFinalCFrame = CFrame.new(cameraFinalCFrame.Position) * CFrame.Angles(--[[originalCameraOrientation[1]] 0, cameraOrientation[2], originalCameraOrientation[3])
		end
		local distanceFromHead = (camera.CFrame.Position - characterHead.Position).Magnitude
		cameraFinalCFrame = CFrame.new(cameraFinalCFrame.X, characterHead.CFrame.Y + cameraPosY, cameraFinalCFrame.Z) * cameraFinalCFrame.Rotation
		--if distanceFromHead > cameraDistance then
		cameraFinalCFrame = cameraFinalCFrame:ToWorldSpace(CFrame.new(0, 0, cameraDistance - distanceFromHead))
		--end
		cameraFinalCFrame = CFrame.lookAt(cameraFinalCFrame.Position, characterHead.Position)
		camera.CFrame = cameraFinalCFrame
		lastHeadPosition = characterHead.Position
	end
end)

Of note is that is that I made cameraPosY a global variable in this version of the script.

Thank you for your help.

The shaking is probably from the head bobbing while walking
You’ll need to figure out how to stabilize the position the camera uses to focus

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