How can I make the camera follow the player smoothly? [Check Video Example To Understand]

Hi Developers! Receently I have tried making an advanced sprinting system, which includes some really cool cinematic effects (which can be toggled as well!). I’ve come to a problem though. Most of the effects seem a little too “sharp” and instant. The camera is not smooth and creates this bad looking effect when jumping or moving in different directions fast. Please look at the example videos. This system was inspired by another developer, I saw a random post on Twitter and decided to re-create the same thing, and maybe even open-source it once its ready.

This is the other developer’s video. His system is pretty advanced and looks butter smooth! Look at the times where he is jupming, the effect is very noticable.

Now look at my re-creation. It looks almost identical with the only difference that there isn’t the fast dash when begining to sprint and I actually allow the player to unlock his cursor. (with other small detailes like different offsets and FOVs)
Here is another reference to how exactly the camera should move like:
Here is another example of how the camera should move:

Here is another example of what I’m talking about:

I’ve got no idea how to do. I want to point out that I’m using multiple scripts to control the camera, running and other aspects of this system. In total I use about 5 models to make this work. It is really complicated, the last part I have left to do is actually the stamina display and the smooth camera, I know how to display the stamina, I just want to finish the camera first because it took the longest to reproduce. Thanks in advance if you tried helping me.

Note: Camera has to be able to rotate without glitching, aka it should be able to do stuff like in the first example video. I have seen a lot of people only needing the camera object to follow another part which is not what i need. I also need it to rotate properly around the player, else its useless to have it.

Please ignore the weird body shaking while I’m in Lock Mode. I have recorded this in Studio. It’s normal for this to happen, it depends on the size of the screen you are playing on. Smaller == More Weird Shaking.

12 Likes

It looks like a delay before moving the camera after you moved.
So it looks like
-Check for major camera move
-Delay a second
-Tween camera
-Maybe a overshoot/sway effect

Also when it dashes, there’s no delay but just a slower tween. Maybe have the camera look for a bool or attribute to see if you’re dashing or not to determine the type of movement

1 Like

That is not really what I’m looking for. He rotates the camera in all angles and it follows it no matter what. Your suggestion is to make the camera go smooth only when you need, aka tween the offset which I already did. I suggest watching the third example a few times then getting back to the first one to notice the smoothing. I’ve learned about learping from a friend about an hour ago as of writing this, but he couldn’t explain it very well or find the correct code needed for my feature. This is a very small detail that is not even noticable, but once you notice it, you won’t be able to play normally without it. That is why I want to add it into my game. This game is mostly going to be open-source since this is my first big project.

If you can get the blue block to follow the red block like that, what’s stopping you from making the camera follow the player the same way? Is it combining it with rotation that’s a problem?

1 Like

Exactly, if you try to do that, when you try to rotate the camera it will go crazy and start spinning around. I don’t know how to make it rotate. That is why I’m asking here.

Everyone! I’ve found a temporary solution. It is kinda comlpicated, script will be below for people wanting to use this. Remember that its edited and if you just put it in your start character folder it wont work. I use my custom data handler module. To make it work simply remove the if controller:Get("combat.isRunning") == true then and the module variables. Else it will error!

repeat
	wait()
until game:IsLoaded()

local partstohide = { -- add all parts to hide, just hiding the ones for r6 because I'm lazy :P accessories don't work, u gotta add them manually or through a for loop! (this is for first person, if u dont add accessories then u will be able to see them in first person! also any other part will be visible while ur first person.)
	"Right Arm",
	"Left Leg",
	"Right Leg",
	"Torso",
	"Head"
}

wait(1)

local MAX_LENGTH = 900000

local debounce = false
local cooldown = 100
local currenttime = 100

local isMouseLocked = false

local player = game.Players.LocalPlayer
local character = player.Character

local Mouse = game:GetService("Players").LocalPlayer:GetMouse()

local Cam = nil

local count = 0
local TweenPos = character:WaitForChild("HumanoidRootPart").CFrame * CFrame.new(0, 2, 0)

local function Lerp(a, b, c)
	return a + (b - a) * c
end

