update: I found a way to make the recoil less instant by interpolating the offset, instead of just directly setting it to the recoil:
local RS = game:GetService("RunService")
local cam = workspace.CurrentCamera
local max = math.max
local min = math.min
local function lerp(a, b, t)
return a * (1 - t) + (b * t)
end
-- variables
local recoil = math.rad(9)
local recoilTime = 1/25 -- (seconds) the lower, the snappier the recoil (instant below 1/60)
recoilTime = 1/recoilTime
local recovTime = .15 -- (seconds) the lower, the faster the recoil recovers
recovTime = 1/recovTime
local maxRecov = 1/3 -- 0 is full recovery, 1 is no recovery
local compensation = 0 -- 0 forgets the previous shots, 1 treats all shots as one
maxRecov = min(maxRecov, 1-1e-16) -- prevent divide by 0
maxRecov = -1-1/(maxRecov-1) -- maps 0,1 to 0,inf (makes tweaking easier) https://www.desmos.com/calculator/ggdwhvw4ga
-- initialize
local offs = 0 -- final offset
local gOffs = 0 -- goal offset
local pOffs = 0 -- previous offset
local zigzag = .3
RS.RenderStepped:Connect(function(dt)
local speed = recovTime*dt
offs = lerp(offs, gOffs, min(recoilTime*dt, 1)) -- smooth the impulse to make it less seizure inducing
cam.CFrame *= CFrame.Angles(offs, offs*zigzag, 0) -- set the offset
gOffs = max(1 - speed*maxRecov, 0) * (pOffs - gOffs*min(speed, 1)) -- recover
pOffs = gOffs -- keep track of the previous offset
end)
local function applyRecoil()
pOffs *= compensation -- so it compensates less for the previous shots
gOffs = recoil -- set the new goal
zigzag *= -1
end
-- for testing
local plr = game:GetService("Players").LocalPlayer
local mouse = plr:GetMouse()
mouse.Button1Down:Connect(applyRecoil)
and a version where the offset is a vector for more possibilities
also the recoil twists the camera:
local RS = game:GetService("RunService")
local cam = workspace.CurrentCamera
local max = math.max
local min = math.min
local function lerp(a, b, t)
return a * (1 - t) + (b * t)
end
-- variables
local recoil = math.rad(9) * Vector3.new(1, .5, 1) -- angle * (vertical, horizontal, twist) multipliers
local recoilTime = 1/25 -- (seconds) the lower, the snappier the recoil (instant below 1/60)
recoilTime = 1/recoilTime
local recovTime = .2 -- (seconds) the lower, the faster the recoil recovers
recovTime = 1/recovTime
local maxRecov = 1/3 -- 0 is full recovery, 1 is no recovery
local compensation = 0 -- 0 forgets the previous shots, 1 treats all shots as one
maxRecov = min(maxRecov, 1-1e-16) -- prevent divide by 0
maxRecov = -1-1/(maxRecov-1) -- maps 0,1 to 0,inf (makes tweaking easier) https://www.desmos.com/calculator/ggdwhvw4ga
-- initialize
local zero = Vector3.new()
local offs = zero -- final offset
local gOffs = zero -- goal offset
local pOffs = zero -- previous offset
local zigzag = 1
RS.RenderStepped:Connect(function(dt)
local speed = recovTime*dt
offs = offs:Lerp(gOffs, min(recoilTime*dt, 1)) -- smooth the impulse to make it less seizure inducing
cam.CFrame *= CFrame.Angles(offs.X, offs.Y, offs.Z) -- set the offset
gOffs = max(1 - speed*maxRecov, 0) * (pOffs - gOffs*min(speed, 1)) -- recover
pOffs = gOffs -- keep track of the previous offset
end)
local function applyRecoil()
pOffs *= Vector3.new(compensation, compensation, 1) -- so it compensates less for the previous shots (except twist, because you can't affect that with your mouse)
gOffs = Vector3.new(recoil.X, zigzag*recoil.Y, -zigzag*recoil.Z) -- set the new goal
zigzag *= -1
end
-- for testing
local plr = game:GetService("Players").LocalPlayer
local mouse = plr:GetMouse()
mouse.Button1Down:Connect(applyRecoil)
recoil smooth attack.rbxl (31.3 KB)