Writing an FPS framework (2020)

Wdym copied rig?, I’ve tried animating with the default Animation editor as I followed these tutorials: How to animate a tool/object with a Dummy in the Animation Editor - Resources / Community Tutorials - DevForum | Roblox

How to animate Tool Parts (Guns, Knifes etc.) - Resources / Community Tutorials - DevForum | Roblox

Also, what am I not supposed to do myself?. In these tutorials I did the Motor6d stuff then straight closed the Animation editor and started scripting. I didn’t actually export it if I’m sure.

how can i make an ammo count gui for this

1 Like


I recommend you to get a basic knowledge of scripting in LuaU first before making a fps framework…

Though you can just copy paste the code and call it a day it won’t increase your knowledge and it would make it difficult for you to add something on this framework(Ammo count UI in this case).

1 Like

what’s the difference between luau and normal lua?
I’m just having trouble reading through to find where I can get the ammo count so i can display it on the gui

how did you get a custom explorer?
Also, gr8 tutorial

1 Like

i want to ask how i would add on more offsets to the system. i want a position for sprinting and added a cframevalue based on idle.
in update i did this:

local sprintOffset = equipOffset:Lerp(self.viewmodel.offsets.sprint.Value, self.lerpValues.sprint.Value)

in initialization:

self.lerpValues.sprint = Instance.new("NumberValue") self.lerpValues.sprint.Value = 1

and everytime i sprint:

local lowerWeapon = tweenService:Create(self.lerpValues.sprint, TweenInfo.new(0.8, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), {Value = 0})

but for some reason it’s not lowering the weapon, not sure what im doing wrong, if you can help then it would be appreciated

its after all the default tweens that came with your code. and im just copying what you did for the equip offset

For people who are reading this tutorial,
I have a question

Is the rootPart the same place with the other parts like the Viewmodel arms and guns for you?

Well, as for me this is how it looks like,


Edit: I just want to know if i am doing this right

set the anchor on the rootpart to false

1 Like

can anyone tell me how to get the current ammo count

  13:53:09.111  ReplicatedStorage.modules.fps:242: 'Value' is not a member of NumberSequence  -  Client - fps:242
  13:53:09.112  Stack Begin  -  Studio
  13:53:09.112  Script 'ReplicatedStorage.modules.fps', Line 242 - function fire  -  Studio - fps:242
  13:53:09.113  Script 'ReplicatedStorage.modules.fps', Line 272 - function fire  -  Studio - fps:272
  13:53:09.113  Script 'Workspace.DRIFT_NINJA06.InputController', Line 104  -  Studio - InputController:104
  13:53:09.113  Script 'ReplicatedStorage.modules.Velocity.Services.InputService', Line 16 - function on  -  Studio - InputService:16

That is the error I get whenever I click the LMB.
Someone pls help me out here

All good now,
Just a spelling problem.

Hey There! I am in the middle of your tutorial (End of part 7, to be exact) and am having trouble getting to have the gun ‘implanted straight in my face’. When I play, the gun spawns on the ground where I was configuring it, as well as the viewmodel. Why?

I have no way to know without your code. If it is not moving, you can safely assume the CFrame of the rootPart is not getting set, which means either the :update() function isn’t running, or an if statement doesn’t pass

edit: (that, or the equip function fails somewhere)

Alright I have checked everything but it still seems that it is not working. I believe it is an issue with my rigging but I am not sure. Here is everything



FPS ModuleScript:

-- This is game.replicatedstorage.fps 

-- Coolio module stuff
local handler = {}
local fpsMT = {__index = handler}	

local replicatedStorage = game:GetService("ReplicatedStorage")
local spring = require(replicatedStorage.Modules.spring)

-- Functions i like using and you will probably too.

-- Bobbing!
local function getBobbing(addition,speed,modifier)
	return math.sin(tick()*addition*speed)*modifier

-- Cool for lerping numbers (useful literally everywhere)
local function lerpNumber(a, b, t)
	return a + (b - a) * t

function handler.new(weapons)
	local self = {}

	return setmetatable(self,fpsMT)

function handler:equip(wepName)

	-- Explained how this works earlier. we can store variables too!
	-- if the weapon is disabled, or equipped, remove it instead.
	if self.disabled then return end
	if self.equipped then self:remove() end
	print("removed old gun")

	-- get weapon from storage
	local weapon = replicatedStorage.Weapons:FindFirstChild(wepName) -- do not cloen 
	if not weapon then return end -- if the weapon exists, clone it, else, stop
	weapon = weapon:Clone()
	print("weapon has been cloned")

	 	Make a viewmodel (easily accessible with weapon.viewmodel too!) 
		and throw everything in the weapon straight inside of it. This makes animation hierarchy work.
	self.viewmodel = replicatedStorage.viewmodel:Clone()
	print("viewmodel has been cloned")
	for i,v in pairs(weapon:GetChildren()) do
		v.Parent = self.viewmodel
		if v:IsA("BasePart") then
			v.CanCollide = false
			v.CastShadow = false

	-- Time for automatic rigging and some basic properties
	self.camera = workspace.CurrentCamera
	self.character = game.Players.LocalPlayer.Character

	-- Throw the viewmodel under the map. It will go back to the camera the next render frame once we get to moving it.
	self.viewmodel.rootPart.CFrame = CFrame.new(0,-100,0)
	print("set viewmodel rootpart cframe")
	-- We're making the gun bound to the viewmodel's rootpart, and making the arms move along with the viewmodel using hierarchy.
	self.viewmodel.rootPart.weapon.Part1 = self.viewmodel.WeaponRootPart
	self.viewmodel.left.leftHand.Part0 = self.viewmodel.WeaponRootPart
	self.viewmodel.right.rightHand.Part0 = self.viewmodel.WeaponRootPart
	print("set all part motor6d stuff")
	-- I legit forgot to do this in the first code revision.
	self.viewmodel.Parent = workspace.Camera
	print("viewmodels parent is the workspaces' camera")

		Real life example:
		self.loadedAnimations.idle = self.viewmodel.AnimationController:LoadAnimation(self.settings.anims.viewmodel.idle)

	self.equipped = true -- Yay! our gun is ready.

