I’m just stuck currently with the spring module. I’ve tried to replicate a shooting recoil. The main problem is that my game is simulated from a helmet camera and adding camera shake would just omit realism. I’m not sure how to maneuver it and I’ve tried making arms shake etc. Nothing seems to work These little details is what I lack overall. BTW great footstep system it works well! I’m using something called EasyFirstPerson instead of a viewmodel as I’m new to it.
local userInputService = game:GetService("UserInputService")
local runService = game:GetService("RunService")
local tweenService = game:GetService("TweenService")
local lighting = game:GetService("Lighting")
local tool = script.Parent.Parent
local animationsFolder = script.Parent:WaitForChild("Animations")
local remotesFolder = script.Parent:WaitForChild("Remotes")
local mouse = game.Players.LocalPlayer:GetMouse()
local camera = game.Workspace.CurrentCamera
local Spring = require(game:GetService("ReplicatedStorage"):WaitForChild("Spring"))
-- Create springs for camera shake
local positionSpring = Spring.new(Vector3.zero, 1, 2) -- Spring for position
local rotationSpring = Spring.new(Vector3.zero, 1, 2) -- Spring for rotation
local equipped = false
local reloading = false
local checkingAmmo = false
local currentHumanoid = nil
local currentCharacter = nil
local currentGui = nil
local wasUnequipped = false
local idleAnim = nil
local shootAnim = nil
local adsshoot = nil
local equipAnim = nil
local reloadAnim = nil
local sprintAnim = nil
local walkAnim = nil
local aimAnim = nil
local AmmoCheck = nil
local fullAuto = true
local fireRate = 0.1
local shooting = false
local sprinting = false
local semiDebounce = false
local walking = false
local aiming = false
local currentCrosshair = nil
local CameraShaker = require(game.ReplicatedStorage.CameraShaker)
local camera = game.Workspace.CurrentCamera
local camShake = CameraShaker.new(Enum.RenderPriority.Camera.Value, function(shakeCf)
camera.CFrame = camera.CFrame * shakeCf
end)
camShake:Start()
-- Add more variation to the shoot animation
local randomSpeed = math.random(7, 15) / 10 -- Random speed between 0.7x and 1.5x
local randomTimePosition = math.random(0, 20) / 100 -- Random start position between 0 and 0.2 seconds
local swayStrength = 0.02 -- Adjust the sway strength
local swaySpeed = 5
--Shoot Replicated--
-- Inside the Shoot event handler
remotesFolder.Shoot.OnClientEvent:Connect(function(maxAmmo, ammo)
if aiming == true then
adsshoot:Play(randomTimePosition, randomSpeed)
else
shootAnim:Play(randomTimePosition, randomSpeed)
end
-- Apply recoil impulses to the springs
positionSpring:Impulse(Vector3.new(0, 0.1, -0.2)) -- Adjust values for desired recoil
rotationSpring:Impulse(Vector3.new(0.05, 0, 0)) -- Adjust values for desired recoil
-- Add the following lines to enable the blur effect
local blur = lighting:FindFirstChild("SHoot")
if blur then
blur.Enabled = true
wait(0.1) -- Adjust the duration of the blur effect as needed
blur.Enabled = false
end
-- Ammo Gui Animation
if currentGui ~= nil then
tweenService:Create(currentGui.Holder.Ammo, TweenInfo.new(0.1), {Size = UDim2.new(0.89, 0, 0.325, 0)}):Play()
currentGui.Holder.Ammo.Text = tostring(ammo).."/"..tostring(maxAmmo)
wait(0.1)
if currentGui ~= nil then
tweenService:Create(currentGui.Holder.Ammo, TweenInfo.new(0.1), {Size = UDim2.new(0.877, 0, 0.304, 0)}):Play()
end
end
end)
local RunService = game:GetService("RunService")
--Reload Replicated--
remotesFolder.Reload.OnClientEvent:Connect(function(reloadTime, maxAmmo)
wasUnequipped = false
reloading = true
reloadAnim:Play(0, 10)
script.Parent.Sounds.ReloadSound:Play()
wait(reloadTime)
if currentGui ~= nil and wasUnequipped == false then
currentGui.Holder.Ammo.Text = tostring(maxAmmo).."/"..tostring(maxAmmo)
end
reloading = false
end)
--Equip Handler--
tool.Equipped:Connect(function()
equipped = true
script.Parent.Sounds.EquipSound:Play()
userInputService.MouseIconEnabled = false
currentCrosshair = script.Parent.CrosshairGui:Clone()
currentCrosshair.Parent = game.Players.LocalPlayer.PlayerGui
currentHumanoid = tool.Parent:FindFirstChildOfClass("Humanoid")
currentCharacter = tool.Parent
idleAnim = currentHumanoid:LoadAnimation(animationsFolder.Idle)
idleAnim.Priority = Enum.AnimationPriority.Movement
shootAnim = currentHumanoid:LoadAnimation(animationsFolder.Shoot)
adsshoot = currentHumanoid:LoadAnimation(animationsFolder.Adsshoot)
equipAnim = currentHumanoid:LoadAnimation(animationsFolder.Equip)
reloadAnim = currentHumanoid:LoadAnimation(animationsFolder.Reload)
sprintAnim = currentHumanoid:LoadAnimation(animationsFolder.Sprint)
walkAnim = currentHumanoid:LoadAnimation(animationsFolder.walking)
aimAnim = currentHumanoid:LoadAnimation(animationsFolder.Aiming)
AmmoCheck = currentHumanoid:LoadAnimation(animationsFolder.AmmoCheck)
equipAnim:Play(0, 100)
idleAnim:Play(0.5, 1)
end)
--Unequip Handler--
tool.Unequipped:Connect(function()
shooting = false
aiming = false
sprinting = false
walking = false
equipped = false
reloading = false
checkingAmmo = false
wasUnequipped = true
wait()
userInputService.MouseIconEnabled = true
currentHumanoid = nil
currentCharacter = nil
idleAnim:Stop()
shootAnim:Stop()
equipAnim:Stop()
reloadAnim:Stop()
adsshoot:Stop()
sprintAnim:Stop()
walkAnim:Stop()
idleAnim = nil
AmmoCheck = nil
shootAnim = nil
equipAnim = nil
aimAnim = nil
adsshoot = nil
reloadAnim = nil
sprintAnim = nil
walkAnim = nil
currentCrosshair:Destroy()
currentCrosshair = nil
currentGui = nil
currentHumanoid.WalkSpeed = 4
script.Parent.Sounds.EquipSound:Stop()
script.Parent.Sounds.ReloadSound:Stop()
end)
--Input Handler--
userInputService.InputBegan:Connect(function(input, gameProcess)
if equipped == true then
if gameProcess then
return
end
if input.UserInputType == Enum.UserInputType.MouseButton1 then --Shoot
if fullAuto == true and semiDebounce == false then --If NOT semi
print("Client")
semiDebounce = true
shooting = true
while shooting == true do
remotesFolder.Shoot:FireServer(mouse.Hit.p, tool.Handle.Muzzle.WorldPosition, tool.Handle.CFrame, tool.Handle.Muzzle.WorldCFrame.LookVector)
--camShake:Shake(CameraShaker.Presets.Deagle2)
wait(fireRate)
end
wait(fireRate)
semiDebounce = false
else --If IS semi
if semiDebounce == false then
semiDebounce = true
remotesFolder.Shoot:FireServer(mouse.Hit.p, tool.Handle.Muzzle.WorldPosition, tool.Handle.CFrame, tool.Handle.Muzzle.WorldCFrame.LookVector)
--camShake:Shake(CameraShaker.Presets.Deagle2)
wait(fireRate)
semiDebounce = false
end
end
end
if input.KeyCode == Enum.KeyCode.R and reloading == false then --Reload
remotesFolder.Reload:FireServer()
end
if input.KeyCode == Enum.KeyCode.T then
-- Perform the raycast
end
if input.KeyCode == Enum.KeyCode.LeftShift and currentHumanoid.MoveDirection.magnitude > 0 then
sprinting = true
if sprinting == true then
sprintAnim:Play(0)
currentHumanoid.WalkSpeed = 10
end
end
if input.UserInputType == Enum.UserInputType.MouseButton2 then
aiming = true
if aiming == true then
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local ScopeParallax = require(ReplicatedStorage.ScopeParallax)
ScopeParallax.SetEnabled(true)
aimAnim:Play(0.5,7)
currentHumanoid.WalkSpeed = 2
else
currentHumanoid.WalkSpeed = 4
end
end
if input.KeyCode == Enum.KeyCode.G then
checkingAmmo = true
if checkingAmmo == true then
AmmoCheck:Play()
tool.Parts.Mag.Attachment.BillboardGui.Enabled = true
wait(2.5)
tool.Parts.Mag.Attachment.BillboardGui.Enabled = false
end
end
if input.KeyCode == Enum.KeyCode.V then --Reload
tool.Parts.Pointer.SpotLight.Enabled = not tool.Parts.Pointer.SpotLight.Enabled
end
end
end)
userInputService.InputEnded:Connect(function(input, gameProcess)
if equipped == true then
if gameProcess then
return
end
if input.UserInputType == Enum.UserInputType.MouseButton1 then --End of shoot
shooting = false
end
if input.KeyCode == Enum.KeyCode.LeftShift then
sprinting = false
sprintAnim:Stop(0.5, 2)
-- Stop the sprint animation here if needed
end
if input.KeyCode == Enum.KeyCode.G then
checkingAmmo = false
AmmoCheck:Stop(0.5, 2)
-- Stop the sprint animation here if needed
end
if input.UserInputType == Enum.UserInputType.MouseButton2 then
aiming = false
aimAnim:Stop()
end
end
end)
runService.RenderStepped:Connect(function()
-- Apply the spring values to the camera
local camera = workspace.CurrentCamera
local recoilOffset = CFrame.new(positionSpring.Position) * CFrame.Angles(rotationSpring.Position.X, rotationSpring.Position.Y, rotationSpring.Position.Z)
camera.CFrame = camera.CFrame * recoilOffset
-- Arm and character pointing towards mouse stuff
if equipped == true and mouse.Hit.Position ~= nil and currentCharacter ~= nil then
local rightX, rightY, rightZ = currentCharacter.Torso["Right Shoulder"].C0:ToEulerAnglesYXZ()
currentCharacter.Torso["Right Shoulder"].C0 = (currentCharacter.Torso["Right Shoulder"].C0 * CFrame.Angles(0, 0, -rightZ)) * CFrame.Angles(0, 0, math.asin((mouse.Hit.p - mouse.Origin.p).unit.y))
tweenService:Create(currentCharacter.Torso["Right Shoulder"], TweenInfo.new(0.2), {C0 = (currentCharacter.Torso["Right Shoulder"].C0 * CFrame.Angles(-rightX, 0, -rightZ)) * CFrame.Angles(0, 0, math.asin((mouse.Hit.p - mouse.Origin.p).unit.y))}):Play()
local leftX, leftY, leftZ = currentCharacter.Torso["Left Shoulder"].C0:ToEulerAnglesYXZ()
currentCharacter.Torso["Left Shoulder"].C0 = (currentCharacter.Torso["Left Shoulder"].C0 * CFrame.Angles(0, 0, -leftZ)) * CFrame.Angles(0, 0, math.asin((-mouse.Hit.p - -mouse.Origin.p).unit.y))
tweenService:Create(currentCharacter.Torso["Left Shoulder"], TweenInfo.new(0.2), {C0 = (currentCharacter.Torso["Left Shoulder"].C0 * CFrame.Angles(-rightX, 0, -leftZ)) * CFrame.Angles(0, 0, math.asin((-mouse.Hit.p - -mouse.Origin.p).unit.y))}):Play()
end
-- Crosshair updater
if equipped == true and currentCrosshair ~= nil then
currentCrosshair.Crosshair.Position = UDim2.fromOffset(mouse.X, mouse.Y)
end
end)
Above is the client for my gun script; The shoot remote is where shooting is fired.
local sensitivity = 2 -- how quick/snappy the sway movements are. Don't go above 2
local swaysize = 1 -- how large/powerful the sway is. Don't go above 2
local includestrafe = false -- if true the fps arms will sway when the character is strafing
local includewalksway = false -- if true, fps arms will sway when you are walking
local includecamerasway = false -- if true, fps arms will sway when you move the camera
local includejumpsway = false -- if true, jumping will have an effect on the viewmodel
local headoffset = Vector3.new(0,0,0) -- the offset from the default camera position of the head. (0,1,0) will put the camera one stud above the head.
local firstperson_arm_transparency = 0 -- the transparency of the arms in first person; set to 1 for invisible and set to 0 for fully visible.
local firstperson_waist_movements_enabled = false -- if true, animations will affect the Uppertorso. If false, the uppertorso stays still while in first person (applies to R15 only)
local CAMERA_HEIGHT = 1.3--0.69 for helmet camera
local CAMERA_DEPTH = -1.6 --was -0.8
local uis = game:GetService("UserInputService")
local runservice = game:GetService("RunService")
local tweenservice = game:GetService("TweenService")
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera
repeat runservice.Heartbeat:Wait() until script.Parent:IsA("Model") -- yield until character
local character = player.Character
local rootpart = character:WaitForChild("HumanoidRootPart")
local humanoid = character:WaitForChild("Humanoid")
local aimoffset = script:WaitForChild("AimOffset") -- a property for other scripts to use to influence the viewmodel offset (such as a gun aim system)
local torso
local roothip
local lowertorso
local oldc0
local leftshoulder
local rightshoulder
local larm
local rarm
local armparts = {}
local rigtype = nil
local isrunning = false
local armsvisible = true
local armtransparency = firstperson_arm_transparency
local isfirstperson = true
local sway = Vector3.new(0,0,0)
local walksway = CFrame.new(0,0,0)
local strafesway = CFrame.Angles(0,0,0)
local jumpsway = CFrame.new(0,0,0)
local jumpswaygoal = Instance.new("CFrameValue")
local viewmodel = Instance.new("Model")
local fakeroot = Instance.new("Part")
viewmodel.Name = "Viewmodel"
fakeroot.Name = "HumanoidRootPart"
fakeroot.CanCollide = false
fakeroot.CanTouch = false
fakeroot.Anchored = true
fakeroot.Transparency = 1
fakeroot.Parent = viewmodel
local faketorso = Instance.new("Part")
faketorso.Name = "Torso"
faketorso.CanCollide = false
faketorso.CanTouch = false
faketorso.Transparency = 1
faketorso.Parent = viewmodel
viewmodel.PrimaryPart = fakeroot
viewmodel.WorldPivot = fakeroot.CFrame+fakeroot.CFrame.UpVector*3
viewmodel.Parent = nil
local fakelowertorso = nil
local waistclone = nil
local leftshoulderclone = nil
local pos = nil
local rightshoulderclone = nil
local roothipclone = nil
local oldcpos = nil
camera.CameraType = Enum.CameraType.Scriptable
rigtype = "R6"
torso = character:WaitForChild("Torso")
-- add the arms
table.insert(armparts, character:WaitForChild("Right Arm"))
table.insert(armparts, character:WaitForChild("Left Arm"))
roothip = rootpart:FindFirstChildOfClass("Motor6D")
oldc0 = roothip.C0
leftshoulder = torso:WaitForChild("Left Shoulder")
rightshoulder = torso:WaitForChild("Right Shoulder")
faketorso.Size = torso.Size
fakeroot.Size = rootpart.Size
faketorso.CFrame = fakeroot.CFrame
roothipclone = roothip:Clone()
roothipclone.Parent = fakeroot
roothipclone.Part0 = fakeroot
roothipclone.Part1 = faketorso
leftshoulderclone = leftshoulder:Clone()
leftshoulderclone.Name = "LeftShoulderClone"
leftshoulderclone.Parent = torso
leftshoulderclone.Part0 = torso
rightshoulderclone = rightshoulder:Clone()
rightshoulderclone.Name = "RightShoulderClone"
rightshoulderclone.Parent = torso
rightshoulderclone.Part0 = torso
larm = character:WaitForChild("Left Arm")
rarm = character:WaitForChild("Right Arm")
if firstperson_arm_transparency >= 1 then
armsvisible = false
end
local function visiblearms(bool)
if armsvisible then
local castshadow = not bool
for i, part in ipairs(armparts) do
part.LocalTransparencyModifier = armtransparency
part.CastShadow = castshadow
end
end
end
local function enableviewmodel()
isfirstperson = true
viewmodel.Parent = workspace.CurrentCamera
rightshoulderclone.Enabled = true
leftshoulderclone.Enabled = true
leftshoulder.Enabled = false
rightshoulder.Enabled = false
rightshoulderclone.Part1 = rarm
rightshoulderclone.Part0 = faketorso
rightshoulderclone.Parent = faketorso
leftshoulderclone.Part1 = larm
leftshoulderclone.Part0 = faketorso
leftshoulderclone.Parent = faketorso
armtransparency = firstperson_arm_transparency
for i, v in pairs(character:GetChildren()) do
if v:IsA("Accessory") then
v:WaitForChild("Handle").LocalTransparencyModifier = 0
end
if v:IsA("Part") or v:IsA("MeshPart") then
v.LocalTransparencyModifier = 0
end
end
end
local function disableviewmodel()
isfirstperson = false
viewmodel.Parent = nil
rightshoulderclone.Enabled = false
leftshoulderclone.Enabled = false
viewmodel.Parent = nil
leftshoulder.Parent = torso
leftshoulder.Part0 = torso
leftshoulder.Part1 = larm
rightshoulder.Parent = torso
rightshoulder.Part0 = torso
rightshoulder.Part1 = rarm
leftshoulder.Enabled = true
rightshoulder.Enabled = true
armtransparency = 0
visiblearms(false)
for i, v in pairs(character:GetChildren()) do
if v:IsA("Accessory") then
v:WaitForChild("Handle").LocalTransparencyModifier = 0
end
if v:IsA("Part") or v:IsA("MeshPart") then
v.LocalTransparencyModifier = 0
end
end
end
local function checkfirstperson()
enableviewmodel()
end
uis.MouseDeltaSensitivity = 0.5
local playerchanged_con = nil
playerchanged_con = player.Changed:Connect(function(property)
if property == "CameraMaxZoomDistance" or property == "CameraMode" then
if player.CameraMaxZoomDistance <= 0.5 or player.CameraMode == Enum.CameraMode.LockFirstPerson then
enableviewmodel()
end
end
end)
if (game.StarterPlayer.CameraMode == Enum.CameraMode.LockFirstPerson) or (game.StarterPlayer.CameraMaxZoomDistance <= 0.5) then
enableviewmodel()
end
game:GetService("RunService").RenderStepped:connect(function()
checkfirstperson()
if isfirstperson == true then
visiblearms(true)
local delta = uis:GetMouseDelta()
rightshoulderclone.Transform = rightshoulder.Transform
leftshoulderclone.Transform = leftshoulder.Transform
--finalCF is the calculation for the position where the viewmodel should be
local finalcf = (camera.CFrame*walksway*jumpsway*strafesway*CFrame.Angles(math.rad(sway.Y*swaysize),math.rad(sway.X*swaysize)/10,math.rad(sway.Z*swaysize)/2))+(camera.CFrame.UpVector*(-1.7-(headoffset.Y+(aimoffset.Value.Y))))+(camera.CFrame.LookVector*(headoffset.Z+(aimoffset.Value.Z)))+(camera.CFrame.RightVector*(-headoffset.X-(aimoffset.Value.X)+(-(sway.X*swaysize)/75)))
viewmodel.PrimaryPart.CFrame = viewmodel.PrimaryPart.CFrame:Lerp(CFrame.new(finalcf.Position, Vector3.new(mouse.Hit.Position.X, mouse.Hit.Position.Y, mouse.Hit.Position.Z)),0.9)
if character:FindFirstChildWhichIsA("Tool") then --if a tool is equipped, enabled the viewmodel otherwise regular arms
if camera.CameraType == Enum.CameraType.Scriptable then
enableviewmodel()
end
else
if camera.CameraType == Enum.CameraType.Scriptable then
disableviewmodel()
end
end
if camera.CameraType == Enum.CameraType.Scriptable then --basically is a tool equipped
local divisor = 0.075 --how smooth the camera turning is, the lower the number the smoother and slower but the more buggy it is but the higher it is the less buggy but the less control of the camera the player has
if oldcpos then
local supamagnitude = (oldcpos-pos).Magnitude
divisor = supamagnitude*0.09
if divisor > 0.075 or divisor == 0 and supamagnitude < 2 then
divisor = 0.075
end
end
local targetPos = pos or character.Camera_Part.Position -- Use fallback if pos is nil
camera.CFrame = camera.CFrame:Lerp(CFrame.new(character.Camera_Part.Position, targetPos), divisor)
--camera.CFrame = CFrame.lookAt(camera.CFrame.Position, camera.CFrame.Position + Vector3.new(camera.CFrame.LookVector.X,-0.15,camera.CFrame.LookVector.Z)) --prevent the weird movement tilt
end
oldcpos = pos
end
end)
mouse.Move:Connect(function()
if mouse.Target then
pos = mouse.Hit.Position --if mouse moved then change pos, this is to only make the camera/viewmodel move when the player wants it to
end
end)
humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
if mouse.Target then
pos = mouse.Hit.Position --if player then change pos, otherwise pos is a set spot in workspace that you could walk up to and the camera would bug out
end
end)
and this is my EasyFirstPerson which has been edited. It would be of great help if you could provide me with some assistance!