How can I make an Over-The-Shoulder System?

Hello! I am trying to make my fourth Gunkit. This one will feature an Over-The-Shoulder (OTS) camera system. I got the whole OTS part down, but I want the humanoid to rotate with the camera movement. So far, I have found a partial solution, however, it also rotates upwards and downwards, not only horizontally. I was wondering if there was a way to ignore the axis that is causing it to move up and down?

Here is my code:

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

local Camera = workspace.CurrentCamera
local LocalPlayer = Players.LocalPlayer

local Conn = RunService.RenderStepped:Connect(function()	
	LocalPlayer.Character.HumanoidRootPart.CFrame = ((Camera.CFrame - Camera.CFrame.p) + LocalPlayer.Character.HumanoidRootPart.CFrame.p)
end)

You can see that it works so that the HumanoidRootPart’s CFrame will rotate on all axises, but I just can’t figure out how to ignore a single axis. Any help would be appreciated!

Some weeks ago I made this same script open-sourced with Orientation and EulerAngles, I’ll leave it here in case it can work with your system (The script is located in StarterCharacterScripts):

local Services = {
	["RunService"] = game:GetService("RunService");
    ["UserInputService"] = game:GetService("UserInputService")
}

local Cam = workspace.CurrentCamera
local Humanoid = script.Parent:WaitForChild("Humanoid")
local HRT = script.Parent:WaitForChild("HumanoidRootPart")

local Main = function()
	local X, Y, Z = Cam.CFrame:ToEulerAnglesXYZ()
	
	HRT.Orientation = Vector3.new(HRT.Orientation.X, Y, HRT.Orientation.Z)
	Services.UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
end

Services.RunService:BindToRenderStep("Main", Enum.RenderPriority.Camera.Value, Main)

Hmm. It seems that when I move my cursor, I fly into the air.

This shouldn’t happen if you modify the Orientation of the HumanoidRootPart, not the CFrame

If you edit the CFrame it will also take the CFrame’s position of the camera.

I was not using CFrame, I made a new LocalScript and put it into StarterCharacterScripts with the exact code. I have a video if you want.

You just want the Y axis to rotate.

Imagine a Y axis pole coming down from the sky through the entire world. You want your part to rotate about that axis.

local Conn = RunService.RenderStepped:Connect(function()
	local targetPoint = Camera.CFrame * CFrame.new(0,0,-1000)
	local rootPart = LocalPlayer.Character.HumanoidRootPart
	rootPart.CFrame = CFrame.lookAt(
		rootPart.Position,
		Vector3.new(targetPoint.Position.X, rootPart.Position.Y, targetPoint.Position.Z)
	)
end)

The rotation aspect of it does seem to be working, but, when I try to walk, it seems very bumpy and has weird rigid movements.

Most probably because you’re recomposing the CFrame each render frame. A common work-around is a non-replicating BodyGyro.

local rootPart = LocalPlayer.Character.HumanoidRootPart
local gyro = Instance.new("BodyGyro")
gyro.MaxTorque = Vector3.new(0,4e5,0)
gyro.Parent = rootPart

local Conn = RunService.RenderStepped:Connect(function()
	local targetPoint = Camera.CFrame * CFrame.new(0,0,-1000)
	gyro.CFrame = CFrame.lookAt(
		rootPart.Position,
		Vector3.new(targetPoint.Position.X, rootPart.Position.Y, targetPoint.Position.Z)
	)
end)
2 Likes

Thank you! That was very helpful. All I needed to do was decrease D, increase P, and increase the MaxTorque so that it would move instantly.

local Gyro = Instance.new("BodyGyro")
Gyro.MaxTorque = Vector3.new(0,4e8,0)
Gyro.P = 1000000
Gyro.D = 0
Gyro.Parent = HumanoidRootPart

RunService.RenderStepped:Connect(function()
local targetPoint = Camera.CFrame * CFrame.new(0,0,-1000)
Gyro.CFrame = CFrame.lookAt(
	HumanoidRootPart.Position,
	Vector3.new(targetPoint.Position.X, HumanoidRootPart.Position.Y, targetPoint.Position.Z)
)
end)

Thank you all for your help. I have been updating my game and created a new gunkit with a different camera technology. Even though the previous BodyGyro system worked fine, it is a bit better for me to not use legacy movers.

Leaving this here in hopes that it helps anyone else trying to make an Over-The-Shoulder system.

local GameSettings = UserSettings():GetService("UserGameSettings")

local WasInFirst = false
	while Aiming and HasGun == lastGun do -- make sure person is still aiming (rmb down) and they didnt switch weapons
		RunService.RenderStepped:Wait() -- avoid script execution timeout
		
		UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter -- makes the mouse stay in center while aiming
		GameSettings.RotationType = Enum.RotationType.CameraRelative -- moves character with camera
		
		if Character.Head.LocalTransparencyModifier >= 1 then -- check if in first person
			if not WasInFirst then -- debounce
				WasInFirst = true
				TweenService:Create(Humanoid, TweenInfo.new(0.1, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut, 0, false), {CameraOffset = Vector3.new(0, 0.5)}):Play()
			end
		else
			if WasInFirst then
				WasInFirst = false
				TweenService:Create(Humanoid, TweenInfo.new(0.1, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut, 0, false), {CameraOffset = Vector3.new(CameraSide, 0.5)}):Play()
			end
		end
	end

-- anything after this will happen when no longer aiming