Setting up a roblox third person shoulder cam script is one of those things that instantly makes your game feel professionally made. Let's be real, the default Roblox follow camera is fine for basic obbies or chill hangouts, but if you're building a tactical shooter, a survival game, or even a detailed RPG, that centered camera just feels a bit dated. Moving the perspective over the shoulder adds a level of grit and immersion that you just can't get any other way.
The good news is that you don't need to be a math genius or a veteran scripter to get this working. It's mostly about taking control of the CurrentCamera and telling it exactly where to sit relative to the player's head. In this post, I'll walk you through how to put together a solid script, why it works, and how you can tweak it to fit your game's specific vibe.
Why You Need a Custom Camera
Before we jump into the code, it's worth thinking about why we're doing this. A centered camera is great for 360-degree awareness, but an over-the-shoulder view focuses the player's attention. It makes the character feel like part of the world rather than just a sprite in the middle of the screen.
When you use a roblox third person shoulder cam script, you're also making it easier for players to aim or interact with things right in front of them without their own character model getting in the way. It's a design choice that signals to your players that this isn't just another generic experience—it's something you've put actual thought into.
Setting Up Your First Script
To get started, you'll want to create a LocalScript. Since the camera is a client-side thing (you don't want one player's camera moving because another player moved), this has to live in StarterPlayerScripts.
Here's a basic version of what that script should look like. It handles the offset, locks the mouse, and ensures the camera follows the player properly.
The Core Code
```lua local RunService = game:GetService("RunService") local UserInputService = game:GetService("UserInputService") local players = game:GetService("Players")
local player = players.LocalPlayer local camera = workspace.CurrentCamera
-- Configuration local OFFSET = Vector3.new(2.5, 2, 8) -- Right, Up, Back local SENSITIVITY = 0.5
local xAngle = 0 local yAngle = 0
-- Lock the mouse to the center UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
RunService.RenderStepped:Connect(function() local character = player.Character if character and character:FindFirstChild("HumanoidRootPart") then local rootPart = character.HumanoidRootPart
-- Get mouse movement local delta = UserInputService:GetMouseDelta() xAngle = xAngle - (delta.X * SENSITIVITY) yAngle = math.clamp(yAngle - (delta.Y * SENSITIVITY), -80, 80) -- Calculate new camera rotation and position local startCFrame = CFrame.new(rootPart.Position) local rotation = CFrame.Angles(0, math.rad(xAngle), 0) * CFrame.Angles(math.rad(yAngle), 0, 0) local finalCFrame = startCFrame * rotation * CFrame.new(OFFSET) camera.CFrame = finalCFrame -- Optional: Make the character face the same direction as the camera rootPart.CFrame = CFrame.new(rootPart.Position) * CFrame.Angles(0, math.rad(xAngle), 0) end end) ```
Breaking Down How the Script Works
Alright, let's talk about what's actually happening in those lines of code. It looks like a lot, but it's actually pretty straightforward once you break it down into pieces.
First, we're using RunService.RenderStepped. This is crucial. Unlike a regular loop, RenderStepped runs every single time the frame renders. This ensures the camera movement is buttery smooth. If you used a while true do loop with a wait(), the camera would look jittery and probably give your players a headache.
The OFFSET variable is where the magic happens. We're using a Vector3 to tell the camera to stay 2.5 studs to the right, 2 studs up, and 8 studs back from the player. If you want a "lefty" mode, you'd just change that 2.5 to a -2.5.
The UserInputService:GetMouseDelta() part is how we track how much the player is moving their mouse. We take those X and Y movements and turn them into angles. You'll notice the math.clamp function on the yAngle. This is super important because it stops the camera from flipping upside down when the player looks too far up or down. We've set it to 80 degrees, which is usually plenty.
Making It Feel Smooth (Lerping)
The script above is functional, but it's a bit "stiff." In top-tier games, the camera usually has a tiny bit of weight to it. It doesn't just snap instantly; it follows with a very slight delay. This is called Interpolation or "Lerping."
To add this to your roblox third person shoulder cam script, you'd use the :Lerp() function on the CFrame. Instead of setting camera.CFrame directly to the finalCFrame, you'd do something like:
camera.CFrame = camera.CFrame:Lerp(finalCFrame, 0.2)
The 0.2 represents how quickly it catches up. A lower number makes it feel floaty and cinematic, while a higher number makes it feel snappy and responsive. For a fast-paced shooter, you probably want it somewhere around 0.5 or higher. For a horror game, maybe 0.1 to give it that slow, heavy feeling.
Handling the Mouse and Character Rotation
One thing people often forget when making a shoulder cam is how the character behaves. In a standard third-person game, the character usually faces where the camera is looking. In the script I provided, I included a line at the bottom that forces the HumanoidRootPart to match the xAngle of the camera.
This is vital for gameplay feel. If the camera moves but the character stays facing the original direction, it feels disconnected. By locking the character's rotation to the camera's horizontal rotation, you ensure that "forward" is always wherever the player is looking. This makes movement intuitive—press W, and you go where the camera points.
However, you might want to disable this if you're making a game where the player can look around while running in a different direction. In that case, you'd just comment out or delete that last line and handle character rotation separately based on movement input.
Common Pitfalls and How to Fix Them
Even with a solid roblox third person shoulder cam script, you're going to run into some bugs. The most common one is the "wall clip." Since the camera is offset behind the player, it might end up inside a wall if the player backs into a corner.
Roblox's default camera has built-in occlusion (the thing that makes it zoom in when a wall is in the way), but when you write a custom script, you often lose that. To fix this, you'd need to use Raycasting. You fire a ray from the player's head to the intended camera position. If the ray hits a wall, you move the camera to the hit position instead. It's a bit more advanced, but it's what separates an okay game from a great one.
Another issue is the "mouse drift." Sometimes, especially if you have GUIs that require mouse clicks, locking the mouse to the center can get annoying. You'll want to add logic that checks if the player is currently in a menu before locking the mouse. Use UserInputService.MouseBehavior = Enum.MouseBehavior.Default to let them use their cursor again.
Customizing the Experience
Don't feel like you have to stick to the default values I gave you. The beauty of a custom roblox third person shoulder cam script is that it's yours to play with.
Try dynamic offsets! You could make the camera zoom out slightly when the player starts sprinting by changing the Z-value of the offset. Or, if they're aiming a weapon, you could move the camera closer to the shoulder and decrease the Field of View (FOV).
Speaking of FOV, don't be afraid to mess with camera.FieldOfView. A tighter FOV (like 60 or 70) feels more intense and personal, while a wider FOV (90+) feels faster and more chaotic. Most modern games sit around 80-90, but giving the player a slider in the settings to change this is always a pro move.
Final Thoughts on Camera Design
At the end of the day, the camera is the player's eyes into your world. If the eyes feel weird, the whole game feels weird. Using a roblox third person shoulder cam script gives you the control you need to set the right tone.
Take your time to test it. Walk around your maps, jump over obstacles, and try to break it. If it feels smooth and you forget you're even "using" a camera script, then you've done it right. It should feel like a natural extension of the player's movement.
So, grab that code, throw it into a LocalScript, and start tweaking those numbers. You'll be surprised at how much of a difference a simple offset and a bit of rotation logic can make. Happy developing!