How to tilt camera smoothly based on the players movement

Hello! I’m trying to make a movement system where the camera rotates based on the player’s movement (like if you press D, your camera rotates to the right, etc.) I found a script in the DevForum, but I do not know how to make it smooth. What the script does is intended, but it just snaps into place.

Script (NOT MINE):

local camera = workspace.Camera
local ts = game:GetService('TweenService')

local player = game:GetService("Players").LocalPlayer
local char = player.Character or player.CharacterAdded:wait()

local runService = game:GetService("RunService")

runService.RenderStepped:connect(function()
	local humanoid = char:FindFirstChild("Humanoid")

	if humanoid then
		local a = false
		local x,y,z = camera.CFrame:ToEulerAnglesXYZ()
		if game:GetService("UserInputService"):IsKeyDown(Enum.KeyCode.A) then
			a = true
			camera.CFrame = CFrame.new(camera.CFrame.Position) * CFrame.Angles(x,y,z) * CFrame.Angles(0,0,math.rad(0.5))
		end
		if game:GetService("UserInputService"):IsKeyDown(Enum.KeyCode.D) then
			a = true
			camera.CFrame = CFrame.new(camera.CFrame.Position) * CFrame.Angles(x,y,z) * CFrame.Angles(0,0,math.rad(-0.5))
		end
		if a == false then
			camera.CFrame = CFrame.new(camera.CFrame.Position) * CFrame.Angles(x,y,z)
		end
	end
end)

Question: How do I smoothly tilt camera, based on the player’s movement?

5 Likes

You should use TweenService or :Lerp.
It’s honestly your choice to choose but I would personally use TweenService as you can do some pretty fancy animations with it.

Here you go:

local camera = workspace.Camera
local ts = game:GetService('TweenService')

local player = game:GetService("Players").LocalPlayer
local char = player.Character or player.CharacterAdded:wait()

local runService = game:GetService("RunService")

local TweenInfo = TweenInfo.new(
0.1, -- Time you want the tween to finish in seconds, i.e 5 would be 5 seconds, 0.2 would be 0.2 milliseconds
Enum.EasingStyle.Linear, -- Type of animation, check out https://developer.roblox.com/en-us/api-reference/enum/EasingStyle for more information about tween types
Enum.EasingDirection.Out, -- Doesn't really matter, but I would do more in depth research if I were you
0, -- repeat count
false, -- bool reverses
0 -- delay time
)

runService.RenderStepped:connect(function()
	local humanoid = char:FindFirstChild("Humanoid")

	if humanoid then
		local a = false
		local x,y,z = camera.CFrame:ToEulerAnglesXYZ()
		if game:GetService("UserInputService"):IsKeyDown(Enum.KeyCode.A) then
			a = true
			ts:Create(camera,TweenInfo,{CFrame = CFrame.new(camera.CFrame.Position) * CFrame.Angles(x,y,z) * CFrame.Angles(0,0,math.rad(0.5))}):Play()
		end
		if game:GetService("UserInputService"):IsKeyDown(Enum.KeyCode.D) then
			a = true
			ts:Create(camera,TweenInfo,{CFrame = CFrame.new(camera.CFrame.Position) * CFrame.Angles(x,y,z) * CFrame.Angles(0,0,math.rad(-0.5))}):Play()
		end
		if a == false then
			ts:Create(camera,TweenInfo,{CFrame = CFrame.new(camera.CFrame.Position) * CFrame.Angles(x,y,z)}):Play()
		end
	end
end)

I tested it, and it works well, although it’s very subtle, the first time I tested it I thought the script wasn’t even working.


Change the circled values to a larger number if you want it to be noticeable, if you change it to something and it doesn’t seem to tween smoothly, make the first value in TweenInfo to a larger number, although go by milliseconds and not by seconds, as that’s too drastic of a change. Just play around with those 3 numbers until you get something satisfactory.