function handler:remove()

	-- Not much to see here yet. even the real life function for removing a weapon takes like 30 lines ***on the client***.
	self.viewmodel = nil
	self.equipped = false -- Nay! your gun is gone.


function handler:aim(toaim)

	-- we'll be using this soon


function handler:update(deltaTime)

	-- IF we have a gun right now. We're checking the viewmodel instead for "reasons".
	if self.viewmodel then
		print("There is a viewmodel")
		self.viewmodel.rootPart.CFrame = self.camera.CFrame
		print("The viewmodel's rootpart's cframe is the camera's cframe")

	Used in my real game for framerate counting. I kept it in for fun.
	(decomissioned since like 3 months ago, don't use this)

		local fps = 0
			fps = fps + 1
		while wait(1) do
			local gui = game.Players.LocalPlayer.PlayerGui:FindFirstChild("wepGui")
			if gui then
				gui.FPScount.Text = string.format("FPS: %s",fps)
			fps = 0
return handler


-- input controller

-- The fps module we're about to add
local weaponHandler = require(game.ReplicatedStorage.Modules.fps)
print("got the wep handler")

--Custom input service. Do this how you want, i just couldn't ever remember how to use other services consistently.
local velocity = require(game.ReplicatedStorage.Modules.Velocity):Init(true)
print("got velocity and have inited")
local inputs = velocity:GetService("InputService")
print("got inputservice")

--Current weapon and translating binds from integer to enum. You don't need to understand that.
local curWeapon = nil
print("set var curWeapon")
local enumBinds = {
	[1] = "One";
	[2] = "Two";
	[3] = "Three";
print("set var enumBinds")

-- Server security. We don't need this right now, so i'll just comment it.
-- local weps,primaryAmmo,secondaryAmmo = game.ReplicatedStorage.weaponRemotes.New:InvokeServer()


	This means if you add a second weapon it'll automatically go under the bind 2,3, etc. 
	This is a bad thing, at least for later stages. The above commented line shows that we 
	should get the weapons from the server on-spawned. Here's how it looks in my game:
	local defaultWeapons = {
		[1] = "UMP45";
	--	[2] = "JESUS"; lol railgun
local weps = game.ReplicatedStorage.Weapons:GetChildren() 
print("got weps")
local weapon = weaponHandler.new(weps)
print("got the weapon")

-- clearing viewmodels we could have kept in the camera because of script errors and stuff
local viewmodels = workspace.Camera:GetChildren()
print("got viewmodels from workspace camera")
for i,v in pairs(viewmodels) do
	if v.Name == "viewmodel" then
		print("destroyed old viewmodel")

-- equip code
for i,v in pairs(weps) do

	-- cooldown for spammy bois
	local working

	-- we will bind this per-weapon
	local function equip()

		-- if cooldown active, then don't execute the function. for less experienced scripters, this is just the equivalent of:
			local function brug()
				if working == false then
					-- do stuff

		if working then return end 

		working = true

		-- if the current equipped weapon is different from the one we want right now (also applies to the weapon being nil)
		if curWeapon ~= v then

			if weapon.equipped then
			print("equipped weapon")
			curWeapon = v
			print("curWeapon is: "..tostring(curWeapon))

			-- if it's the same, just remove it

			curWeapon = nil


		working = false

	-- This means you can have 3 different weapons at once.
	inputs.BindOnBegan(nil,enumBinds[i],equip,"Equip : "..i)
	print("on input began")

local function update(dt)

-- marking the gun as unequippable
game.Players.LocalPlayer.Character:WaitForChild("Humanoid").Died:Connect(function() weapon:remove() weapon.disabled = true end)

Still, no errors.
If you need anything else let me know.

EDIT: All my folders and scripts are PascalCase, so ignore that.

Hi! (not sure if this thread is active but might as well try)
Im currently at the part about making it aim, and im having a bit of an issue, I have the function aim running through some print statements, and its finishing them, but not weening the correct values, im not sure why
and yes I did go in the game and edit the values with the command bar

function handler:aim(toaim)
	if self.disabled then return end
	if not self.equipped then return end

	self.aiming = toaim
	game:GetService("UserInputService").MouseIconEnabled = not toaim 

	if toaim then
		local tweeningInformation = TweenInfo.new(1, Enum.EasingStyle.Quart,Enum.EasingDirection.Out)
		tweenService:Create(self.lerpValues.aim, tweeningInformation, { Value = 1 }):Play()			

		local tweeningInformation = TweenInfo.new(0.5, Enum.EasingStyle.Quart,Enum.EasingDirection.Out)
		tweenService:Create(self.lerpValues.aim, tweeningInformation, { Value = 0 }):Play()			

weaponRootPart is not a valid member of Model “viewmodel” Not sure what I did wrong

screw this. this stuff doesn’t even work, and there’s some steps that aren’t specified on. Spent all day trying to fix my gun, and checked everything.

Don’t blame the tutorial for user mistake.

It works perfectly for me, pay attention to the tutorial and you’ll eventually find out what you did wrong.

1 Like