Camera Jittering and Flickering too much

Hi, I’m making a drone system where you can do basic drone movements, along with a camera system. My problem is that there is too much camera jittering whenever the camera switches to the drone.

How would I fix this? Since this is bug is really annoying and I’ve tried looking for some solutions, but I couldn’t find one that helped with this.

Video can be seen here, there’s a lot of flickering of the camera and It’s hard to just ignore.

Here is the code:

task.wait()
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local Lighting = game:GetService("Lighting")

local Tool = script.Parent
local Equipped = false
local Spawned = false
local Player = Players.LocalPlayer
local Character = workspace:WaitForChild(Player.Name)
local Camera = workspace.CurrentCamera
local Mouse = Player:GetMouse()
local camerabottom = script.Parent:WaitForChild("cameraBottom")
local cameraforwardValue = script.Parent:WaitForChild("cameraforward")
local spawnedVal = script.Parent:WaitForChild("spawnedv")

local Event = script:WaitForChild("Control")

----control----
local righteVector = 0
local lefteVector = 0
local up = 0
local down = 0
local forward = 0
local backward = 0
local left = 0
local right = 0
local cameraforward = false
local cameraBottom = false
local release = false
local cameraLerpFactor = 0.1

---------------

-----etc-----
local audioEnableID = "9114641335"
local audioDisableID = "2184540229"

local soundEnable = Instance.new("Sound")
soundEnable.SoundId = "rbxassetid://" .. audioEnableID
soundEnable.Parent = Tool

local soundDisable = Instance.new("Sound")
soundDisable.SoundId = "rbxassetid://" .. audioDisableID
soundDisable.Parent = Tool

local direction
local speed = 7
local minDistance = 10
local restrictBackward = false

local Players = game:GetService("Players")

local player = Players.LocalPlayer
local playerScripts = player:WaitForChild("PlayerScripts")

local PlayerModule = require(playerScripts:WaitForChild("PlayerModule"))
local controls = PlayerModule:GetControls()
local thermgui = Players.LocalPlayer.PlayerGui.drone.Background.Labrel
-------------
local maxRollAngle = 8
local rollSpeed = 15
local maxPitchAngle = 9
local pitchSpeed = 15
local currentRoll = 0
local currentPitch = 0
local resetSpeed = 0.05

local function RollDrone(drone, rollAmount)
	currentRoll = math.clamp(currentRoll + rollAmount, -maxRollAngle, maxRollAngle)
	drone.CFrame = CFrame.new(drone.Position) * CFrame.Angles(math.rad(currentPitch), 0, math.rad(currentRoll))
end

local function PitchDrone(drone, pitchAmount)
	currentPitch = math.clamp(currentPitch + pitchAmount, -maxPitchAngle, maxPitchAngle)
	drone.CFrame = CFrame.new(drone.Position) * CFrame.Angles(math.rad(currentPitch), 0, math.rad(currentRoll))
end

local function ResetRollAndPitch()
	currentRoll = currentRoll - currentRoll * resetSpeed
	currentPitch = currentPitch - currentPitch * resetSpeed
end

local cameraRotation = Camera.CFrame
local cameraPosition = Camera.CFrame.Position

RunService.RenderStepped:Connect(function(deltaTime)
	if Equipped and (cameraforwardValue.Value) then
		local mousePosition = Mouse.Hit.Position
		local cameraPosition = Camera.CFrame.Position
		direction = (mousePosition - cameraPosition).unit * speed

		if (mousePosition - cameraPosition).Magnitude < minDistance then
			direction = Vector3.new()
		end

		Camera.CFrame = CFrame.lookAt(Camera.CFrame.Position, Camera.CFrame.Position + direction)
	end
end)

local function PlaySound(sound)
	sound.Volume = 1
	sound.Pitch = 1
	sound.Parent = Camera
	sound:Play()
end

local function StopSound(sound)
	sound:Stop()
end

local ThermalVisionEnabled = false

local UAVIRColors = Lighting:FindFirstChild("UAVIRColors")

local function AddHighlightToCharacter(peeps)
	local char = peeps.Character
	if char then
		local highlight = Instance.new("Highlight")
		highlight.FillColor = Color3.new(1, 1, 1)
		highlight.OutlineColor = Color3.new(1, 1, 1)
		highlight.FillTransparency = 0
		highlight.Parent = peeps.Character
		return highlight
	end
end

local function RemoveHighlightToCharacter(peeps)
	local chars = peeps.Character
	if chars then
		local highlight = peeps.Character:FindFirstChildOfClass("Highlight")
		if highlight then
			highlight:Destroy()
		end
	end
end

