View bobbing issues using LERP and TWEENSERVICE

EDIT: SMOOTH ISSUES ARE THOSE STUTTERING THING TRANSITIONING BETWEEN REGULAR AND OFFSET CFRAME

For a little bit of context, ive never made bobbing before beceause i thought it was too difficult and boring math ive never used. But yesterday i realized it is actually quite easy to make, following a guide post on this forum explaining everything i needed to know.

Ive been trying to make this TPS bobbing view messing with the camera Z orientation and the results are here tho, i am now stuck with a “smooth” issue.
At first i was using the :Lerp() function as you can see here:

local rs = game:GetService("RunService")

local plr = game.Players.LocalPlayer
local hum = plr.Character:FindFirstChild("Humanoid")
local cam = workspace.CurrentCamera
local sprint = script.Parent.Parent.sprint.sprint


rs.RenderStepped:Connect(function()
	local currentTime = tick()

	if hum.MoveDirection.Magnitude > 0 then
		local walkBobble = cam.CFrame * CFrame.Angles(0, 0, math.cos(currentTime * 12 / 2.5) * 0.1)
		local sprintBobble = cam.CFrame * CFrame.Angles(0, 0, math.cos(currentTime * 24 / 2.3) * 0.1)
		
		if sprint.Value == false then
			cam.CFrame = cam.CFrame:Lerp(walkBobble, .2)
		else
			cam.CFrame = cam.CFrame:Lerp(sprintBobble, .5)
		end
	end
end)

The first issue i had using :Lerp() is that when walking/running, there was no in between when transitioning from camera CFrame to the offset CFrame as we can see in this clip:

After that, i thought about using TweenService and did so but i doesnt fix my issue and actually make it more buggy:

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

local plr = game.Players.LocalPlayer
local hum = plr.Character:FindFirstChild("Humanoid")
local cam = workspace.CurrentCamera
local sprint = script.Parent.Parent.sprint.sprint
local normalCFrame = cam.CFrame

local ti = TweenInfo.new(.35, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)

rs.RenderStepped:Connect(function()
	
	local currentTime = tick()

	local walkTween = ts:Create(cam, ti, {CFrame = cam.CFrame * CFrame.Angles(0, 0, math.cos(currentTime * 12 / 2.5) * 0.1)})
	local sprintTween = ts:Create(cam, ti, {CFrame = cam.CFrame * CFrame.Angles(0, 0, math.cos(currentTime * 24 / 2.3) * 0.5)})
	local baseTween = ts:Create(cam, ti, {CFrame = normalCFrame})

	if hum.MoveDirection.Magnitude > 0 then
		if sprint.Value == false then
			sprintTween:Cancel()
			walkTween:Play()
		end
		
		if sprint.Value == true then
			walkTween:Cancel()
			sprintTween:Play()
		end
	end
end)

local sprint = script.Parent.Parent.sprint.sprint

Is the sprint value a nested value? why are you looking for a second sprint?

that variable is a bool value letting the bobbing script know when the player is sprinting or not. It is controlled by another script.

the sprint value isnt really important i could do this instead:

local rs = game:GetService("RunService")

local plr = game.Players.LocalPlayer
local hum = plr.Character:FindFirstChild("Humanoid")
local cam = workspace.CurrentCamera


rs.RenderStepped:Connect(function()
	local currentTime = tick()

	if hum.MoveDirection.Magnitude > 0 then
		local walkBobble = cam.CFrame * CFrame.Angles(0, 0, math.cos(currentTime * hum.WalkSpeed / 2.5) * 0.1)

		cam.CFrame = cam.CFrame:Lerp(walkBobble, .2)
	end
end)

but i dont beceause when the player sprint, he’s velocity increase slowly and doesnt do +15 instantly so if i code it this way, it is buggy.

Here is a few I modified/rewrote a few years ago not using LERP and TWEENSERVICE…

A bit over complicated but works well.

CBob
--CBob script with a tilt. org.by:IAmDevForumMember
task.wait(3) --StarterCharacterScripts.LocalScript
local Workspace = game:GetService("Workspace")
local RunService = game:GetService("RunService")
local LocalPlayer = game:GetService("Players").LocalPlayer
local UserInputService = game:GetService("UserInputService")
local Root = LocalPlayer.Character:WaitForChild("HumanoidRootPart")
local Mouse, Camera  = LocalPlayer:GetMouse(), Workspace.CurrentCamera
local angleX, x, y, tilt, vX, vY, sX, sY = 0, 0, 0, 0, 0, 0, 10, 10
local TouchEnabled = UserInputService.TouchEnabled
--* UserInputService.MouseIconEnabled = flase
local randomX, randomY = nil, nil
local function lerp(v1, v2, t)
	return v1 + (v2 - v1) * t
