I have a recoil system that currently just offsets your camera by a little bit, but the issue with it is that it can be completely nullified by just moving your mouse, resulting in practically no recoil
I came up with an idea to add ‘visual recoil’ on top of the gun recoil that cannot be controlled/nullified, but i’m not really sure how to implement it
What i want to do is;
Apply the normal gun recoil, but on top of that gun recoil, apply the camera shake
the camera should end up being the same as it was without any visual recoil (so basically what i have right now)
Since that explanation sucks absolute balls, i draw:
To make the ‘with’ section understandable, the gun recoil shouldn’t just make the camera bounce like that, the visual recoil is responsible for that.
The gun recoil should remain the same (Straight line A → B), while the visual recoil just shakes the camera.
I do not want the camera to ‘overflow’, i do not want it to go downwards when travelling, like this:
i think that explanation is very understandable, and i hope i get help ![]()
local function ApplyDeltaRotation(currentX, currentY, currentZ, lastX, lastY, lastZ)
local deltaX = currentX - lastX
local deltaY = currentY - lastY
local deltaZ = currentZ - lastZ
local deltaRotation = CFrame.Angles(deltaX, deltaY, deltaZ)
camera.CFrame *= deltaRotation
return currentX, currentY, currentZ
end
local function ApplyRecoil(gunData: {})
local recoilTweak: NumberValue = utility.GetGameplayValue('recoilTweak')
local elapsed = 0
local applying = true
local renderStepped: RBXScriptConnection = nil
local lastX, lastY, lastZ = 0, 0, 0
local recoilStrength = gunData.recoilStrength
local kickTime = gunData.kickTime
local recoveryTime = gunData.recoveryTime
if renderStepped then
renderStepped:Disconnect()
end
local x, y, z = recoilStrength:ToEulerAnglesXYZ()
x /= recoilTweak.Value
y /= recoilTweak.Value
z /= recoilTweak.Value
renderStepped = rService.PreRender:Connect(function(deltaTime)
local alpha
if applying then
alpha = math.clamp(elapsed / kickTime, 0, 1)
if alpha >= 1 then
applying = false
elapsed = 0
end
else
alpha = 1 - math.clamp(elapsed / recoveryTime, 0, 1)
if alpha <= 0 then
local removeX = -lastX
local removeY = -lastY
local removeZ = -lastZ
local removeRotation = CFrame.Angles(removeX, removeY, removeZ)
camera.CFrame *= removeRotation renderStepped:Disconnect()
return
end
end
local currentX = x * alpha
local currentY = y * alpha
local currentZ = z * alpha
lastX, lastY, lastZ = ApplyDeltaRotation(currentX, currentY, currentZ, lastX, lastY, lastZ)
elapsed += deltaTime
end)
end