local function EnableThermalVision()
	ThermalVisionEnabled = true
	UAVIRColors.Enabled = true
	for _, persons in ipairs(game.Players:GetPlayers()) do
		AddHighlightToCharacter(persons)
	end
	thermgui.Text = "On"
	PlaySound(soundEnable)
	local colorCorrection = Lighting:FindFirstChild("ColorCorrection")
	local contrast = Lighting:FindFirstChild("Contrast")

	if colorCorrection then
		colorCorrection.Enabled = false
	end

	if contrast then
		contrast.Enabled = false
	end
end

local function DisableThermalVision()
	ThermalVisionEnabled = false
	UAVIRColors.Enabled = false
	for _, persons in ipairs(game.Players:GetPlayers()) do
		RemoveHighlightToCharacter(persons)
	end
	thermgui.Text = "Off"
	PlaySound(soundDisable)
	local colorCorrection = Lighting:FindFirstChild("ColorCorrection")
	local contrast = Lighting:FindFirstChild("Contrast")

	if colorCorrection then
		colorCorrection.Enabled = true
	end

	if contrast then
		contrast.Enabled = true
	end
end

local function ResetHighlightVision()
	ThermalVisionEnabled = false
	UAVIRColors.Enabled = false
	for _, persons in ipairs(game.Players:GetPlayers()) do
		RemoveHighlightToCharacter(persons)
	end
end

local function CameraEnter(drone)
	Camera.CameraType = Enum.CameraType.Attach
	Camera.CoordinateFrame = drone.WorldCFrame
	if Player.PlayerGui:FindFirstChild("drone") then
		Player.PlayerGui.drone.Enabled = false
	end
end

local function CameraExit()
	if not (cameraBottomValue.Value or cameraforwardValue.Value) then
		Camera.CameraType = Enum.CameraType.Custom
		Camera.CameraSubject = Player.Character.Humanoid
		if Player.PlayerGui:FindFirstChild("drone") then
			Player.PlayerGui.drone.Enabled = false
		end
	end
end

local function CheckDrone(drone)
	return drone:FindFirstChild("BodyPosition")
end

local function DroneControl()
	local drone = workspace.WorkDrone:FindFirstChild(Player.Name)
	if drone then
		if drone:FindFirstChild("Drone") then
			if CheckDrone(drone.Drone) then
				if cameraforward then
					CameraEnter(drone.Drone.ForwardCam)
				elseif cameraBottom then
					CameraEnter(drone.Drone.BottomCam)
				else
					CameraExit()
				end

				local rightvec = drone.Drone.CFrame.RightVector * righteVector
				local leftvec = -drone.Drone.CFrame.RightVector * lefteVector
				drone.Drone.BodyPosition.Position = drone.Drone.BodyPosition.Position + Vector3.new(0, (up - down), 0)
				drone.Drone.BodyPosition.Position = drone.Drone.BodyPosition.Position + drone.Drone.CFrame.LookVector * (forward - backward)
				drone.Drone.BodyPosition.Position = drone.Drone.BodyPosition.Position + rightvec
				drone.Drone.BodyPosition.Position = drone.Drone.BodyPosition.Position + leftvec
				
				if Equipped then
					local targetRoll = 0
					local targetPitch = 0
					local rollInput = 0
					local pitchInput = 0
					local rollLerpFactor = 0.1
					local pitchLerpFactor = 0.1

					if UserInputService:IsKeyDown(Enum.KeyCode.D) then
						targetRoll = -maxRollAngle 
					elseif UserInputService:IsKeyDown(Enum.KeyCode.A) then
						targetRoll = maxRollAngle
					else
						targetRoll = 0
					end

					if UserInputService:IsKeyDown(Enum.KeyCode.W) then
						targetPitch = -maxPitchAngle
					elseif UserInputService:IsKeyDown(Enum.KeyCode.S) then
						targetPitch = maxPitchAngle
					else
						targetPitch = 0 
					end

					rollInput = math.clamp(rollInput + (targetRoll - rollInput) * rollLerpFactor, -maxRollAngle, maxRollAngle)
					pitchInput = math.clamp(pitchInput + (targetPitch - pitchInput) * pitchLerpFactor, -maxPitchAngle, maxPitchAngle)
					
					RollDrone(drone.Drone, rollInput)
					PitchDrone(drone.Drone, pitchInput)
					
					if not (rollInput ~= 0 or pitchInput ~= 0) then
						ResetRollAndPitch()
					end

					if release then
						drone.Drone:FindFirstChild("Release"):FireServer()
					end
			else
				CameraExit()
			end
		else
			CameraExit()
		end
	end
	end
end