I doubt this changes any performance at all but if you have serious performance issues in your game you could consider changing the tweens into variables and playing them when needed, but I don’t think it makes much of a difference.

Have a great day!

1 Like

Tween will be the best bet. Roblox default camera scripts utilize a tween to get the camera to smoothly move to the new position. Lerp will not make it move smoothly, Lerp is just a easy way to move between two values by a percentage. Its not a gradual change like a tween would be.

local tweens = game:GetService("TweenService")
local run = game:GetService("RunService")
local userInput = game:GetService("UserInputService")

local camera = workspace.CurrentCamera
local info = TweenInfo.new(0.5, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)

local function doTween(degrees)
	local tween = tweens:Create(camera, info, {CFrame = camera.CFrame * CFrame.Angles(0, 0, math.rad(degrees))})
	tween:Play()
end

run.RenderStepped:Connect(function()
	if userInput:IsKeyDown(Enum.KeyCode.A) and userInput:IsKeyDown(Enum.KeyCode.D) then
		doTween(0)
	elseif userInput:IsKeyDown(Enum.KeyCode.A) then
		doTween(5)
	elseif userInput:IsKeyDown(Enum.KeyCode.D) then
		doTween(-5)
	else
		doTween(0)
	end
end)

I implemented the smooth animating and cleaned up the script a bit.

1 Like

If you wanted to check for forward movement you’d need to check if “W” is being pressed.

yea i deleted it cuz it didnt worked lol my mistake sorry

I’ll test out the scripts maybe tmrw, I have exams and I am pretty busy. I’ll tell yall if the scripts work :slight_smile:

Thank you so much! The script works perfectly! Thank you for your help, and thank those that also helpd as well :slight_smile:

Is there an alternative way to achieve this? Using UserInputService to detect PC inputs may not work for people on mobile or xbox.

Problem with this is as you probably have already noticed it does not actually Tween since its just calling for a new Tween every frame.

and another problem is that like harry said it won’t work on other devices,

as of right now I am currently trying to whip up a better version and this is all i have for now.

local tweens = game:GetService("TweenService")
local run = game:GetService("RunService")
local userInput = game:GetService("UserInputService")

local DoingT = false

local camera = workspace.CurrentCamera
local info = TweenInfo.new(0.5, Enum.EasingStyle.Back, Enum.EasingDirection.Out)

local function doTween(degrees)
	if DoingT == false then
		local tween = tweens:Create(camera, info, {CFrame = camera.CFrame * CFrame.Angles(0, 0, math.rad(degrees))})
		tween:Play()
		DoingT = true
		wait(0.5)
		DoingT = false
	end
end

run.RenderStepped:Connect(function()
	if userInput:IsKeyDown(Enum.KeyCode.A) and userInput:IsKeyDown(Enum.KeyCode.D) then
		if DoingT == false then
			doTween(0)			
		end
	elseif userInput:IsKeyDown(Enum.KeyCode.A) then
		if DoingT == false then
			doTween(35)			
		end
	elseif userInput:IsKeyDown(Enum.KeyCode.D) then
		if DoingT == false then
			doTween(-35)			
		end
	end
end)

This actually Tweens but it has many issues, I will keep working on it.

1 Like

How is it coming along so far?

2 Likes

Hello hello! Here’s a MUCH better script for the tilting.

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

local Player = Players.LocalPlayer
local Camera = workspace.CurrentCamera

local rad = math.rad
local Rot = CFrame.new()

local function GetRollAngle()
	local Character = Player.Character

	if not Character then
		return
	end

	local Cf = Camera.CFrame

	return -Cf.RightVector:Dot(Character.Humanoid.MoveDirection)
end

RunService:BindToRenderStep("RotateCameraInDirectionPlayerIsGoing", Enum.RenderPriority.Camera.Value + 1, function()
	local Roll = GetRollAngle() * 2
	Rot = Rot:Lerp(CFrame.Angles(0, 0, rad(Roll)),0.075)

	Camera.CFrame *= Rot
end)
9 Likes

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