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.
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.
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!