My viewmodel gun recoil doesn’t include a spray pattern and feels choppy rather than smooth and natural. I’m aware that the current recoil implementation isn’t great, but I’d really appreciate some feedback on how I could improve it.
I’ve attached a video and the part of the script that handles the recoil.(Yeah, I know that code is bad.)
Are there any suggestions on how to make the recoil feel more natural and visually satisfying?
edit:And is there any way to make a recoil recovery system?
local XrecoilAmount = 0.6
local YrecoilAmount = 0.2
local returnSpeed = 0.3
local RecoliReturn = false
local baseCFrame
game:GetService("RunService").RenderStepped:Connect(function()
if not Firing then return end
baseCFrame = camera.CFrame
RecoliReturn = true
camera.CFrame = baseCFrame * CFrame.Angles(math.rad(XrecoilAmount + math.random(-0.1,0.1)), math.rad(YrecoilAmount + math.random(-1,1)), 0)
if RecoliReturn == true then
camera.CFrame = camera.CFrame:Lerp(baseCFrame, returnSpeed)
RecoliReturn = false
end
end)
Use a spring module such as this one. On firing the gun, apply a velocity to the spring and calculate the recoil CFrame from the spring’s position each step.
For recoil recovery this is what I’d do:
local retainedRecoil = CFrame.new()
local recovery = 0.5 --percent of recoil that is recovered
runService.RenderStepped:Connect(function()
local pos = recoilSpring.Position
local recoilTarget = CFrame.Angles(pos.X, pos.Y, pos.Z)
local recoil = recoilTarget
recoil *= CFrame.new():Lerp(retainedRecoil, recovery) --this adds the % of recoil that isnt recovered
retainedRecoil = recoilTget
cam.CFrame *= recoil
end
I’m not very familiar with this module, so I wrote the code like this. However, it feels a bit off, and the recoil is very shaky. Are there any tips for improving this, or any mistakes I might have made in my script? What would be a better way to structure this kind of code?
local retainedRecoil = CFrame.new()
local recovery = 0.2
local spring = require(game.ReplicatedStorage.Spring)
local runService = game:GetService(“RunService”)
local cam = workspace.CurrentCamera
local recoilSpring = spring.new(1, 1, 10, 0, 0, 0)
runService.RenderStepped:Connect(function()
local pos = Vector3.new(recoilSpring.Offset, 0, 0)
local recoilTarget = CFrame.Angles(pos.X, pos.Y, pos.Z)
this line will block any kind of user input, leaving your camera unmovable until the lerping is complete
you’d have to do camera.CFrame *= whatever if you want to allow camera movement while the recoil is being applied
renderStepped = rService.PreRender:Connect(function(deltaTime)
local alpha
if applying then -- the recoil is being applied
alpha = math.clamp(elapsed / kickTime, 0, 1) -- kickTime is 0.15, for example
-- clamp between 0, 1 since alpha cannot be lower or higher
if alpha >= 1 then -- if the recoil has finished kicking
applying = false
elapsed = 0
end
else -- not applying? recovery
alpha = 1 - math.clamp(elapsed / recoveryTime, 0, 1)
if alpha <= 0 then
-- if the recovery has finished
end
end
elapsed += deltaTime
end)
The shakiness is due to the spring’s settings, try adjusting the damper/stiffness to make it less bouncy. To get the right feel to it you just have to experiment with different settings. Other than that I don’t see any issues with the code so good job.
Could you tell me what each variable in this code does? I tried to recreate it exactly the same, but something feels off. I think I might have made a mistake somewhere.
After a lot of trial and error tweaking the recoil function variables, I finally managed to create recoil that I’m truly satisfied with. I want to thank the two people who helped me fix and improve this issue I really appreciate your support!