FPS Tutorial [Part 4]

Hello everyone and this is the fourth part to the fps tutorial. Its the part about reloading!

Disclaimer: I am not a beginner in the FPS world anymore but I still make mistakes! In this part I will show you how I did it so please don’t tell me that its the wrong way. I also recommend you read every single word as this is complex and not for beginners.

In this part we will make the gun reload.

Its been a long time coming and was rewritten a few times hence it took so long. Without further I do, we shall begin!

1. Preparation

  • First you will need to make a decision on how to make the reloading ex. you drop the mag when reloading, you have stored ammo, unlimited stored ammo just to name a few.
    In this tutorial we will make it so you have unlimited stored ammo, but it will be easy to customize to have limited stored ammo.

  • Now how will we make it? Here’s how it works:


    Since we are making it so we have unlimited stored ammo it will look a bit different.

  • Lets get to setting up the game!

Inside our gun settings we will need a few variables:

Settings.storedammo = math.huge --the stored ammo aka backpack math.huge is infinate
Settings.magsize = 30 --ammo that the gun can hold
Settings.currentammo = 30 --ammo that the gun currently holds

We will also need to make the gun lose ammo when shooting, put this inside the mousebutton1down:

if weaponmodule.currentammo > 0 then
    fire()
end	

That’s it for the settings for now. Lets head to the framework

2. The Logic

  • Now we will be going into the framework and transferring the logic there.

To start of we will make a function called reload() in the framework somewhere below the fire function.

function reload()
    --we will add code here in a moment
end

So the first thing we need to do as shown in the diagram - make the gun not shoot, and how do we do that? By making a new bool called canfire and setting it to true:

local canfire = true

Now we have to make the gun check if the variable is true to shoot, we will do it like this:

mouse.Button1Down:Connect(function()
    if weaponmodule.currentammo > 0 and canfire then --this just means if canfire == true but we are saving space
        fire()
    end	
end)

Now if we were to set canfire to false our gun wouldn’t shoot, brilliant.
Lets get back on track again, so inside the reload function set the canfire to false just like this:

function reload()
    canfire = false
end

Now for the second point we need to check if the gun has enough stored ammo to reload, there are two ways to do it - the first one to just check if stored ammo is higher or equal to our magazine size or the second to check if the stored ammo is higher than 0, this way we will need to do some extra math inside the reload function but it is much more preferred by FPS developers.

For this tutorial I will show you the first way, let’s get back on topic and check if stored ammo is higher than 0 inside the reload function:

function reload()
    canfire = false
    if weaponmodule.storedammo >= weaponmodule.magsize and weaponmodule.currentammo < weaponmodule.magsize then
        --here we will put our reload code
    end
end

Now that we have that logic going on we will go to the actual reloading logic part of the logic:

  • Play reload animation
  • Minus stored ammo
  • Restore the current mag to its full capacity

Animation
First we will need a variable for the animation id in the module:

local Settings = {}
Settings.maincf = CFrame.new(0, -1, 0.5) * CFrame.Angles(0 ,0, 0)
Settings.aimcf = CFrame.new(-0.58,0.1, 2) * CFrame.Angles(0,0,0)
Settings.idleanim = "rbxassetid://6339207070"
Settings.reloadanim = "rbxassetid://7016589629"
Settings.storedammo = 500
Settings.magsize = 30
Settings.currentammo = 30
Settings.reloadtime = 3
return Settings

Then we will need to load it and play it inside our reload function:

local reload = Instance.new("Animation", currentweapon)
reload.AnimationId = weaponmodule.reloadanim
local reloadanim = currentweapon:WaitForChild("Humanoid"):LoadAnimation(reload)
reloadanim:Play()

We are done with our animation.

Minus stored ammo

We can do that very easily inside our if statement:

function reload()
    canfire = false
    if weaponmodule.storedammo >= weaponmodule.magsize and weaponmodule.currentammo < weaponmodule.magsize then
        local reload = Instance.new("Animation", currentweapon)
        reload.AnimationId = weaponmodule.reloadanim
        local reloadanim = currentweapon:WaitForChild("Humanoid"):LoadAnimation(reload)
        reloadanim:Play()
        weaponmodule.storedammo -= weaponmodule.magsize
    end
end

Reseting the mag to full

This is also very easy and we can do it after we minus the stored ammo like this:

function reload()
    canfire = false
    if weaponmodule.storedammo >= weaponmodule.magsize and weaponmodule.currentammo < weaponmodule.magsize then
        local reload = Instance.new("Animation", currentweapon)
        reload.AnimationId = weaponmodule.reloadanim
        local reloadanim = currentweapon:WaitForChild("Humanoid"):LoadAnimation(reload)
        reloadanim:Play()
        weaponmodule.storedammo -= weaponmodule.magsize
        weaponmodule.currentammo = weaponmodule.magsize
    end
end

A couple of additions

Just to make this more flexible and actually work we will add reloading time:

--inside the module
local Settings = {}
Settings.maincf = CFrame.new(0, -1, 0.5) * CFrame.Angles(0 ,0, 0)
Settings.aimcf = CFrame.new(-0.58,0.1, 2) * CFrame.Angles(0,0,0)
Settings.idleanim = "rbxassetid://6339207070"
Settings.reloadanim = "rbxassetid://7016589629"
Settings.storedammo = 500
Settings.magsize = 30
Settings.currentammo = 30
Settings.reloadtime = 3 --make sure its about as longs as the animation
return Settings

Then we will add a wait inside the if statement

if weaponmodule.storedammo >= weaponmodule.magsize and weaponmodule.currentammo < weaponmodule.magsize then
        local reload = Instance.new("Animation", currentweapon)
        reload.AnimationId = weaponmodule.reloadanim
        local reloadanim = currentweapon:WaitForChild("Humanoid"):LoadAnimation(reload)
        reloadanim:Play()
        weaponmodule.storedammo -= weaponmodule.magsize
        weaponmodule.currentammo = weaponmodule.magsize
        wait(weaponmodule.reloadtime)
    end

And we also need to make sure you can fire after the reload:

function reload()
    canfire = false
    if weaponmodule.storedammo >= weaponmodule.magsize and weaponmodule.currentammo < weaponmodule.magsize then
        local reload = Instance.new("Animation", currentweapon)
        reload.AnimationId = weaponmodule.reloadanim
        local reloadanim = currentweapon:WaitForChild("Humanoid"):LoadAnimation(reload)
        reloadanim:Play()
        weaponmodule.storedammo -= weaponmodule.magsize
        weaponmodule.currentammo = weaponmodule.magsize
        wait(weaponmodule.reloadtime)
        reloadanim:Stop() --we stop the animation if we can already shoot
        reload:Destroy() --not really good practice but its better than leaving it there
    end
    canfire = true --we add it here so if you cant reload you can still fire after pressing R
end

3. User Input Service
Finally we reached a new section of the tutorial, in this section we will make it so when you press the key R you will reload!
User Input Service
Because we have never used UIS (User Input Service) we will need to set it up i.e. get the service and use one of the three input states, you can read about user input service here.

First we will make a variable for it in the framework (you should have it, but still check):

local plr = game.Players.LocalPlayer
local char = plr.Character
local mouse = plr:GetMouse()
local cam = workspace.CurrentCamera
local runs = game:GetService("RunService")
local uis = game:GetService("UserInputService") --because its a service we need to get it with game:GetService()
local rs = game.ReplicatedStorage
local gunmodels = rs:WaitForChild("models")
local gunmodules = rs:WaitForChild("modules")
local resources = rs:WaitForChild("resources")

Getting The Input
Now we will use one of the user input service functions, we will use input began:

uis.InputBegan:Connect(function(input, gameprocessed)
    if not gameprocessed then --this checks if the -player is not typing in chat
        
    end
end)

Now we will check if the input is the key R with keycode:

uis.InputBegan:Connect(function(input, gameprocessed)
    if not gameprocessed then
        if input.KeyCode == Enum.KeyCode.R then
            reload()
        end
    end
end)

And that is it!!!
Now your gun should reload if your magazine is not full and not shoot if its empty.

4. Outro
In the next tutorial we will make our system more user friendly by adding ui and sounds as well as making walking animations running ect., basically making it beautiful.

Some useful links that were in this tutorial:
User Input Service
My Links
FPS Tutorial Part 1
FPS Tutorial Part 2
FPS Tutorial Part 3
The Uncopylocked Game
My discord if very needed: Stormtrooperhelmet#9491

Thank you very much for all of the likes on the previous ones and because you made it to the end here’s a cookie :wink:

12 Likes

I like this tutorial due to how simple the framework is compared to others. Good job!

2 Likes

Thank you very much, that’s what I am going for in all of my tutorials. I try to explain it as clearly as possible but also make it simple.

2 Likes

Also I found that you are creating animation object every time someone reloads. You should fix that.

yup fixed it here

    canfire = false
    if weaponmodule.storedammo >= weaponmodule.magsize and weaponmodule.currentammo < weaponmodule.magsize then
        local reload = Instance.new("Animation", currentweapon)
        reload.AnimationId = weaponmodule.reloadanim
        local reloadanim = currentweapon:WaitForChild("Humanoid"):LoadAnimation(reload)
        reloadanim:Play()
        weaponmodule.storedammo -= weaponmodule.magsize
        weaponmodule.currentammo = weaponmodule.magsize
        wait(weaponmodule.reloadtime)
        reloadanim:Stop() --we stop the animation if we can already shoot
        reload:Destroy() --not really good practice but its better than leaving it there
    end
    canfire = true --we add it here so if you cant reload you can still fire after pressing R
end

will a part 5 be out? I wanna make a sprint animation and stuff

I am thinking of reworking the entire fps tutorial and making a new one, these tutorials are old, unoptimized and badly designed, though I might still finish this one.

1 Like

Would love to see a new one.
Just glancing over theses as I was building a new FPS framework and was ever wondering if you we’re going to make a updated version.