How to make a camera system like in flashpoint?

i was playing and i came across a game called flashpoint.

link to game:
https://www.roblox.com/games/13796645198/UPD-FLASHPOINT

one thing that immediately stood out for me was how the camera was moving.

i would love to recreate this effect somehow but im very inexperienced with advanced camera features like this. if anyone could help that would be amazing!

ive got a video here. skip to 23-26 seconds to see alot of angles.
https://vimeo.com/1100118138?share=copy

1 Like

From what I see, I believe there is an invisible part that is at a distance behind the character, and the character’s speed determines that distance. As the speed increases, the part goes further behind the character. The CurrentCamera’s CameraSubject is then set to the part to achieve the effect in the video.

Another way is to set the Humanoid’s CameraOffset dependant on the speed, which could be easier to program.

If you need help programming any of the systems, I can give you some tips.

interesting that actually could be how they did it. i would love some tips since i aint really experienced with these type of things

1 Like
  1. When modifying the camera, you can use RunService.RenderStepped, which is an event that fires before every frame render

  2. We can also grab the player’s speed through their HumanoidRootPart, by accessing it’s AssemblyLinearVelocity property.

  3. When combined, a function like this to modify the camera position can be used.

RunService.RenderStepped:Connect(function()
	local speed = humanoidRootPart.AssemblyLinearVelocity.Magnitude
	humanoid.CameraOffset = Vector3.new(0, 0, speed / 20)
end)

By modifying the Z axis of the CameraOffset, we can choose how far back to move the camera. Keep in mind you need to store the HumanoidRootPart and Humanoid as variables, and the script must be a LocalScript in order to modify the camera.

The system also looks like it modifies the camera’s FieldOfView, so you may need to add that into the script.

If you need further help please ask.

1 Like

This is my recreation (without all the lasers and effects lol):
Flashpoint Camera Recreation.rbxl (56.5 KB)

Begin walking and your speed will slowly increase.

1 Like

i have made this using your idea it looks nice but deffo needs some tweaks

https://vimeo.com/1100128850?share=copy

this is the code i used

local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local player = Players.LocalPlayer
local camera = workspace.CurrentCamera

-- CONFIG
local MIN_FOV = 70
local MAX_FOV = 115
local MAX_SPEED = 300
local FOV_SMOOTH = 0.15
local TILT_SMOOTH = 0.15
local MAX_TILT = math.rad(10)
local CAMERA_Z_OFFSET_FACTOR = 1 / 20

-- STATE
local isActive = false
local currentFOV = MIN_FOV
local currentTilt = 0

camera.CameraType = Enum.CameraType.Custom

ReplicatedStorage:WaitForChild("SuperSpeedFX").OnClientEvent:Connect(function(state)
	isActive = state
	if not state then
		currentFOV = MIN_FOV
		currentTilt = 0
		camera.FieldOfView = MIN_FOV

		local char = player.Character
		local hum = char and char:FindFirstChildOfClass("Humanoid")
		if hum then
			hum.CameraOffset = Vector3.zero
		end
	end
end)

RunService.RenderStepped:Connect(function()
	if not isActive then return end

	local char = player.Character
	local humanoid = char and char:FindFirstChildWhichIsA("Humanoid")
	local hrp = char and char:FindFirstChild("HumanoidRootPart")
	if not humanoid or not hrp then return end

	local speed = hrp.AssemblyLinearVelocity.Magnitude
	local moveDir = humanoid.MoveDirection

	-- FOV
	local targetFOV = MIN_FOV + (speed / MAX_SPEED) * (MAX_FOV - MIN_FOV)
	currentFOV += (targetFOV - currentFOV) * FOV_SMOOTH
	camera.FieldOfView = currentFOV

	-- Tilt
	local strafe = moveDir:Dot(camera.CFrame.RightVector)
	local targetTilt = -strafe * MAX_TILT
	currentTilt += (targetTilt - currentTilt) * TILT_SMOOTH

	-- Apply tilt
	local camPos = camera.CFrame.Position
	local camLook = camera.CFrame.LookVector
	camera.CFrame = CFrame.new(camPos, camPos + camLook) * CFrame.Angles(0, 0, currentTilt)

	humanoid.CameraOffset = Vector3.new(0, 0, speed * CAMERA_Z_OFFSET_FACTOR)
end)

1 Like

You can increase the divisor to make it smoother, or incorporate tweening instead of directly setting the CameraOffset, since it looks like it’s adjusting too quickly.

alright let me try. should i do both? increase the divisor and incorporate tweening?

Try both, and you can always adjust to make it look better.

so would you say here change it to like 40 instead of 20?

local CAMERA_Z_OFFSET_FACTOR = 1 / 20

and then tween it

Yes, you can try increasing to 40 or higher.

watchu think now. i feel like my camera tilt i also have is unsmooth and bad idk if you can help me with that?

https://vimeo.com/1100130884?share=copy

this is the code ive done now

local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local player = Players.LocalPlayer
local camera = workspace.CurrentCamera

-- CONFIG
local MIN_FOV = 70
local MAX_FOV = 115
local MAX_SPEED = 300
local FOV_SMOOTH = 0.15
local TILT_SMOOTH = 0.15
local MAX_TILT = math.rad(10)
local CAMERA_Z_OFFSET_FACTOR = 1 / 40

