Setting up a camera shake

I’m trying to offset a custom camera, but it isn’t working properly. It looks like it’s offsetting the camera relative to the world than relative to the camera’s position, however I’m using CFrames to offset, so I’m not sure why it’s doing this.

I’m only changing the z-offset for testing however when shaking the camera at a certain angle it shakes from left to right rather than forwards to backwards

Module Script
local shakeVal = {-1, 1}
module.cameraShake = function(len, int)
	local function shakeLength()
		local str = int
		for t = 1, len do
			_G.ShakeOffsetTarget = CFrame.new((shakeVal[math.random(1, #shakeVal)] * 0), (shakeVal[math.random(1, #shakeVal)] * str * 0), (shakeVal[math.random(1, #shakeVal)] * str * 100))
			--print(tostring(_G.ShakeOffsetTarget))
			str *= 0.95
			task.wait(0.05)
		end
		_G.ShakeOffsetTarget = CFrame.new(0, 0, 0)
	end
		
	local coro = coroutine.create(shakeLength)
	coroutine.resume(coro)
end
Local Script for camera
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
local camOffset = {
	[false] = Vector3.new(0, 10, 65),
	[true] = Vector3.new(10, 10, 65)
}
_G.ShakeOffset = CFrame.new(0, 0, 0)
_G.ShakeOffsetTarget = CFrame.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 sTog = false
_G.ShiftLock = false
local weight = 10

local currentCamOffset = Instance.new("Vector3Value")
currentCamOffset.Value = camOffset[sTog]

local lastCF

local function focusControl(_, inpState, _)
	if inpState == Enum.UserInputState.Begin then
		if _G.toggle == false then
			UIS.MouseBehavior = Enum.MouseBehavior.LockCenter
			--UIS.MouseIconEnabled = false
		end
	end
end

CAS:BindAction("focusControl", focusControl, false, Enum.UserInputType.MouseButton1, Enum.UserInputType.Focus)

local stDb = false

local function shiftToggle(_, state, _)
	if state == Enum.UserInputState.Begin then
		if stDb == false then
			stDb = not stDb
			sTog = not sTog
			_G.ShiftLock = sTog
			local newCamOffset = camOffset[sTog]
			local info = TweenInfo.new(0.3, Enum.EasingStyle.Circular, Enum.EasingDirection.Out)
			local tween = TS:Create(currentCamOffset, info, {Value = newCamOffset})
			tween:Play()
			local finCon
			finCon = tween.Completed:Connect(function()
				finCon:Disconnect()
				stDb = not stDb
			end)
		end
	end
end

CAS:BindAction("shiftToggle", shiftToggle, false, Enum.KeyCode.LeftControl)

local camAngX = 0
local camAngY = 0
local loop

local hum
local root
local head

plr.CharacterAdded:Connect(function(chr)
	if loop then loop:Disconnect() end
	
	hum = chr:WaitForChild("Humanoid")
	root = chr:WaitForChild("HumanoidRootPart")
	head = chr:WaitForChild("Head")
	
	sTog = false
	_G.ShiftLock = sTog
	
	currentCamOffset.Value = camOffset[sTog]

	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)
	
	loop = RS.RenderStepped:Connect(function()
		if hum.Health > 0 then
			if cam.CameraType ~= Enum.CameraType.Scriptable then
				cam.CameraType = Enum.CameraType.Scriptable
			end
			
			_G.ShakeOffset = _G.ShakeOffset:Lerp(_G.ShakeOffsetTarget, 0.3)
			
			local followPos = follow.Position
			local headPos = root.Position + Vector3.new(0, 1.5, 0)
			local xPos = Vector3.new(followPos.X, 0, followPos.Z):Lerp(Vector3.new(headPos.X, 0, headPos.Z), script:GetAttribute("xLerp"))
			local yPos = Vector3.new(0, followPos.Y, 0):Lerp(Vector3.new(0, headPos.Y, 0), script:GetAttribute("yLerp"))
			follow.Position = xPos + yPos

			--local camOffsetFinal = Vector3.new(currentCamOffset.Value.X, currentCamOffset.Value.Y, 100 + zoom)
			local newCamOffset = currentCamOffset.Value-- + _G.ShakeOffset
			local startCF = CFrame.new(follow.CFrame.Position) * CFrame.fromOrientation(0, math.rad(camAngX), 0) * CFrame.fromOrientation(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
			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

			--require(RepStore["debugModule"]).visualiseRaycast(org, dir)
			local result = workspace:Raycast(org, dir, params)

			local hitPos
			if result then
				hitPos = CFrame.new(result.Position) * _G.ShakeOffset
			else
				hitPos = CFrame.new(org + dir) * _G.ShakeOffset
			end
			--print(tiltOffset)
			local finalCF = CFrame.lookAt(hitPos.Position, camFocus.Position)
			cam.CFrame = cam.CFrame:Lerp(finalCF, 0.6)

			hum.AutoRotate = not sTog
			if sTog then
				root.CFrame = root.CFrame:Lerp(CFrame.lookAt(root.Position, Vector3.new(finalCF.LookVector.X * 900000, root.Position.Y, finalCF.LookVector.Z * 900000)), 0.6)
			end
			lastCF = cam.CFrame
		else
			cam.CFrame = lastCF
			loop:Disconnect()
		end
	end)
	
	hum.Died:Connect(function()
		loop:Disconnect()
	end)

	--RS:BindToRenderStep("moveCam", Enum.RenderPriority.Camera.Value + 1, moveCam)
end)
4 Likes

You can use ez camera shaker. EZ Camera Shake ported to Roblox

2 Likes

This was a while ago, but this doesn’t help pedro’s issue. He wants to fix his script and I know I’m not the only one looking to figure this out. Some people want to understand the engine and create their own modules.

1 Like

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)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.