UserInputService.InputBegan:Connect(function(Input, GameProcessed)
	if GameProcessed then
		return
	end
	if not UserInputService.TouchEnabled and Equipped then
		if Input.UserInputType == Enum.UserInputType.MouseButton1 then
			if not Spawned and (Mouse.Hit.Position - Character.HumanoidRootPart.Position).Magnitude <= 20 then
				--print("a")
				Event:FireServer("Spawn", Mouse.Hit)
				Spawned = true
				spawnedVal = true
			end
		end
		if Input.KeyCode == Enum.KeyCode.R then
			up = 1
		end
		if Input.KeyCode == Enum.KeyCode.F then
			down = 1
		end
		if Input.KeyCode == Enum.KeyCode.W then
			forward = 1
		end
		if Input.KeyCode == Enum.KeyCode.S then
			backward = 1
		end
		if Input.KeyCode == Enum.KeyCode.A then
			lefteVector = 1
		end
		if Input.KeyCode == Enum.KeyCode.D then
			righteVector = 1
		end
		if Input.KeyCode == Enum.KeyCode.T then
			if spawnedVal == true then
				if cameraforward then
					cameraforward = false
					cameraforwardValue.Value = false
					restrictBackward = false
				else
					cameraforward = true
					cameraforwardValue.Value = true
					cameraBottom = false
					cameraBottomValue.Value = false
					restrictBackward = true
				end
			end
		end
		if Input.KeyCode == Enum.KeyCode.V then
			if cameraBottom then
				cameraBottom = false
				cameraBottomValue.Value = false
			else
				cameraBottom = true
				cameraBottomValue.Value = true
				cameraforward = false
				cameraforwardValue.Value = false
			end
		end
		if Input.KeyCode == Enum.KeyCode.B then
			release = true
			wait(0.1)
			release = false
		end
		if (cameraBottom or cameraforward) and Input.KeyCode == Enum.KeyCode.H then
			if ThermalVisionEnabled then
				DisableThermalVision()
				script.Parent.thermalvis.Value = false
			else
				EnableThermalVision()
				script.Parent.thermalvis.Value = true
			end
		end
	end
	if cameraforwardValue.Value == true or cameraBottomValue.Value == true then
		controls:Disable()
	else
		controls:Enable()
	end
end)

UserInputService.InputEnded:Connect(function(Input, GameProcessed)
	if GameProcessed then
		return
	end
	if not UserInputService.TouchEnabled then
		if Input.KeyCode == Enum.KeyCode.R then
			up = 0
		end
		if Input.KeyCode == Enum.KeyCode.F then
			down = 0
		end
		if Input.KeyCode == Enum.KeyCode.W then
			forward = 0
		end
		if Input.KeyCode == Enum.KeyCode.S then
			backward = 0
		end
		if Input.KeyCode == Enum.KeyCode.A then
			lefteVector = 0
		end
		if Input.KeyCode == Enum.KeyCode.D then
			righteVector = 0
		end
	end
end)

Tool.Equipped:Connect(function()
	Equipped = true
end)

Tool.Unequipped:Connect(function()
	local cameraforward = false
	local cameraBottom = false
	CameraExit()
	Equipped = false
	DisableThermalVision()
end)

RunService.RenderStepped:Connect(DroneControl)

RunService.Heartbeat:Connect(function()
	if Equipped and ((not cameraforward and not cameraBottom) or (cameraforwardValue and cameraBottomValue and not cameraforwardValue.Value and not cameraBottomValue.Value)) then
		ResetHighlightVision()
	end
end)

Thanks in advance!

I do not see deltaTime being used anywhere in this function. The delta is what helps keep consistency with frame-bound systems since it corrects for fluctuations in the frametime.

Do something like this:

direction = (mousePosition - cameraPosition).unit * speed * deltaTime*60

If the FPS drops, the deltaTime will increase the speed to correct for it. The same happens in reverse; if the FPS increases then the speed will decrease


Also, a small note on improving your code’s performance, you should run the distance check first and then calculate the direction. That way it can skip the calculation when possible instead of having to calculate it all the time.

		local displacement = (mousePosition - cameraPosition)
		direction = if displacement.Magnitude < minDistance then Vector3.zero else displacement.unit * speed * deltaTime*60
1 Like

Thank you, but the jittering issue is still there.
I’ve messed with the code and when I removed these 2 lines of code, the jittering issue isn’t there anymore. Though I want to keep this feature.

RollDrone(drone.Drone, rollInput)
PitchDrone(drone.Drone, pitchInput)

Make sure they also compensate for frametime fluctuations! Anything that involves speed and time need to adjust for one another.

Sorry but what does that mean ? Do I have to mess with the code you pointed out above?
Since my issue here is the camera jittering, and I’ve tried removing that piece of code you pointed out and it didn’t fix the camera jittering.

What I meant is you need to apply the example I shown with all math-related stuff going on in your code that updates on a frame-by-frame basis. Calculating camera pitch and roll involves setting a speed and it is done every frame, so you need to adjust it to respect the frametime fluctuations

If the camera stops jittering after you remove the lines of code that adjusts the drone, then it’s definitely the drone’s math that’s causing the issue.
Can you show what the drone looks like outside the drone’s perspective when it’s flying?