-- STATE
local isActive = false
local currentFOV = MIN_FOV
local currentTilt = 0

camera.CameraType = Enum.CameraType.Custom

ReplicatedStorage:WaitForChild("SuperSpeedFX").OnClientEvent:Connect(function(state)
	isActive = state
	if not state then
		currentFOV = MIN_FOV
		currentTilt = 0
		camera.FieldOfView = MIN_FOV

		local char = player.Character
		local hum = char and char:FindFirstChildOfClass("Humanoid")
		if hum then
			hum.CameraOffset = Vector3.zero
		end
	end
end)

RunService.RenderStepped:Connect(function()
	if not isActive then return end

	local char = player.Character
	local humanoid = char and char:FindFirstChildWhichIsA("Humanoid")
	local hrp = char and char:FindFirstChild("HumanoidRootPart")
	if not humanoid or not hrp then return end

	local speed = hrp.AssemblyLinearVelocity.Magnitude
	local moveDir = humanoid.MoveDirection

	-- FOV
	local targetFOV = MIN_FOV + (speed / MAX_SPEED) * (MAX_FOV - MIN_FOV)
	currentFOV += (targetFOV - currentFOV) * FOV_SMOOTH
	camera.FieldOfView = currentFOV

	-- Tilt
	local strafe = moveDir:Dot(camera.CFrame.RightVector)
	local targetTilt = -strafe * MAX_TILT
	currentTilt += (targetTilt - currentTilt) * TILT_SMOOTH

	-- Apply tilt
	local camPos = camera.CFrame.Position
	local camLook = camera.CFrame.LookVector
	camera.CFrame = CFrame.new(camPos, camPos + camLook) * CFrame.Angles(0, 0, currentTilt)

	local currentOffset = humanoid.CameraOffset
	local targetOffset = Vector3.new(0, 0, speed * CAMERA_Z_OFFSET_FACTOR)
	humanoid.CameraOffset = currentOffset:Lerp(targetOffset, 0.15)

end)

Have you tried lerping the camera tilt too?

1 Like

i have tried that but i aint the best at that so it didnt work. could you maybe help. ive tweaked the code a bit and i think im close. there are just some things that arent the best. such as the players character can get too far from the camera and when you run backwards the character gets out of sight like i showed in the video.

here is a new video of what i looks like
https://vimeo.com/1100305278?share=copy

here is the code

local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local player = Players.LocalPlayer
local camera = workspace.CurrentCamera

local MIN_FOV = 70
local MAX_FOV = 100
local MAX_SPEED = 300
local FOV_SMOOTH = 0.1
local TILT_SMOOTH = 0.5
local MAX_TILT = math.rad(14)
local CAMERA_Z_OFFSET_FACTOR = 1 / 35
local CAMERA_X_OFFSET = 2
local CAMERA_LAG_SPEED = 0.1

local isActive = false
local currentFOV = MIN_FOV
local currentTilt = 0
local currentCamPos = nil

camera.CameraType = Enum.CameraType.Custom

ReplicatedStorage:WaitForChild("SuperSpeedFX").OnClientEvent:Connect(function(state)
	isActive = state
	if not state then
		currentFOV = MIN_FOV
		currentTilt = 0
		camera.FieldOfView = MIN_FOV
		currentCamPos = nil

		local char = player.Character
		local hum = char and char:FindFirstChildOfClass("Humanoid")
		if hum then
			hum.CameraOffset = Vector3.zero
		end
	end
end)

-- Main camera logic
RunService.RenderStepped:Connect(function(dt)
	if not isActive then return end

	local char = player.Character
	local humanoid = char and char:FindFirstChildWhichIsA("Humanoid")
	local hrp = char and char:FindFirstChild("HumanoidRootPart")
	if not humanoid or not hrp then return end

	local speed = hrp.AssemblyLinearVelocity.Magnitude

	local targetFOV = MIN_FOV + (speed / MAX_SPEED) * (MAX_FOV - MIN_FOV)
	currentFOV += (targetFOV - currentFOV) * FOV_SMOOTH
	camera.FieldOfView = currentFOV

	local camRight = camera.CFrame.RightVector
	local lateral = hrp.AssemblyLinearVelocity:Dot(camRight)
	local targetTilt = -math.clamp(lateral / 30, -1, 1) * MAX_TILT
	local tiltDelta = targetTilt - currentTilt
	currentTilt += tiltDelta * (TILT_SMOOTH * dt) * 2

	local currentOffset = humanoid.CameraOffset
	local targetOffset = Vector3.new(CAMERA_X_OFFSET, 0, speed * CAMERA_Z_OFFSET_FACTOR)
	humanoid.CameraOffset = currentOffset:Lerp(targetOffset, 0.15)

	local desiredCamPos = camera.CFrame.Position
	if not currentCamPos then
		currentCamPos = desiredCamPos
	end
	currentCamPos = currentCamPos:Lerp(desiredCamPos, CAMERA_LAG_SPEED)

	local camLook = camera.CFrame.LookVector
	camera.CFrame = CFrame.new(currentCamPos, currentCamPos + camLook) * CFrame.Angles(0, 0, currentTilt)
end)