Is there a good way of doing over the shoulder style third person cameras yet?

Is there any way for a developer to implement a third person style camera similar to shift lock mode?

The camera bit isn’t so hard but as soon as we cut to CameraType = Scriptable the player will no longer face the camera, so it’s back to the drawing board there.

Know this is something people have been trying to do for a long time, has anyone figured out a neat way yet?

6 Likes

If I’m thinking what you’re thinking, doesn’t Polyguns do this camera well?

4 Likes

Yeah they did a good job.

Just force the player to look in the same direction as the camera and use Humanoid.CameraOffset

2 Likes

I believe the player’s humanoid faces the camera’s direction if AutoRotate is enabled. If this doesn’t happen during the scriptable type, then I’d disable it and use rotvelocity to adjust the player to face the camera.

Not sure which is best:

  • CFraming the rootPart on .Stepped
  • Using BodyGyro to face on .Stepped

I don’t know which is more expensive.

1 Like

Been perfecting this for over a year and this week added support for mobile and controller controls.

6 Likes

This is what I did for polyguns, it’s a lot more stable than bodygyro

6 Likes

Did you encounter any performance issues - did this behave weirdly on account of the root part not being anchored?

No performance issues

make sure to disable humanoid.AutoRotate, otherwise the replication will be a bit weird.

1 Like

Don’t really want to give away the whole secret because it’s one of my best creations so far :smiley: all I can say that the camera is a physical part.

3 Likes

Cheers mate!

2 Likes
UserInputService.InputChanged:connect(function(IO)
	if IO.UserInputType == Enum.UserInputType.MouseMovement then
		X = IO.Delta.X/150
		Y = math.clamp(Y-IO.Delta.Y/150,-1,1)
	
		
	end
end)
game["Run Service"].RenderStepped:connect(function()
	Camera.CFrame = Char.HumanoidRootPart.CFrame * CFrame.new(Vector3.new( 0,2.5,.25)+Char.Humanoid.CameraOffset)*CFrame.fromEulerAnglesXYZ(Y,0,0)*CFrame.new(XOffest,0,6)
	Char.HumanoidRootPart.CFrame = Char.HumanoidRootPart.CFrame * CFrame.fromEulerAnglesXYZ(0,-X,0)
	X = 0
end)

Char being Character, and Camera being the camera…

play around with the variables, see what happens.
EDIT: I’ve learned a bit in the 2 years since i posted this, so I’ve made a few minor changes
you can skip all that logic stuff by using math.clamp (which i didn’t know was a thing)

8 Likes

Sorry to bump this, but this doesn’t work :grimacing:

You could at least explain how it doesn’t work, such as what problems you’re running into. This thread is two years old so the methods since then would have very obviously changed. It’d be better to create a new thread than to bump something very old.

Roblox is natively capable of a very rudimentary third person camera through the manipulation of Humanoid.CameraOffset a certain amount of studs on the X coordinate.

3 Likes

I’ve been told off before for creating a new thread when an older thread already existed

As for the problems, 1. ‘XOffset’ is never defined and 2. If I replace XOffset with a 0 I get this error

UserInputService.InputChanged:connect(function(IO)
	if IO.UserInputType == Enum.UserInputType.MouseMovement then
		X = IO.Delta.X/150
		Y = math.clamp(Y-IO.Delta.Y/150,-1,1)
	
		
	end
end)
game["Run Service"].RenderStepped:connect(function()
	Camera.CFrame = Character.HumanoidRootPart.CFrame * CFrame.new(Vector3.new( 0,2.5,.25)+Character.Humanoid.CameraOffset)*CFrame.fromEulerAnglesXYZ(Y,0,0)*CFrame.new(0,0,6) -- ERROR HERE
	Character.HumanoidRootPart.CFrame = Character.HumanoidRootPart.CFrame * CFrame.fromEulerAnglesXYZ(0,-X,0)
	X = 0
end)

Argument 1 missing or nil

from this section CFrame.fromEulerAnglesXYZ(Y,0,0) on the line after the RunService

Hi, this is my bad,
you’re gonna want to define X and Y as both equal to 0 when you start the script.

basically, Y is nil until you move the mouse. i should have written this better.

Y,X,XOffest= 0,0,0
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
UserInputService.InputChanged:connect(function(IO)
	if IO.UserInputType == Enum.UserInputType.MouseMovement then
		X = IO.Delta.X/150
		Y = math.clamp(Y-IO.Delta.Y/150,-1,1)
	
		
	end
end)
game["Run Service"].RenderStepped:connect(function()
	Camera.CFrame = Char.HumanoidRootPart.CFrame * CFrame.new(Vector3.new( 0,2.5,.25)+Char.Humanoid.CameraOffset)*CFrame.fromEulerAnglesXYZ(Y,0,0)*CFrame.new(XOffest,0,6)
	Char.HumanoidRootPart.CFrame = Char.HumanoidRootPart.CFrame * CFrame.fromEulerAnglesXYZ(0,-X,0)
	X = 0
end)
4 Likes

Final Edit: I’ve had many people trying to use this code, but I have since found an infinitely better way to do it. This post should explain it all: Camera lock without first person - #2 by Hello42bacon

I wanted to do this exact thing a couple months ago. I’ll save you the time it took me to figure it out haha

Edit: Sorry, originally gave un-tested code. This code should work as I just tested it:

local uis = game:GetService(“UserInputService”)
local ts = game:GetService(“TweenService”)
local cam = workspace.CurrentCamera
local plr = game.Players.LocalPlayer
local mouse = plr:GetMouse()
repeat wait() until plr.Character and plr.Character.Parent == workspace

local char = plr.Character
local hum = char:WaitForChild(“Humanoid”)
local waist = char.UpperTorso:WaitForChild(“Waist”)
local root = char:WaitForChild(“HumanoidRootPart”)

hum.CameraOffset = Vector3.new(4, 0, 0)
hum.AutoRotate = false
cam.CameraSubject = hum

game:GetService(“RunService”).RenderStepped:Connect(function()
uis.MouseBehavior = Enum.MouseBehavior.LockCenter
local delta = uis:GetMouseDelta()
local deltaX, deltaY = delta.X, -delta.Y
cam.CFrame = CFrame.new(cam.CFrame.p, cam.CFrame* CFrame.new(deltaX, deltaY, -2000).p)

local offset = char.LowerTorso.CFrame:ToWorldSpace(CFrame.new(0, char.UpperTorso.Size.Y/2, 0)) * CFrame.fromEulerAnglesXYZ(math.max(math.min(math.asin((mouse.Hit.p - mouse.Origin.p).unit.y), .6), -.75), 0, 0) * CFrame.new(0, char.UpperTorso.Size.Y/2, 0)

waist.C1 = offset:inverse() * char.LowerTorso.CFrame * CFrame.new(0, .8, 0)

local tweenInfo = TweenInfo.new(.15, Enum.EasingStyle.Sine, Enum.EasingDirection.Out, 0, false, 0)
local newRootCF = CFrame.new(root.CFrame.p,root.CFrame.p+Vector3.new(cam.CFrame.lookVector.X,0,cam.CFrame.lookVector.Z))
local tween = ts:Create(root, tweenInfo, {[“CFrame”] = newRootCF}, true)
tween:Play()
end)

5 Likes