I’ve worked out the solution a while ago by this point, but I felt like I should resolve the post. It could be done more efficiently but it works for what I need.
-- This script is mostly just the relevant parts of the camera script
local CAS = game:GetService("ContextActionService")
local UIS = game:GetService("UserInputService")
local RS = game:GetService("RunService")
local RepStore = game:GetService("ReplicatedStorage")
local TS = game:GetService("TweenService")
local plr = game.Players.LocalPlayer
local cam = game.Workspace.CurrentCamera
-- Rather than having two different global values, I can instead just use a remote event to change one local value
local shakeOffset = Vector3.new(0, 0, 0)
local follow = Instance.new("Part")
follow.CanCollide = false
follow.CanQuery = false
follow.Transparency = 1
follow.Anchored = true
follow.Parent = game.Workspace
local rootLerp = 0.6
local camAngX = 0
local camAngY = 0
local loop
local head
plr.CharacterAdded:Connect(function(chr)
local hum = chr:WaitForChild("Humanoid")
local root = chr:WaitForChild("HumanoidRootPart")
head = chr:WaitForChild("Head")
local function plrInput(_, inpState, inpObject)
if inpState == Enum.UserInputState.Change then
if _G.toggle == false then
camAngX = camAngX - inpObject.Delta.X
camAngY = math.clamp(camAngY - inpObject.Delta.Y * 0.4, -55, 35)
end
end
end
CAS:BindAction("plrInput", plrInput, false, Enum.UserInputType.MouseMovement)
local function moveCam()
if hum.Health > 0 then
if cam.CameraType ~= Enum.CameraType.Scriptable then
cam.CameraType = Enum.CameraType.Scriptable
end
follow.CFrame = follow.CFrame:Lerp(head.CFrame, 0.3)
local newCamOffset = currentCamOffset.Value
local startCF = CFrame.new(root.CFrame.Position) * CFrame.Angles(0, math.rad(camAngX), 0) * CFrame.Angles(math.rad(camAngY), 0, 0)
local camCFrame = startCF:ToWorldSpace(CFrame.new(newCamOffset.X, newCamOffset.Y, newCamOffset.Z))
local camFocus = startCF:ToWorldSpace(CFrame.new(newCamOffset.X, newCamOffset.Y, -10000))
local org = follow.Position + shakeOffset
local dir = CFrame.lookAt(follow.Position, camCFrame.Position).LookVector * 15
local exclude = {chr, workspace["enemies"], workspace["npcs"], workspace["raycastParts"], workspace["vfx"], workspace["projectiles"]}
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
for _,c in pairs(game.Workspace:GetChildren()) do
if c:IsA("Model") and game.Players:FindFirstChild(c.Name) then
table.insert(exclude, c)
end
end
params.FilterDescendantsInstances = exclude
local hitPos
if result then
hitPos = result.Position
else
hitPos = org + dir
end
local finalCF = CFrame.lookAt(hitPos, camFocus.Position)
cam.CFrame = CFrame.lookAt(hitPos, camFocus.Position)
lastCF = cam.CFrame
else
cam.CFrame = lastCF
end
end
RS:BindToRenderStep("moveCam", Enum.RenderPriority.Camera.Value + 1, moveCam)
end)
local function randomFromTable(arr)
local amnt = #arr
local random = math.random(1, amnt)
return arr[random]
end
local function cameraShake(intensity, decay)
local intensityCopy = intensity
local loop
loop = RS.Stepped:Connect(function()
local xOffset = randomFromTable({-1, 1})
local yOffset = randomFromTable({-1, 1})
local zOffset = randomFromTable({-1, 1})
-- Constantly change the camera's shake offset to a random Vector3 which decreases in magnitude over time
shakeOffset = shakeOffset:Lerp(Vector3.new(xOffset, yOffset, zOffset) * intensityCopy, 0.2)
intensityCopy *= decay
if intensityCopy < 0.1 then
-- When the intensity has lowered enough, stop the loop and remove the offset
loop:Disconnect()
shakeOffset = Vector3.new(0, 0, 0)
end
end)
end
RepStore["Events"]["shake"].OnClientEvent:Connect(cameraShake)
RepStore["Events"]["shake"]["bindable"].Event:Connect(cameraShake)