Before i start I would like to say that this is not the only way to make an fps viewmodel, this is just my way which I have been doing for months now so I hope you enjoy.
Last thing Part 3 will focus on reloads and sway.
Step 1 Damage
Create a script called Server and rename your local script to Local.
Inside of your local script type this,
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
db = true -- start cooldown
print("hi")
wait(0.5) -- time between cooldown
db = false -- end cooldown
end
end
end)
This will make it so when you click it prints “hi”
Now inside of handle add a fire sound and name it Shoot.
Now replace print(“hi”) with script.Parent.Handle.Shoot:Play()
This will play the shoot sfx. Now create a remote event inside of Local, after script.Parent.Handle.Shoot:Play()
do script.RemoteEvent:FireServer(Mouse.Hit.Position)
This will fire the server and make it so you can connect a server script to this. Now open the script server and paste this:
script.Parent.Local.RemoteEvent.OnServerEvent:Connect(function(player,mousePos) -- gets player and mouse position
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)
This will create a ray wich if a player gets hit 30 damage will be taken away from the players health, you can replace 30 with the amount of damage you want taken.
Last step for part 2 Animations!
First open the animation editor and create an Idle animation for your gun, now set looped to true,
now set the animation type to action.

You can now export the animation and copy the id.
Now inside handle create an animation call this animation Idle and paste the id

Now inside your local script at the top type:
local idle = script.Parent:WaitForChild("AnimationController").Animator:LoadAnimation(script.Parent.Handle.Idle)
now under RunService.RenderStepped:Connect(function(step)
write idle:Play()
Now your idle animation should work! The last step for today is to create the shoot animation, hop into the animator and create a shoot animation, do not loop the animation. Then set the animation type to action. Export and create a new animation in handle, call this animation ShootAnim and paste your id.
Now inside of Local at the top paste
local ShootAnim = script.Parent:WaitForChild("AnimationController").Animator:LoadAnimation(script.Parent.Handle.ShootAnim)
and right after script.Parent.Handle.Shoot:Play()
write ShootAnim:Play()
The local script should look like this in the end,
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 RunService = game:GetService("RunService")
RunService.RenderStepped:Connect(function(step)
idle:Play()
script.Parent.PrimaryPart.CFrame = game.Workspace.CurrentCamera.CFrame
end)
RunService.RenderStepped:Connect(function(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
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
end
end)
That’s the end of part 2 like for part 3!
Part 3: