How to make an FPS viewmodel part 3! Sway and reloading!

Please Read Carefully!
Step 1 Sway!
To add sway to a gun hop into your local script, now replace this

RunService.RenderStepped:Connect(function(step)
	idle:Play()
	script.Parent.PrimaryPart.CFrame = game.Workspace.CurrentCamera.CFrame
end)

with

local mult = 1 -- multiply by
local swayOffset = CFrame.new() -- the cframe
local lastCameraCF = workspace.CurrentCamera.CFrame -- camera cframe
RunService.RenderStepped:Connect(function(step)
	idle:Play() -- play idle
	local camframe = game:GetService("Workspace").CurrentCamera.CFrame -- camera cframe while playing
	script.Parent.HumanoidRootPart.CFrame = camframe + camframe.LookVector * -0.5 + camframe.UpVector * -0.2 + camframe.RightVector * -0.5 -- how much the camera angle is changed by
	local rotation = workspace.CurrentCamera.CFrame:toObjectSpace(lastCameraCF) -- the rotation of the camera 
	local x,y,z = rotation:ToOrientation() 
	swayOffset = swayOffset:Lerp(CFrame.Angles(math.sin(x)*mult,math.sin(y)*mult,0), 0.1)  -- offset
	script.Parent.HumanoidRootPart.CFrame = script.Parent.HumanoidRootPart.CFrame * swayOffset 
	lastCameraCF = workspace.CurrentCamera.CFrame -- the last cframe
end)

This will make your gun sway, read the notes to further understand.
New Project - 2022-05-28T154610.537

Part 2 Reloads!
Create a new number value inside of the server script, now name it Ammo set the value to the amount of ammo you want. Inside of your server script do at the very top local ammo = script.Ammo then write ammo.Value -= 1 under script.Parent.Local.RemoteEvent.OnServerEvent:Connect(function(player,mousePos) At the very bottom write

while true do
	wait()
	if ammo.Value == 0 then
		wait(script.Parent.Handle.Reload.TimeLength)
		ammo.Value = 15
	end
end

This basically checks if ammo = 0 then if true reloads, set 15 to your amount of ammo.
Now go into the animation editor and make an animation for your gun, then set animation type to animation 2, now inside of your handle make an animation called Reload Anim and paste the animation. Now find a reload sound and rename it to Reload and put it in handle.
Now hop onto your Local Script and replace this

		if (Camera.CFrame.Position - Head.Position).Magnitude < 0.8 then -- If in first person
			db = true -- start cooldown
			script.RemoteEvent:FireServer(Mouse.Hit.Position) -- will fire a server that sets of a server script
			script.Parent.Handle.Shoot:Play()
			ShootAnim:Play()
			wait(0.5) -- time between cooldown
			db = false -- end cooldown
		end

with this

		if (Camera.CFrame.Position - Head.Position).Magnitude < 0.8 then -- If in first person
			if script.Parent.Server.Ammo.Value >= 1 then
				db = true -- start cooldown
				script.RemoteEvent:FireServer(Mouse.Hit.Position) -- will fire a server that sets of a server script
				script.Parent.Handle.Shoot:Play()
				ShootAnim:Play()
				wait(0.5) -- time between cooldown
				db = false -- end cooldown
			else
				ReloadAnim:Play()
				script.Parent.Handle.Reload:Play()
				db = true
				wait(script.Parent.Handle.Reload.TimeLength)
				db = false
			end
		end

This will check if you have to reload and then play anims if you do.
Your final local script should look like this:

local idle = script.Parent:WaitForChild("AnimationController").Animator:LoadAnimation(script.Parent.Handle.Idle)
local ShootAnim = script.Parent:WaitForChild("AnimationController").Animator:LoadAnimation(script.Parent.Handle.ShootAnim)
local ReloadAnim = script.Parent:WaitForChild("AnimationController").Animator:LoadAnimation(script.Parent.Handle.ReloadAnim)
local RunService = game:GetService("RunService")
local mult = 1
local swayOffset = CFrame.new()
local lastCameraCF = workspace.CurrentCamera.CFrame
RunService.RenderStepped:Connect(function(step)
	idle:Play()
	local camframe = game:GetService("Workspace").CurrentCamera.CFrame
	script.Parent.HumanoidRootPart.CFrame = camframe + camframe.LookVector * -0.5 + camframe.UpVector * -0.2 + camframe.RightVector * -0.5
	local rotation = workspace.CurrentCamera.CFrame:toObjectSpace(lastCameraCF) 
	local x,y,z = rotation:ToOrientation() 
	swayOffset = swayOffset:Lerp(CFrame.Angles(math.sin(x)*mult,math.sin(y)*mult,0), 0.1) 
	script.Parent.HumanoidRootPart.CFrame = script.Parent.HumanoidRootPart.CFrame * swayOffset 
	lastCameraCF = workspace.CurrentCamera.CFrame 
end)
local RATE_PER_SECOND = 0
RunService.RenderStepped:Connect(function(step)
	local increment = RATE_PER_SECOND * step
	for index, instance in pairs(script.Parent:GetDescendants()) do 
		if instance:IsA("BasePart") then
			instance.LocalTransparencyModifier = 0
		end
	end
end)

local db = false
local Camera = game.Workspace.CurrentCamera
local Head = game.Players.LocalPlayer.Character.Head
local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()
Mouse.Button1Down:Connect(function() -- if mouse down
	if db == false then -- If the debounce is dissabled (Debounce is a cooldown)
		if (Camera.CFrame.Position - Head.Position).Magnitude < 0.8 then -- If in first person
			if script.Parent.Server.Ammo.Value >= 1 then
				db = true -- start cooldown
				script.RemoteEvent:FireServer(Mouse.Hit.Position) -- will fire a server that sets of a server script
				script.Parent.Handle.Shoot:Play()
				ShootAnim:Play()
				wait(0.5) -- time between cooldown
				db = false -- end cooldown
			else
				ReloadAnim:Play()
				script.Parent.Handle.Reload:Play()
				db = true
				wait(script.Parent.Handle.Reload.TimeLength)
				db = false
			end
		end
	end
end)

and your final server script should look like this:

local ammo = script.Ammo

script.Parent.Local.RemoteEvent.OnServerEvent:Connect(function(player,mousePos) -- gets player and mouse position
	ammo.Value -= 1
	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = {player.Character}
	raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

	local raycastResult = workspace:Raycast(player.Character.HumanoidRootPart.Position,(mousePos - player.Character.HumanoidRootPart.Position)*1000,raycastParams) -- Were ray starts

	if raycastResult then
		local hitPart = raycastResult.Instance -- what the ray hits
		local model = hitPart:FindFirstAncestorOfClass("Model") -- if ray hits model

		if model then
			if model:FindFirstChild("Humanoid") then -- if hits model with humanoid
				model.Humanoid.Health -= 30 -- hurt player replace number with damage number
			end
		end
	end
end)

while true do
	wait()
	if ammo.Value == 0 then
		wait(script.Parent.Handle.Reload.TimeLength)
		ammo.Value = 15
	end
end

Final product:
https://www.roblox.com/library/9976650514/Finished-viewmodel

9 Likes

i suppose you should add images or videos to demonstrate your code

4 Likes

Oh sorry I’ll do that. I forgot.

1 Like

why cant i animate the gun correctly? i try to make a reload animation, but the gun doesnt wanna move.

1 Like