Designing an FPS Framework: Beginner's guide

Ok this may be like the 40th FPS tutorial but I’m going to make this beginner friendly (hopefully). I will not be using OOP but I will still use module scripts

Why did you make a “beginner” tutorial instead of an advanced one?

When I first tried making an fps framework, I didn’t know what to look for and watch out for. I remade my fps framework 3 times to actually make it somewhat usable (it wasn’t great). This tutorial aims to teach you the concept and ideas of how to make one and in no way supposed to give you a polished fps framework.

Before continuing, you need to have some knowledge on:

  1. Roblox lua
  2. Module scripts
  3. CFrames
  4. Moon Animator or Roblox Animator ( or if you know how to animate in blender, use blender )

Objectives:

  1. Make a basic framework
  2. Make the gun shoot (duh)
  3. Make a projectile module

If you have any question you can ask in here

I’ll explain how to make bullet replication, hit detection and aiming in a part 2, tell me if you want a part 2 lol

( also for some reason the videos doesn’t load sometimes )

Step 1: Preparation

To make an FPS game you will need a viewmodel or fake arms.

Here is the viewmodel I used: viewmodel.rbxm (5.2 KB)

image

First we’re gonna Motor6D the viewmodel. We’re basically creating bones for the viewmodel or welding the parts / limbs.

If your viewmodel has accessories just Motor6D the accessory to the arm (or to whatever the accessory is resting on). Now Motor6D both arms to the HumanoidRootPart

(Part0 = HumanoidRootPart, Part1 = RightArm, same goes for the left arm).

Last thing, make a Motor6D called ‘Handle’ that is where we will weld the gun to the arm.
(Part0 = HumanoidRootPart, Part 1 = nil)

Your viewmodel needs to have a CameraBone (basically just another HRP), then Motor6D the HumanoidRootPart to the CameraBone .

(Part0 = CameraBone, Part1 = HumanoidRootPart)

Now your viewmodel hierarchy should look something like this

image

(also if your viewmodel has a humanoid, delete it, we’re going to load animations use AnimationController)

After your done put the Viewmodel inside ReplicatedStorage, lets talk about the gun

Here is the gun I used: groza.rbxm (8.7 KB)

You can have any gun you want, the less part the better. We’re not going to Motor6D the gun yet, we will handle that later.

We’re going to make the gun components (aka parts that are going to be used in scripts) You must have the barrel, handle, sight. Put all of those in a folder called “GunComponents”

After your done, put the gun in ReplicatedStorage (oh yeah the ‘Sight’ part does nothing because I’m not going to teach you how to aim in this post, maybe later).

Step 2: Positioning the viewmodel

First lets make a LocalHandler, you can put the script in StarterPlayerScripts. Now make a module scripts in ReplicatedStorage, I’m going to name this module script… ‘MainModule’ very creative, I know. Although we’re going to put a lot unrelated functions, I’m just going to name it MainModule so I don’t confuse you guys. :man_shrugging:

Alright now lets make so the viewmodel attachees to the camera. How we are going to do this is we’re going to position the viewmodel to the camera every frame.

First open the LocalHandler and parent the Viewmodel to the Camera. So it’s easier to find the viewmodel.

If you test it now nothing will happen, now open your MainModule and make this function:

image

This will position the viewmodel’s HumanoidRootPart to the Camera’s CFrame. Then we will want it to update it every frame, Just attach this function inside a RenderStepped!

Now it will update every frame! Now you should have something like this:

eyJhbGciOiJIUzI1NiJ9.eyJpbWciOiJfMzZiNGUwMzI3NDRhNDY1Y2JmZWRjYjM1Nzc5N2NhMTYifQ.4XA6DcteZCkYBJ-X6lNv2y2C4NdLgqttbVY78mh20g8-gif

Step 3: Animating and positioning the gun

Before we position and animate the gun, do you remember when I said this?

We’re going to do that real quick!

Alright lets make a function for welding guns, I’m gonna call it module.weldgun(gun). The concept is that we will pass a gun object, and get the parts and weld it all to the handle.

image
(VERY VERY important note: make sure you add if v ~= Main then or else when animating it will crash moon animator)

Alright we have our weldgun function now lets call it in the LocalHandler
image

Now we have that out of the way, first step is to make it so the gun welds to the viewmodel. Let’s make an module.equip(viewmodel, gun) function.

image

We’re basically setting the Motor6D to the gun’s handle. Now the gun is in your face! no!!

Time for some animating! First we need to get the viewmodel with the gun so first play the game and then go to Workspace > Camera > Viewmodel. Make sure the gun is also inside the viewmodel, then copy the viewmodel, then stop playing and paste. (make sure the gun is oriented correctly!! if not check the the gun’s handle orientation). Now you should have something like this

We need to rotate the viewmodel correctly first before animating, so just select the HumanoidRootPart and set its position to 0, 0, 0 (optional) and the rotation to 0, 0, 0

image

Voila!! Lets get animating. Now we want to make a hold animation. Just put the viewmodel inside moon animator and start animating! In the end you should have something like this

Make sure it’s looped and when your done, publish the animation!

Alright now you have your holding animations, lets actually play the animation, but before we do that. Lets make a folder containing the gun’s animations. Inside we put the holding animation, I just named it “Hold”.

image

Alright! Now lets edit the module.equip(viewmodel, gun) function. We’re gonna add a new parameter called ‘hold’.

image

anddd in the LocalHandler

image

Lets give it a try

andddd

eyJhbGciOiJIUzI1NiJ9.eyJpbWciOiJfMGUyNGYyMGMwMzdjYjllYzE3NTQ2OTJjNTU3NzA2YmUifQ.-J7kzVsk0Ia7urRXI-j6yXLv-81RB0DHY81QiG8RJHs-gif

Voila!! The animation works! If it dosen’t work for you check the animation and the Motor6D and also make sure the Animation object is valid.

Step 4: Projectile module

I’m just going to make a basic projectile module, you can skip this step if you know how to use FastCast

Alright so in the MainModule let’s make a new function called module.cast(). The idea is that it will create a bullet and then position it self to the barrel facing to the mouse position then making the bullet go forward.

First step: Making the bullet go to the barrel

Later we will pass 3 information, the gun, the end position and the velocity of the bullet or studs per frame

image

Second step: Making the bullet go forward

We will use a RenderStepped to make the bullet travel smoothly

image

We’re multiplying the -velocity with dt is because the bullet velocity will adjust to frame drops or when framerate goes higher or lower

Third step: Destroying the bullet when it goes too far

image

Step 5: Making the gun shoot

We’re gonna write some code that detects if the player is holding the mouse or not

image

Now lets check if the player is holding or not, if if the player is holding then fire the gun.

andddd

eyJhbGciOiJIUzI1NiJ9.eyJpbWciOiJfM2U1NjQwMTdmM2I3NGE2NzU1MzI2ZDgyMWFmMTY0NWQifQ.A3CsPboxngMcN-tLFfLPDwtaCI9ahTvHgkFyKPYLvtw-gif

We’re finished! The gun dosen’t do much… but I’ll continue this tutorial if people think I’m worthy enough to continue this. 20 hearts and I’ll do a part 2! If it there was a part 2, I would probably teach you how to make recoil, bullet replication and hit detection (let me know what gun feature do you want). Part 2 is out now!

Code:

LocalHandler:

MainModule:

(Part 2 coming tomorrow!!)
edit: part 2 might be delayed sorry!!
Part 2 is here (finally)!!

If you have any questions you can join the discord server

412 Likes

Best tutorial ever! Keep going man.

33 Likes

You should use Code Bricks, they look like this: ```

Start and end the string with: ```

Much better, right?
local YouShouldUseThis = true

image

30 Likes

1 more like for part 2, can’t wait.

8 Likes

Now I gave the 20th like, time for part 2 :sunglasses:

7 Likes

I’m not sure if it’s something I wrote wrong or what, but the blank arms are facing the wrong way, what would I do to fix this? I’ve tried just changing the direction of the model in studio it doesn’t work

5 Likes

Try and check if the HumanoidRootPart of the viewmodel is facing in the right direction, to do this just insert a decal and select it, then observe if the orange outline is facing in the right direction.

image

This is how it should look
If your still having trouble here is the viewmodel I used: viewmodel.rbxm (5.2 KB)

13 Likes

So far very good, im at this point in the code, and its giving the errorbelow and not putting the gun in the viewmodel, any idea whats happening?

 Handle is not a valid member of MeshPart "Workspace.Camera.Viewmodel.HumanoidRootPart" 
 --  Client - MainModule:23

code-
modulescript

local module = {}
function module.update(viewmodel, dt)
	viewmodel.HumanoidRootPart.CFrame = game.Workspace.Camera.CFrame
end

function module.weldgun(gun)
	local Main = gun.Components.Handle
	
	for i, v in ipairs(gun:GetDescendants()) do
		if v:IsA("BasePart") and v ~= Main then
			local newMotor = Instance.new("Motor6D")
			newMotor.Name = v.Name
			newMotor.Part0 = Main
			newMotor.Part1 = v
			newMotor.C0 = newMotor.Part0.CFrame:inverse() * newMotor.Part1.CFrame
			newMotor.Parent = Main
		end
	end
end

function module.equip(viewmodel, gun)
	local gunHandle = gun.Components.Handle
	local HRP_Motor6D = viewmodel:WaitForChild("HumanoidRootPart").Handle
	
	gun.Parent = viewmodel
	HRP_Motor6D.Part1 = gunHandle
end
return module

local script-

local GunModel = game.ReplicatedStorage:WaitForChild("MP5")
local Viewmodel = game.ReplicatedStorage:WaitForChild("Viewmodel")

local mainModule = require(game.ReplicatedStorage.MainModule)

Viewmodel.Parent = game.Workspace.Camera

game:GetService("RunService").RenderStepped:Connect(function(dt)
	print("running")
	mainModule.update(Viewmodel, dt)
	mainModule.weldgun(GunModel)
	mainModule.equip(Viewmodel, GunModel)
	print("finished running")
end)
6 Likes

Make sure your viewmodel’s HRP has an empty Motor6D called ‘Handle’

image

Part 0 = HumanoidRootPart
Part 1 = nil

13 Likes

For making this into a tool and making multiple guns, would it just be as simple and renaming the handler and module script to the gunname+handler/module and turning them off an on depending on if the player has equipped the tool?

6 Likes

Module scripts shouldn’t be replicated for a single tool, they should be kept in ReplicatedStorage. Turning this system into a tool based system isn’t easy. I’d also recommend you just use only 1 Handler. With this you have to constantly check what gun is the player holding and if they equipped the tool. If you want an easier way to implement it I’d just recommend you look at other Community Tutorials because it can get a bit complex when trying to do this but it is very do-able.

8 Likes

Well part 2 is finally out, check it out here. :wink:

6 Likes

Oh god, I didn’t notice this:

image

You only equip and weld the gun once, you don’t need to do it for every frame. So instead it should be like this:

mainModule.weldgun(GunModel)
mainModule.equip(Viewmodel, GunModel)

game:GetService("RunService").RenderStepped:Connect(function(dt)
	mainModule.update(Viewmodel, dt)
end)

I apologize for not noticing!

5 Likes

Dont worry about it, I ended up looking at the end of your scripts and realizing my mistake

3 Likes

So I got an error in the module script, I don’t know why, here is the error:

Screenshot 2021-05-04 143500

Here is the module script so far:



local MainModule = {}

function MainModule.Update(viewmodel, dt)
	viewmodel.HumanoidRootPart.CFrame = workspace.Camera.CFrame
end

function MainModule.weldgun(gun)
	local Main = gun.GunComponets.Handle
	
	for i,v in ipairs(gun:GetDescendants()) do
		if v:IsA("BasePart") and v ~= Main then
			local newMotor = Instance.new("Motor6D")
			newMotor.Name = v.Name
			newMotor.Part0 = Main
			newMotor.Part1 = v
			newMotor.C0 = newMotor.Part0.CFrame:inverse() * newMotor.Part1.CFrame
			newMotor.Parent = Main
		end
	end
end

function MainModule.equip(viewmodel, gun, hold)
	local GunHandle = gun.GunComponets.Handle
	local HRP_Motor6D = viewmodel:WaitForChild("HumanoidRootPart").Handle
	
	gun.Parent = viewmodel
	HRP_Motor6D.Part1 = GunHandle
	
	local Hold = viewmodel.AnimationController:LoadAnimation(hold)
	Hold:Play()
end

function MainModule.cast(gun,endposition,velocity)
	local GunBarrel = gun.GunComponets.Barrel
	
	local Bullet = Instance.new("Part")
	Bullet.Size = Vector3.new(1,1,5)
	Bullet.Anchored = true
	Bullet.CanCollide = false
	Bullet.Color = Color3.fromRGB(255,255,255)
	Bullet.Material = Enum.Material.Neon
	Bullet.Parent = workspace
	
	Bullet.CFrame = CFrame.new(GunBarrel.Position, endposition)
	
	local Loop
	
	Loop = game:GetService("RunService").RenderStepped:Connect(function(dt)
		Bullet.CFrame *= CFrame.new(0,0 -velocity * dt)
		if (Bullet.Position - GunBarrel.Position).magnitude > 1000 then
			Bullet:Destroy()
			Loop:Disconnect()
		end
	end)
end


return MainModule

Here is the line of code that the error goes to:

Bullet.CFrame = CFrame.new(GunBarrel.Position, endposition)

I don’t know why it’s like this, it’s exactly the same in your code.

2 Likes

Hey! Sorry for not responding (i was at school), but make sure your “endposition” is a Vector3.

4 Likes

I guess we live in very different time zones, but I fixed that issue, but the bullet doesn’t spawn at the current position of the barrel, I don’t know why, it doesn’t even spawn at the barrel, it just spawns in the middle of your screen.

2 Likes

Can you make sure that the barrel is in the correct position? If it spawns in the middle of the space that means the barrel is in an incorrect position.

2 Likes

Here is the link to my the topic I made just to figure out this issue: topic

Here is an image of the barrel’s position:

Screenshot 2021-05-05 084841
You can barely see the barrel, and still it’s not in the center of the screen. I made a video of shooting the gun, but the file was too big somehow.

Also I noticed that part 2 doesn’t have aiming and reloading, a part 3 would be amazing.

This is where the bullet spawns also:
Screenshot 2021-05-05 091320

If possible, could you provide an rbxm file of the module script for this part?

2 Likes

A video would be much better for me to understand but for now all I could recommend is to print() the barrel’s position and the camera’s position (since you said the bullet were spawning in the middle of your screen). And also try doing this after the CFrame.new(barrel.Position, endposition):
Bullet.Position = barrel.Position

Regarding about aiming in reloading, I already gave a statement about aiming here and I won’t be doing a reloading tutorial since it’s very simple.

Update: ok so mid way writing this you editted your post and it looks like the bullet is spawning at 0, 0, 0 (correct me if im wrong). but still try the possible solution above

5 Likes