I’ve been trying to create an over the shoulder camera for a weapon system, and have managed to get it mostly working. I’ve been taking inspiration from a lot of different pieces of code found on the DevHub and other threads here on the forums. I have one issue though that I can’t seem to figure out.
When I change directions with my mouse the rotation of my player as well as the joint angles gets messed up as if it’s using the previous camera cframe.
Example Of Issue
Another Example
If I release the camera and use it again it usually fixes itself. I’m unsure where this is going wrong in my code and was wondering if anyone else could see my problem, thanks!
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")
local LocalPlayer = Players.LocalPlayer
local TweenService = require(script.Parent.TweenService)
local Camera = workspace.CurrentCamera
local Mouse = LocalPlayer:GetMouse()
local function setCharacterJoints(Character)
local Joints =
{
Neck = Character.Head:WaitForChild("Neck"),
Waist = Character.UpperTorso:WaitForChild("Waist")
}
local CameraDirection = Character.PrimaryPart.CFrame:ToObjectSpace(Camera.CFrame).LookVector
local CamDirectionY = math.clamp(CameraDirection.Y, -0.8, 0.8)
local CamDirectionX = math.clamp(CameraDirection.X, -0.8, 0.8)
Joints.Neck.C0 = CFrame.new(Joints.Neck.C0.Position) * CFrame.Angles(math.asin(CamDirectionY), -math.asin(CamDirectionX), 0)
Joints.Waist.C0 = CFrame.new(Joints.Waist.C0.Position) * CFrame.Angles(math.asin(CamDirectionY), -math.asin(CamDirectionX), 0)
end
local function resetCharacterJoints(Character)
local Joints =
{
Neck = Character.Head:WaitForChild("Neck"),
Waist = Character.UpperTorso:WaitForChild("Waist")
}
Joints.Neck.C0 = CFrame.new(Joints.Neck.C0.Position)
Joints.Waist.C0 = CFrame.new(Joints.Waist.C0.Position)
end
local function updateCharacterRotation(Character, deltaX)
local goal = {CFrame = Character.PrimaryPart.CFrame * CFrame.Angles(0, math.rad(-deltaX), 0)}
local properties = {Time = 0.01}
TweenService.tween(Character.PrimaryPart, goal, properties)
end
local ShoulderCamera = {}
ShoulderCamera.Character = LocalPlayer.Character
function ShoulderCamera:UpdateCamera()
local Character = ShoulderCamera.Character
if Character and Character:FindFirstChild("HumanoidRootPart") then
local startCFrame = CFrame.new(Character.PrimaryPart.CFrame.Position) * CFrame.Angles(0, math.rad(ShoulderCamera.RotationX), 0) * CFrame.Angles(math.rad(ShoulderCamera.RotationY), 0, 0)
local cameraOffset = Vector3.new(2,4,ShoulderCamera.Zoom)
if ShoulderCamera.Aiming then
end
local cameraCFrame = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, cameraOffset.Z))
local cameraFocus = startCFrame:ToWorldSpace(CFrame.new(cameraOffset.X, cameraOffset.Y, -10000))
local goal = {FieldOfView = ShoulderCamera.FOV}
local properties = {Time = 0.1, Style = "Sine", Dir = "Out"}
TweenService.tween(Camera, goal, properties)
local goal = {CFrame = CFrame.new(cameraCFrame.Position, cameraFocus.Position)}
local properties = {Time = 0.01}
TweenService.tween(Camera, goal, properties)
end
end
function ShoulderCamera:Start()
Camera.CameraType = "Scriptable"
ShoulderCamera.Zoom = 10
ShoulderCamera.FOV = 70
ShoulderCamera.Connections = {}
ShoulderCamera.RotationX = 0
ShoulderCamera.RotationY = 0
ShoulderCamera.CameraSpeed = 0.75
ShoulderCamera.Aiming = false
ShoulderCamera.Character.PrimaryPart.CFrame = CFrame.new(ShoulderCamera.Character.PrimaryPart.Position, Mouse.Hit.Position * Vector3.new(1,0,1) + Vector3.new(0,ShoulderCamera.Character.PrimaryPart.Position.Y,0))
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
ShoulderCamera.runConnection = RunService.RenderStepped:Connect(function()
local Character = ShoulderCamera.Character
if Character and Character:FindFirstChild("HumanoidRootPart") then
ShoulderCamera:UpdateCamera()
setCharacterJoints(Character)
Character.Humanoid.AutoRotate = false
end
end)
local function playerInput(actionName, inputState, inputObject)
local Character = ShoulderCamera.Character
if Character and Character:FindFirstChild("HumanoidRootPart") then
if inputState == Enum.UserInputState.Change then
ShoulderCamera.RotationX = ShoulderCamera.RotationX - inputObject.Delta.X * ShoulderCamera.CameraSpeed
ShoulderCamera.RotationY = math.clamp(ShoulderCamera.RotationY-inputObject.Delta.Y*0.4* ShoulderCamera.CameraSpeed, -75, 75)
updateCharacterRotation(Character, inputObject.Delta.X * ShoulderCamera.CameraSpeed)
end
end
end
ContextActionService:BindAction("PlayerInput", playerInput, false, Enum.UserInputType.MouseMovement, Enum.UserInputType.Touch)
ShoulderCamera.Connections.Move = Mouse.Move:Connect(function()
local Character = ShoulderCamera.Character
if Character and Character:FindFirstChild("HumanoidRootPart") then
end
end)
ShoulderCamera.Connections.InputBegan = UserInputService.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton2 then
ShoulderCamera.FOV = 60
ShoulderCamera.CameraSpeed = 0.15
ShoulderCamera.Aiming = true
end
end)
ShoulderCamera.Connections.InputEnded = UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton2 then
ShoulderCamera.FOV = 70
ShoulderCamera.CameraSpeed = 0.75
ShoulderCamera.Aiming = false
end
end)
ShoulderCamera.Connections.WheelForward = Mouse.WheelForward:Connect(function()
ShoulderCamera.Zoom = math.clamp(ShoulderCamera.Zoom - 1, 0, 10)
end)
ShoulderCamera.Connections.WheelForward = Mouse.WheelBackward:Connect(function()
ShoulderCamera.Zoom = math.clamp(ShoulderCamera.Zoom + 1, 0, 10)
end)
end
function ShoulderCamera:Stop()
UserInputService.MouseBehavior = Enum.MouseBehavior.Default
ContextActionService:UnbindAction("PlayerInput")
if ShoulderCamera.runConnection then
ShoulderCamera.runConnection:Disconnect()
end
for _,connection in pairs(ShoulderCamera.Connections) do
if connection then
connection:Disconnect()
end
end
local Character = ShoulderCamera.Character
if Character and Character:FindFirstChild("HumanoidRootPart") then
resetCharacterJoints(Character)
Character.Humanoid.AutoRotate = true
Camera.CameraType = "Custom"
Camera.CameraSubject = Character.Humanoid
end
end
return ShoulderCamera