Need help fixing this camera recoil

I’m trying to create accurate recoil in my game, and I’m doing this by using methods by triple A titles such as Apex Legends or Battlefield V.

Recoil in Apex Legends (Focus on camera):

Recoil in Battlefield V(Focus on camera):

I will be using the following variables to explain each scenario (Assume numbers in degrees):
Start angle = S
End angle = E
Camera movement on the Y axis = Y
Recoil Applied = R
Recoil to Counter = C

RITSS = Recoil Increases Till Stop Shooting

Scenario one: RITTS and camera is not moved, it returns to normal.
S = 0, E = 0, Y = 0, R = 30, C = 30

Scenario two: RITSS and camera is slightly countering recoil, returns to normal.
S = 0, E = 0, Y = -15, R = 30, C = 15

Scenario three: RITTS and camera completely counters recoil, camera remains level.
S = 0, E = 0, Y = -30, R = 30, C = 0

Scenario four: RITTS and camera overcompensates for recoil, camera remains level.
S = 0, E = -15, Y = -45, R = 30, C = 0

Scenario five: RITTS and camera gets moved up with recoil, camera returns to level + amount moved.
S = 0, E = 15, Y = 15, R = 30, C = 30

So far, I have managed to make it so the recoil keeps increasing until they stop shooting, and then return to the angle they were aiming it if they didn’t move the camera.
However, if they did keep the camera level for example, where you would expect the camera to remain level the camera still moves the camera down by how much recoil there is.
Also, the minimum angle when shooting is reduced by how much recoil there is, for example if the minimum angle is -85, and there is recoil of 15 the minimum angle is -70 while shooting then returns to -85 after shooting.
This is just the bare bones of what I have at the moment since in the framework I have this is spread over hundreds of lines.

My recoil:

local cf = CFrame.new
local ang = CFrame.Angles
local rad = math.rad
local v3 = Vector3.new

local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable

local player = game:GetService("Players").LocalPlayer
local chr = player.Character
local rootpart = chr.HumanoidRootPart
player.CameraMode = Enum.CameraMode.LockFirstPerson

local DeltaY = 0
local RotY = 0
local shooting = false
local rpm = 60/1200
local lastshot = 0
local vcam = 0
local vccf = cf()

player:GetMouse().Button1Down:Connect(function()
	shooting = true
end)

player:GetMouse().Button1Up:Connect(function()
	shooting = false
end)

local function firestep()
	if shooting then
		local tfl = tick() - lastshot
		if tfl >= rpm then
			vcam = vcam + rad(2)
			lastshot = tick()
			print("shooting")
		end
	end
	vccf = vccf:lerp(ang(vcam, 0, 0), 0.1)
	if tick() - lastshot > rpm*1.2 then--or not shooting then
		if vcam > 0.001 then 
			vcam = vcam/2
			print("div")
		else 
			vcam = 0 
		end
	end
end

game:GetService("UserInputService").InputChanged:Connect(function(input, gpe)
	if gpe then return end
	if input.Delta ~= v3() then
		DeltaY = input.Delta.Y*0.2
	end
end)

game:GetService("RunService").RenderStepped:Connect(function(s)
	firestep()
	RotY = math.min(math.max(RotY - rad(DeltaY), rad(-85)), rad(85))
	camera.CFrame = 
		cf(rootpart.Position)*
		cf(0, 1.8, 0)*
		ang(RotY,0,0)*
		vccf
	DeltaY = 0
end)

You can try this out for yourself by putting a local script in an empty game in StarterCharacterScripts.

My recoil:

I really need help on this, I tried creating a counter lerp or taking away DeltaY from the recoil but it doesn’t work and I’m really stuck for ideas. I would really appreciate it if someone could help me!

You should use a spring algorithm to create recoil. If you use this nice object orientated one (included below), you can create a spring object and update its target after a shot is made and then set the gun’s CFrame accordingly.

Then just dampen the CFrame of the camera to match an offsetted position of the gun. This will get you a similar effect to the one seen in Battlefield 5.

local Spring = {}
Spring.__index = Spring
 
function Spring:Update(dt)
	local t, k, d, x0, v0 = self.t, self.k, self.d, self.x, self.v
	local a0 = k*(t - x0) + v0*d
	local v1 = v0 + a0*(dt/2)
	local a1 = k*(t - (x0 + v0*(dt/2))) + v1*d
	local v2 = v0 + a1*(dt/2)
	local a2 = k*(t - (x0 + v1*(dt/2))) + v2*d
	local v3 = v0 + a2*dt
	local x4 = x0 + (v0 + 2*(v1 + v2) + v3)*(dt/6)
	self.x, self.v = x4, v0 + (a0 + 2*(a1 + a2) + k*(t - (x0 + v2*dt)) + v3*d)*(dt/6)
	return x4
end
 
function Spring.new(stiffness, dampingCoeff, dampingRatio, initialPos)
	local self = setmetatable({}, Spring)
 
	dampingRatio = dampingRatio or 1
	local m = dampingCoeff*dampingCoeff/(4*stiffness*dampingRatio*dampingRatio)
	self.k = stiffness/m
	self.d = -dampingCoeff/m
	self.x = initialPos
	self.t = initialPos
	self.v = initialPos*0
 
	return self
end
 
return Spring

I struggle to understand the maths behind it myself, but it works, it’s free, and it’s great.

7 Likes

thank you for your reply, however i’m looking for specifically camera recoil not weapon recoil.

I am fairly sure that could work as well.

Plus the equations would be the same for both.

1 Like

Give the weapon recoil and then just dampen the camera. That’s how they do it in both games you showed in your original post.