How to recreate a camera tilt

As mentioned in the title, I’m trying to recreate a camera tilt. This, however, has proven to be a tad bit too difficult to recreate. To figure out what I’m talking about, watch this video’s first 10 seconds or so: ACSM Third Person, Momentum, Movement Rework & Locker System - YouTube
As seen in the video, change in the camera’s Y orientation creates a tilt in the Z axis that returns to it’s original state. I’ve used public modules, but none quite do what I want them to. Could you please help?

7 Likes

To create roll or tilt you can alter the cameras Z orientation.

local Camera = workspace.CurrentCamera
while true do
	Camera.CFrame *= CFrame.Angles(0,0,0.017)
end

The code rotates the camera forever.

3 Likes

I’m aware of how to shift the camera’s Z orientation. I was asking how to specifically do so using change in the Y axis. This is some code I had previously:

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

local Camera = workspace.CurrentCamera
local LastCameraCFrame = nil
local TweenPlaying = false
local AllowedAngle = 5
local Circle = (2 * math.pi)

RunService.RenderStepped:Connect(function(DeltaTime)
	if LastCameraCFrame then
		local CurrentCFrameAngles = {Camera.CFrame:ToEulerAnglesYXZ()}
		local LastCFrameAngles = {LastCameraCFrame:ToEulerAnglesYXZ()}
		--print(math.deg(CurrentCFrameAngles[2] % (2 * math.pi)))
		local OriY = math.deg(CurrentCFrameAngles[2] % Circle)
		local OldOriY = math.deg(LastCFrameAngles[2] % Circle)
		local Calculations = OriY - OldOriY
		if OriY > OldOriY then
		else
			print("Not Here")
		end
		local CFang = {(Camera.CFrame * CFrame.Angles(0, 0, Calculations / 1.5)):ToEulerAnglesYXZ()}
		local CFangY = math.deg(CFang[3])
		if CFangY >= -AllowedAngle and CFangY <= AllowedAngle then
			TweenPlaying = true

			local Tween = TweenService:Create(
				Camera,
				TweenInfo.new(DeltaTime, Enum.EasingStyle.Linear),
				{["CFrame"] = Camera.CFrame * CFrame.Angles(0, 0, Calculations / 1.5)}
			)
			Tween.Completed:Once(function()
				TweenPlaying = false
			end)

			Tween:Play()
		end
	end

	LastCameraCFrame = Camera.CFrame
end)```
2 Likes

Oh sorry I didn’t understood your topic. For that you can see the change in the Y orientation every renderstep and from that rotate the camera.

2 Likes

Easier said than done. Here is a video of the code I made using that exact method.


It was working somewhat better before I made changes, but as seen, it doesn’t quite copy that effect.
If you could, are there any changes to my code you could make?

1 Like

You can use the :lerp function to tween it smoothly and setting its alpha value to deltatime*5. 5 being responsiveness.

local _,_,roll = camera.CFrame:Lerp(newCFrame,deltatime*5):ToOrientation()
camera.CFrame = CFrame.Angles(0,0,roll)

Tweening inside RenderStepped is not the best idea, try lerping.

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

local Camera = workspace.CurrentCamera
local LastCameraCFrame = nil
local TweenPlaying = false
local AllowedAngle = 5
local Circle = (2 * math.pi)

local lerp = function(a, b, t)
    return a + (b - a) * t
end

RunService.RenderStepped:Connect(function(DeltaTime)
	if LastCameraCFrame then
		local CurrentCFrameAngles = {Camera.CFrame:ToEulerAnglesYXZ()}
		local LastCFrameAngles = {LastCameraCFrame:ToEulerAnglesYXZ()}
		--print(math.deg(CurrentCFrameAngles[2] % (2 * math.pi)))
		local OriY = math.deg(CurrentCFrameAngles[2] % Circle)
		local OldOriY = math.deg(LastCFrameAngles[2] % Circle)
		local Calculations = OriY - OldOriY
		if OriY > OldOriY then
		else
			print("Not Here")
		end
		local CFang = {(Camera.CFrame * CFrame.Angles(0, 0, Calculations / 1.5)):ToEulerAnglesYXZ()}
		local CFangY = math.deg(CFang[3])
		if CFangY >= -AllowedAngle and CFangY <= AllowedAngle then
			Camera.CFrame = Camera.CFrame:Lerp(Camera.CFrame * CFrame.Angles(0, 0, Calculations / 1.5), 1.5 * DeltaTime)
		end
	end

	LastCameraCFrame = Camera.CFrame
end)

It works much better now that it lerps instead of tweens, but now this happens:

You can add a debounce before the camera reverts back to its original. Or you can view this topic.

So try this script instead (not tested)

local RunService = game:GetService("RunService")

local Camera = workspace.CurrentCamera
local LastYRotation = 0
local AllowedAngle = 5

local lerp = function(a, b, t)
    return a + (b - a) * t
end

RunService.RenderStepped:Connect(function(DeltaTime)
	local _, currentY, currentZ = Camera.CFrame:ToEulerAnglesXYZ()
	
    local z = lerp(currentZ, (y - lastY) / 15, DeltaTime * 0.15)
    Camera.CFrame *= CFrame.Angles(0, 0, math.clamp(z, -AllowedAngle, AllowedAngle)

	LastYRotation = currentY
end)

Oops I forgot your allowed angles in degrees mb.

local RunService = game:GetService("RunService")

local Camera = workspace.CurrentCamera
local LastYRotation = 0
local AllowedAngle = 5

local lerp = function(a, b, t)
    return a + (b - a) * t
end

RunService.RenderStepped:Connect(function(DeltaTime)
	local _, currentY, currentZ = Camera.CFrame:ToEulerAnglesXYZ()
	
    local z = math.deg(lerp(currentZ, (y - lastY) / 15, DeltaTime * 0.15))
    Camera.CFrame *= CFrame.Angles(0, 0, math.clamp(z, -AllowedAngle, AllowedAngle)

	LastYRotation = currentY
end)

Problems with that script is that it doesn’t account for fact that orientation in roblox isn’t (0, 360), and instead is (-180, 180). Also, for reasons I don’t feel like finding out, the math isn’t working as intended, and (as far as I observed) is based off of 0 as the Y-axis.

The former won’t provide you with negative tilt so that’s why I chose the latter.

Huh? oh ryt i c

local RunService = game:GetService("RunService")

local Camera = workspace.CurrentCamera
local LastYRotation = nil
local AllowedAngle = 5

local lerp = function(a, b, t)
    return a + (b - a) * t
end

RunService.RenderStepped:Connect(function(DeltaTime)
	local _, currentY, currentZ = Camera.CFrame:ToEulerAnglesXYZ()
	
    if not LastYRotation then LastYRotation = currentY; return end

    local z = math.deg(lerp(currentZ, (y - lastY) / 15, DeltaTime * 0.15))
    Camera.CFrame *= CFrame.Angles(0, 0, math.clamp(z, -AllowedAngle, AllowedAngle)

	LastYRotation = currentY
end)

Not how that works. When using the (-180, 180) range, when you reach a certain global orientation, the script flips, so if you’re looking left, it’s going to tilt to the right. That’s why you use modular arithmetic.

Well I don’t really seem get ur point tbh, your approach seems to make it more complex than it should be (atleast from my limited mathematical viewpoint lol). But ye I dunno why its being jittery, maybe cuz the tilt is being added every frame?. Also there’s a typo on my part, should be LastYRotation instead of lastY and currentY instead of just y.

local RunService = game:GetService("RunService")

local Camera = workspace.CurrentCamera
local LastYRotation = 0
local AllowedAngle = 5

local lerp = function(a, b, t)
    return a + (b - a) * t
end

RunService.RenderStepped:Connect(function(DeltaTime)
	local _, currentY, currentZ = Camera.CFrame:ToEulerAnglesXYZ()
	
    local z = math.deg(lerp(currentZ, (currentY - LastYRotation) / 15, DeltaTime * 0.15))
    Camera.CFrame *= CFrame.Angles(0, 0, math.clamp(z, -AllowedAngle, AllowedAngle)

	LastYRotation = currentY
end)

Previous explanation was rather rough, but yes, you require Modulos to do what I’m trying to do. Another script module that’s rather known that does an altered version of what I want also uses Modular arithmetic.

Boosting. I really need help on this one.

I would recommend you making a camera system entirely (Its not hard at all!). Or using pre-made camera system.

Is the camera just circling a point that is offset from the character?