function createCam(char)
	character = player.Character

	if currenttime < cooldown then return end

	count += 1

	Cam = nil

	currenttime = 0

	Cam = Instance.new("Part", char)
	Cam.Parent = char
	Cam.Transparency = 1
	Cam.Anchored = true
	Cam.CanCollide = false
	Cam.Name = "SmoothCam" .. count
	Cam.CFrame = character:WaitForChild("HumanoidRootPart").CFrame * CFrame.new(0, 2, 0)

	local camera = workspace.CurrentCamera

	if char:WaitForChild("HumanoidRootPart", 5) and char:WaitForChild(Cam.Name, 5) then
		camera.CameraSubject = Cam
	else
		createCam(char)
	end

	TweenPos = character:WaitForChild("HumanoidRootPart").CFrame * CFrame.new(0, 2, 0)
end

createCam(character)

local tweenService = game:GetService("TweenService")
local runService = game:GetService("RunService")

player.CharacterAdded:Connect(function(char)
	TweenPos = char:WaitForChild("HumanoidRootPart").CFrame * CFrame.new(0, 2, 0)
	wait(1)
	Cam:Destroy()
	createCam(char)
end)

local function GetUpdatedCameraCFrame(ROOT, CAMERA)
	return CFrame.new(ROOT.Position, Vector3.new(CAMERA.CFrame.LookVector.X * MAX_LENGTH, ROOT.Position.Y, CAMERA.CFrame.LookVector.Z * MAX_LENGTH))
end

runService.RenderStepped:Connect(function()

	currenttime = math.clamp(currenttime + 1, 0, cooldown)
	if character:FindFirstChild("HumanoidRootPart") then
		local camera = workspace.CurrentCamera

		TweenPos = character:WaitForChild("HumanoidRootPart").CFrame * CFrame.new(0, 2, 0)
		if not Cam or not character:FindFirstChild(Cam.Name) then
			createCam(character)
		end
		if character:FindFirstChild(Cam.Name) or Cam ~= nil then
			local transparency = 2-((camera.CFrame.Position - Cam.CFrame.Position).Magnitude * 2)
			for i, v in pairs(character:GetChildren()) do
				if table.find(partstohide, v.Name) and v:IsA("BasePart") then
					v.Transparency = transparency
				end
			end
			local PlayerManager = require(game:GetService('ReplicatedStorage'):WaitForChild('Modules'):WaitForChild('DataHandler'))
			local controller = PlayerManager.new(player)
			local Build = controller:build()
			local tween
			if controller:Get("combat.isRunning") == true then
				tween = tweenService:Create(Cam, TweenInfo.new(1, Enum.EasingStyle.Back), {CFrame = TweenPos})
			else
				tween = tweenService:Create(Cam, TweenInfo.new(0.75, Enum.EasingStyle.Back), {CFrame = TweenPos})
			end
			tween:Play()
		else
		end
	end
end)

My goal is still not achieved so I will not post this as a solution since this is running every frame and might lag everything. It is pretty smooth. For people asking why I check if the player is moving: I want the camera to get smoother once the player starts running, if he doesn’t then the smoothing effect is reduced so the camera isn’t that slow.

For people that know how to improve it please tell me. I will be open sourcing this in the future when I make all of my scripts for my future game. (with a tutorial on how to set-up!).

2 Likes

Here is a showcase. Sry I forgot to attach it.

1 Like

Everyone! I found the solution. I reworked the previous script from above. Now it works. If anyone still doesn’t understand how to make it feel free to leave a reply. I will explain it better. My code will be below, keep in mind that its edited and will not work if you just paste it in since it uses other module scripts.

repeat
	wait()
until game:IsLoaded()

wait(1)

local MAX_LENGTH = 900000

local debounce = false
local cooldown = 100
local currenttime = 100

local isMouseLocked = false

local player = game.Players.LocalPlayer
local character = player.Character

local Mouse = game:GetService("Players").LocalPlayer:GetMouse()

local Cam = nil

local count = 0
local TweenPos = character:WaitForChild("HumanoidRootPart").CFrame * CFrame.new(0, 2, 0)

local function Lerp(a, b, c)
	return a + (b - a) * c
end

function createCam(char)
	character = player.Character

	if currenttime < cooldown then return end

	count += 1

	Cam = nil

	currenttime = 0

	Cam = Instance.new("Part", char)
	Cam.Parent = char
	Cam.Transparency = 1
	Cam.Anchored = true
	Cam.CanCollide = false
	Cam.Name = "SmoothCam" .. count
	Cam.CFrame = character:WaitForChild("HumanoidRootPart").CFrame * CFrame.new(0, 2, 0)

	local camera = workspace.CurrentCamera

	if char:WaitForChild("HumanoidRootPart", 5) and char:WaitForChild(Cam.Name, 5) then
		camera.CameraSubject = Cam
	else
		createCam(char)
	end

	TweenPos = character:WaitForChild("HumanoidRootPart").CFrame * CFrame.new(0, 2, 0)
end

createCam(character)

local tweenService = game:GetService("TweenService")
local runService = game:GetService("RunService")
local accessories = {}
player.CharacterAdded:Connect(function(char)
	TweenPos = char:WaitForChild("HumanoidRootPart").CFrame * CFrame.new(0, 2, 0)
	wait(1)
	Cam:Destroy()
	createCam(char)
	accessories = char.Humanoid:GetAccessories()
end)

local function GetUpdatedCameraCFrame(ROOT, CAMERA)
	return CFrame.new(ROOT.Position, Vector3.new(CAMERA.CFrame.LookVector.X * MAX_LENGTH, ROOT.Position.Y, CAMERA.CFrame.LookVector.Z * MAX_LENGTH))
end

local JumpedOrFalling = 0
local TimePassed = 0
character.Humanoid:GetPropertyChangedSignal("FloorMaterial"):Connect(function()
	if character.Humanoid.FloorMaterial == Enum.Material.Air then
		JumpedOrFalling = 1
	else
		JumpedOrFalling = 0
	end
end)

runService.RenderStepped:Connect(function()

	currenttime = math.clamp(currenttime + 1, 0, cooldown)
	if character:FindFirstChild("HumanoidRootPart") then
		local camera = workspace.CurrentCamera

		TweenPos = character:WaitForChild("HumanoidRootPart").CFrame * CFrame.new(0, 2, 0)
		if not Cam or not character:FindFirstChild(Cam.Name) then
			createCam(character)
		end
		if character:FindFirstChild(Cam.Name) or Cam ~= nil then
			local PlayerManager = require(game:GetService('ReplicatedStorage'):WaitForChild('Modules'):WaitForChild('DataHandler'))
			local controller = PlayerManager.new(player)
			local Build = controller:build()
			local tween
			if controller:Get("combat.isRunning") and JumpedOrFalling == 0 then
				tween = tweenService:Create(Cam, TweenInfo.new(1, Enum.EasingStyle.Back), {CFrame = TweenPos})
			elseif not controller:Get("combat.isRunning") and JumpedOrFalling == 0 then
				tween = tweenService:Create(Cam, TweenInfo.new(0.75, Enum.EasingStyle.Back), {CFrame = TweenPos})
			elseif JumpedOrFalling == 1 then
				tween = tweenService:Create(Cam, TweenInfo.new(0.45, Enum.EasingStyle.Back), {CFrame = TweenPos})
			end
			tween:Play()
		else
		end
	end
end)

If this topic gets closed and you still need help feel free to message me. I will reply, don’t worry I’m not like other people here.

10 Likes

do you mind give me the module script?:sweat_smile:
I cant figure it out how it works. If you can’t its okay

2 Likes

Hi there,

I’d like to implement this on a game of mine and I feel like I ought ask for permission. Also, as Starlight pointed out, it’d be great if you could share the Module. I’m sure people’d use if you put it on a example place or GitHub Repository. Good luck on that and

thanks in advance hehe.

This is a reply both to @UMadara10 and @FOCUSXZV2. Yea, I can open-source it and make a brief tutorial on how to use it. It’s not really that complicated, I just made some if statements to check if the player is running, falling or just walking, you can add more statements if you want different rates. For example you may want to completely disable it if the player is falling or just walking. You can experiment with it. Ill try to open-source it tomorrow. And also yes, you can use it in your own game as long as it does not violate the Roblox Community Standards.

Edit 1: I will notify both of you when I make a post about the module. I will just make a reply here.
Edits 2 and 3: I made the wrong reply with a wrong topic. I referred to another module which and post I made before this. I fixed the message because it was literally about another module which has no connection to this one.

2 Likes

Model is out now!

Everyone who are looking for the code, just go there.

7 Likes

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