Configurable Head Bobbing Script
I have open-sourced my first person camera and head bobbing script for anyone to use. The bobbing is based on the clamped magnitude of your X and Z velocity and uses the sine function. Here is a video preview:To use, place it as a LocalScript under StarterCharacterScripts. I have explained the constants with comments for easy adjustment.
You may wish to modify this script and turn it into a module to use with your current game’s code and may do so freely.
This script is free to use, redistribute and adapt under CC BY-SA 4.0
--[[
Created by @BuiltToWreck
Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
https://creativecommons.org/licenses/by-sa/4.0/
Place in StarterCharacterScripts
Adjust constants to your liking.
]]
-- CONSTANTS --
-- Camera position offset from HumanoidRootPart:
local OFFSET = Vector3.new(0, 3, 0)
-- Mouse sensitivity multiplier
local SENSITIVITY = 0.006
-- Verticle angle limits (how far you can look up and down):
local UPPER_ANGLE_LIMIT = math.rad(85)
local LOWER_ANGLE_LIMIT = math.rad(-85)
-- How many bobs per second horizontally and vertically:
local BOB_FREQUENCY_Y = 4
local BOB_FREQUENCY_X = 2
-- How intense the horizontal and vertical bobs are:
local BOB_AMPLITUDE_X = 0.6
local BOB_AMPLITUDE_Y = 0.4
-- The maximum velocity that affects the intensity of the bob:
local MAX_VELOCITY = 16
-- How fast it takes for bobbing to start/stop when you start/stop moving:
local RECENTER_SPEED = 4
-- SERVICES --
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
-- REFERENCES --
local localPlayer = Players.LocalPlayer
local character = localPlayer.Character or localPlayer.CharacterAdded:Wait()
local currentCamera = workspace.CurrentCamera
-- VARIABLES --
local cameraAngle = Vector2.new(0, 0)
local currentTime = 0
local velocityMultiplier = 0
-- FUNCTIONS --
local function scalarLerp(a, b, c)
c = math.clamp(c, 0, 1)
return a + c * (b - a)
end
local function renderStepped(deltaTime)
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
local mouseDelta = UserInputService:GetMouseDelta() * SENSITIVITY
-- Calculate bob offsets
currentTime += deltaTime
local bobOffsetY = (math.sin(currentTime * math.pi * BOB_FREQUENCY_Y) - 0.5) * BOB_AMPLITUDE_Y
local bobOffsetX = (math.sin(currentTime * math.pi * BOB_FREQUENCY_X) - 0.5) * BOB_AMPLITUDE_X
-- Smooth between bobbing and neutral based on horizontal velocity
local velocityMagnitude = (humanoidRootPart.Velocity * Vector3.new(1,0,1)).Magnitude
local targetVelocityMultiplier = math.clamp(velocityMagnitude, 0, MAX_VELOCITY) / MAX_VELOCITY
velocityMultiplier = scalarLerp(velocityMultiplier, targetVelocityMultiplier, RECENTER_SPEED * deltaTime)
bobOffsetX *= velocityMultiplier
bobOffsetY *= velocityMultiplier
-- Update the camera angle, clamp it to angle limits
cameraAngle -= mouseDelta
cameraAngle = Vector2.new(
cameraAngle.X,
math.clamp(cameraAngle.Y, LOWER_ANGLE_LIMIT, UPPER_ANGLE_LIMIT)
)
-- Set the cframe of the camera
currentCamera.CFrame =
CFrame.new(humanoidRootPart.Position + OFFSET + Vector3.new(0, bobOffsetY, 0)) *
CFrame.Angles(0, cameraAngle.X, 0) *
CFrame.Angles(cameraAngle.Y, 0, 0) *
CFrame.new(bobOffsetX, 0, 0)
end
-- LOGIC/SETUP/CONNECTIONS --
localPlayer.CharacterAppearanceLoaded:Wait()
currentCamera.CameraType = Enum.CameraType.Scriptable
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
-- Make character invisible
for _, desc in ipairs(character:GetDescendants()) do
if desc:IsA("Part") then
desc.Transparency = 1
end
end
RunService.RenderStepped:Connect(renderStepped)
I hope this is of use to you