end --rewrite.by:2112Jay

RunService.RenderStepped:Connect(function(dt)
	local velocity = Root.Velocity.magnitude
	if velocity > 0.02 then dt *= 60 randomX = math.random(1, 2) randomY = math.random(1, 2) --x(10,15), y(5,10)
		vX = dt <= 2 and lerp(vX, math.cos(tick() * 0.5 * randomX) * (math.random(5, 20) / 200) * dt, 0.05 * dt) or 0
		vY = dt <= 2 and lerp(vY, math.cos(tick() * 0.5 * randomY) * (math.random(2, 10) / 200) * dt, 0.05 * dt) or 0
		
		Camera.CFrame *= CFrame.Angles(0, 0, math.rad(angleX))
			* CFrame.Angles(math.rad(math.clamp(x * dt, -0.15, 0.15)), math.rad(math.clamp(y * dt, -0.5, 0.5)), tilt)
			* CFrame.Angles(math.rad(vX), math.rad(vY), math.rad(vY * 10))
		
		tilt = math.clamp(lerp(tilt, -Camera.CFrame:VectorToObjectSpace((Root and Root.Velocity or 
			Vector3.new()) / math.max(LocalPlayer.Character.Humanoid.WalkSpeed, 0.01)).X * 0.05, 0.1 * dt), -0.05, 0.05)
		
		if not TouchEnabled and dt < 2 then
			angleX = lerp(angleX, math.clamp(UserInputService:GetMouseDelta().X / dt * 0.15, -2.5, 2.5), 0.25 * dt)
		end x = lerp(x, math.sin(tick() * sX) / 5 * math.min(1, sY / 10), 0.25 * dt)
		y = velocity > 1 and lerp(y, math.cos(tick() * 0.5 * math.floor(sX)) * (sX / 200), 0.25 * dt) or lerp(y, 0, 0.05 * dt)
		sX, sY = velocity > 12 and 20 or (velocity > 0.1 and 12 or 0), velocity > 0.1 and 18 or (velocity > 0.1 and 14 or 0)
	end
end)

I like this version also …
A little more simplistic.

Wavy
local frequency = 5
local horizantalAmplitude = .2
local verticalAmplitude = .2
local deltaTime = 0

local rns = game:GetService("RunService")
local player = game:GetService('Players').LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild('Humanoid')

function Update(delta) deltaTime += delta
	if humanoid.MoveDirection.Magnitude > 0  then
		local horizontalOffset = math.sin(deltaTime * frequency * 2) * horizantalAmplitude
		local verticalOffset = math.sin(deltaTime * frequency) * verticalAmplitude
		-- Create an offset on both x and y axis using a proportional ratio
		local bobble = Vector3.new(horizontalOffset, verticalOffset, 0)
		humanoid.CameraOffset = humanoid.CameraOffset:Lerp(bobble, .5)
	end
end

rns.RenderStepped:Connect(Update)

Uhhhh so the solution would be rewrite the :Lerp() function ? Thats just over complicating things they’re must be another way to fix that issue i dont get it

Up to you… They are stand alone ClientScrips in StarterCharacterScripts.
When I see this: local sprint = script.Parent.Parent.sprint.sprint
I think un-testable for me. Also; Cbob uses lerp.

Sometimes your answer is within other answers…

This looks good, just need a few changes

local walkSpeed = 0.2
local sprintSpeed = 0.5

rs.RenderStepped:Connect(function(delta)
	local currentTime = tick()

	if hum.MoveDirection.Magnitude > 0 then
		if sprint.Value == false then
			local walkBobble = cam.CFrame * CFrame.Angles(0, 0, math.cos(currentTime * 12 / 2.5) * 0.1)
			cam.CFrame = cam.CFrame:Lerp(walkBobble, delta * walkSpeed)
		else
			local sprintBobble = cam.CFrame * CFrame.Angles(0, 0, math.cos(currentTime * 24 / 2.3) * 0.1)
			cam.CFrame = cam.CFrame:Lerp(sprintBobble, delta * sprintSpeed)
		end
	end
end)
  1. Multiply the alpha (second param of :Lerp() ) by deltaTime
  2. Dont compute the bobble for both cases all the time

You will have to play around with the Speed variables

EDIT: could also store values such as 24/2.3 and 15/2.5 into a variable for that micro optimization

1 Like

TYSM, i wouldve never find out multiplying alpha by delta time would be the trick, seems to be the type of solution you cant figure out by yourself

1 Like

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