Designing an FPS Framework: Beginner’s guide [PART 2]

Ah well that’s good and just to mention a better method to that is

if not game:IsLoaded() then
    game.Loaded:Wait()
end

(fyi you put the waitforchild on the humanoirdrootpart)

1 Like

Ive ran into a issue with 0 errors (sorry for asking for help 3 diffrent times lol) basically ive gotten to the damage part and i cant seem to make it work im pretty sure that i did everything right unless its just another spelling mistake.

Heres all the scripts:

MainModule
local module = {}

if not game.Loaded then
game.Loaded:Wait()
end

local function GetBobbing(addition)
return math.sin(tick() * addition * 1.3) * 0.5
end

function module.update(viewmodel, dt, recoilspring, bobbleswaying, swayingspring)
viewmodel.HumanoidRootPart.CFrame = game.Workspace.Camera.CFrame

local bobble = Vector3.new(GetBobbing(10), GetBobbing(5),GetBobbing(5))
local mousedelta = game:GetService("UserInputService"):GetMouseDelta()

local character = game.Players.LocalPlayer.Character or game.Players.LocalPlayer.CharacterAdded:Wait()

bobbleswaying:shove(bobble / 10 * (character:WaitForChild("HumanoidRootPart").Velocity.Magnitude) / 10)
swayingspring:shove(Vector3.new(-mousedelta.X / 500, mousedelta.Y / 200, 0))

local updatedrecoilspring = recoilspring:update(dt)
local updatedbobblespring = bobbleswaying:update(dt)
local updatedswayingspring = swayingspring:update(dt)

viewmodel.HumanoidRootPart.CFrame = viewmodel.HumanoidRootPart.CFrame:ToWorldSpace(CFrame.new(updatedbobblespring.Y,updatedbobblespring.X,0))
viewmodel.HumanoidRootPart.CFrame *= CFrame.new(updatedswayingspring.X,updatedswayingspring.Y,0)

viewmodel.HumanoidRootPart.CFrame *= CFrame.Angles(math.rad(updatedrecoilspring.X) * 2,0,0)
game.Workspace.Camera.CFrame *= CFrame.Angles(math.rad(updatedrecoilspring.X),math.rad(updatedrecoilspring.Y),math.rad(updatedrecoilspring.Z))
end
--Weld gun or something

function module.weldgun(gun)
local main = gun.Components.Handle
for i, v in pairs(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, hold)
local gunhandle = gun.Components.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 module.cast(gun, endposition, velocity, damage)
local gunbarrel = gun.Components.Barrel
local bullet = Instance.new("Part")

bullet.Size = Vector3.new(0.523,0.1,0.1	)
bullet.Anchored = true
bullet.CanCollide = false
bullet.Color = Color3.new(1, 1, 0)
bullet.Material = Enum.Material.Neon
bullet.Parent = game.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 * 60))
	
	local hit = workspace:Raycast(bullet.Position, bullet.CFrame.LookVector * velocity * 1.5)
	if hit then
		if hit.Instance.Parent:FindFirstChild("Humanoid") then
			
			damage:FireServer(hit.Instance.Parent,10)

		else

			--WALL

		end
	end

	if (bullet.Position - gunbarrel.Position).magnitude > 5000 then
		bullet:Destroy()
		loop:Disconnect()
	end
end)

end




return module
LocalHandler
local gunmodel = game.ReplicatedStorage:WaitForChild("MiniGun")
local AnimationsFolder = game.ReplicatedStorage:WaitForChild("MiniGunAnimations")
local viewportmodel = game.ReplicatedStorage:WaitForChild("Viewmodel")
local ts = game:GetService("TweenService")
local firing = viewportmodel.AnimationController:LoadAnimation(AnimationsFolder.Firing)
local springmodule = require(game.ReplicatedStorage.SpringModule)
local recoilspring = springmodule.new()
local bobbleswaying = springmodule.new()
local swayspring = springmodule.new()
local Damage = game.ReplicatedStorage.ToolEvents:WaitForChild("MiniGunDamage")
-- mainmodule idk

local mainmodule = require(game.ReplicatedStorage.GunSystemMainModule)

viewportmodel.Parent = game.Workspace.Camera

--position viewport model

game:GetService("RunService").RenderStepped:Connect(function(dt)
mainmodule.update(viewportmodel,dt, recoilspring, bobbleswaying, swayspring)
end)

--calls the weldgun thing
mainmodule.weldgun(gunmodel)
--equip thing
mainmodule.equip(viewportmodel,gunmodel, AnimationsFolder.Hold)
local IsPlayerHoldingMouse
local CanFire = true
local Delay_ = 0.1

game:GetService("RunService").Heartbeat:Connect(function(dt)
if IsPlayerHoldingMouse then
	if CanFire then
		CanFire = false
		recoilspring:shove(Vector3.new(2,math.random(-2,2),10))
		
		coroutine.wrap(function()
			for i, v in pairs(gunmodel.Components.Barrel:GetChildren()) do
				if v:IsA("ParticleEmitter") then
					v:Emit()
				end
			end
			
			local firesound = gunmodel.Components.Sounds.MiniGunFiring:Clone()

			firesound.Parent = game.Workspace
			firesound.Parent = nil
			firesound:Destroy()
		end)()


		coroutine.wrap(function()
			wait(0.2)
			recoilspring:shove(Vector3.new(-2.8,math.random(-1,1),-10))
		end)()
		
		 
  mainmodule.cast(gunmodel,game.Players.LocalPlayer:GetMouse().Hit.Position,5,Damage)
		recoilspring:shove(Vector3.new(1,0,0))
		mainmodule.cast(gunmodel, game.Players.LocalPlayer:GetMouse().Hit.Position,60)
		wait(Delay_)
		CanFire = true
	end
end

end)

game:GetService("UserInputService").InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
	IsPlayerHoldingMouse = true
	firing:Play()
end
end)
game:GetService("UserInputService").InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
	IsPlayerHoldingMouse = false
	firing:Stop()
end
  end)
DamageScript
local damage = game.ReplicatedStorage.ToolEvents:WaitForChild("MiniGunDamage")

damage.OnServerEvent:Connect(function(client,player,damage)
if player then
	player:FindFirstChild("Humanoid"):TakeDamage(damage)
end
end)

(Again sorry for asking for help again im not that great at scripting lol)

Script Editor colors, now, or else… :crazy_face:

1 Like

Hey, I’ve been having an issue. Unless the game is paused (with F5) the bullets come out of where the mouse is. I don’t know what’s causing this but, here’s my scripts I guess.

LocalHandler:

local Viewmodel = game.ReplicatedStorage:WaitForChild("Viewmodel")
local Animations = game.ReplicatedStorage.AnimationFolder

local MainModule = require(game.ReplicatedStorage.MainModule)
local SpringModule = require(game.ReplicatedStorage.SpringModule)

local IsPlayerHoldingMouse
local CanFire = true
local Delay = 0.1

Viewmodel.Parent = game.Workspace.Camera
MainModule.weldgun(GunModel)

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

MainModule.equip(Viewmodel, GunModel, Animations.HK_Hold)

game:GetService("RunService").Heartbeat:Connect(function(dt)
	
	if IsPlayerHoldingMouse then
		
		if CanFire then
			
			CanFire = false
			
			MainModule.cast(GunModel, game.Players.LocalPlayer:GetMouse().Hit.Position, 60)
			
			wait(Delay)
			CanFire = true
			
		end
		
	end
	
end)

game:GetService("UserInputService").InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		IsPlayerHoldingMouse = true
	end
end)

game:GetService("UserInputService").InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		IsPlayerHoldingMouse = false
	end
end)

MainModule

local module = {}

function module.update(viewmodel, dt)
	viewmodel.RootPart.CFrame = game.Workspace.Camera.CFrame
end

function module.weldgun(gun)
	
	local Main = gun.GunComponents.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, hold)
	
	local GunHandle = gun.GunComponents.Handle
	local HRP_Motor6D = viewmodel:WaitForChild("RootPart").Handle
	
	gun.Parent = viewmodel
	HRP_Motor6D.Part1 = GunHandle
	
	local Hold = viewmodel.AnimationController:LoadAnimation(hold)
	Hold:Play()
	
end

function module.cast(gun, endposition, velocity)
	
	local gunbarrel = gun.GunComponents.Barrel
	
	local bullet = Instance.new("Part")
	bullet.Size = Vector3.new(1,1,5)
	bullet.Anchored = true
	bullet.CanCollide = false
	bullet.Color = Color3.new(1, 1, 0)
	bullet.Material = Enum.Material.Neon
	bullet.Parent = game.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 * 60))

		if (bullet.Position - gunbarrel.Position).magnitude > 5000 then
			bullet:Destroy()
			loop:Disconnect()
		end
		
	end)

end


return module

I haven’t done anything in this post yet, I was going to reply on Part 1 but that would necrobump it. Not cool.

Note: Upon actually looking at the barrels position in the camera, the Y is at like 1000. Which is really weird.

1 Like

Sorry for responding late. I think the problem resides here.
image

1 Like

No problem!

When i try to change Damage to lowercase it says unknown global and gives me this error
image
Unless thats not the problem?
(Ive never used ray cast so this is all really new to me lol but i really want to make this)

Sorry about the confusion, the first .cast is correct, I’m mentioning the second .cast where you forgot to place the Damage variable.
image

1 Like

@EXM_0 Im sorry for asking another question but the topic i made got flagged how would i make this into a tool?

Would i put the view model into rep store when the tool is not equipped and when its equipped put it into the camera? Would that work?

Yup you could do something like that, although this tutorial isn’t really made for tool you could edit it to make it tool compatible, but basically you could make a Disabled variable that disables everything upon being set to true. When the tool gets equipped the VM will get parented back, and when it is unequipped, set the disabled variable to true and store the VM. Although it is guaranteed that you will encounter other problems that I haven’t mention here (since I’ve never converted an FPS system to a tool based one).

2 Likes

@ProxyBuild
I tried to do this as a test to see if it would work i put the localhandler in a tool and changed the script to this

local gunmodel = game.ReplicatedStorage.GunSystems.Guns:WaitForChild("MiniGun")
local AnimationsFolder = game.ReplicatedStorage.GunSystems.Animations:WaitForChild("MiniGunAnimations")
local viewportmodel = game.ReplicatedStorage:WaitForChild("Viewmodel")
local ts = game:GetService("TweenService")
local firing = viewportmodel.AnimationController:LoadAnimation(AnimationsFolder.Firing)
local springmodule = require(game.ReplicatedStorage.SpringModule)
local recoilspring = springmodule.new()
local bobbleswaying = springmodule.new()
local swayspring = springmodule.new()
local Damage = game.ReplicatedStorage.GunSystems.Events:WaitForChild("MiniGunDamage")
local fire = game.ReplicatedStorage.GunSystems.Events:WaitForChild("MiniGunFire")
local ammo = game.ReplicatedStorage.GunSystems.Ammo:WaitForChild("MiniGunAmmo").Value
local tool = script.Parent
-- mainmodule idk

local mainmodule = 
require(game.ReplicatedStorage.GunSystems.Scripts.MiniGunSystemMainModule)

tool.Equipped:Connect(function()
viewportmodel.Parent = game.Workspace.Camera

--position viewport model

game:GetService("RunService").RenderStepped:Connect(function(dt)
mainmodule.update(viewportmodel,dt, recoilspring, bobbleswaying, swayspring)
end)

--calls the weldgun thing
mainmodule.weldgun(gunmodel)
 --equip thing			
 mainmodule.equip(viewportmodel,gunmodel, AnimationsFolder.Hold)
 local IsPlayerHoldingMouse
 local CanFire = true
 local Delay_ = 0

game:GetService("RunService").Heartbeat:Connect(function(dt)
if IsPlayerHoldingMouse then
	if CanFire and ammo > 0 then
		ammo -= 1
		CanFire = false
		recoilspring:shove(Vector3.new(2,math.random(-2,2),10))
		
		coroutine.wrap(function()
			for i, v in pairs(gunmodel.Components.Barrel:GetChildren()) do
				if v:IsA("ParticleEmitter") then
					v:Emit()
				end
			end
			
			local firesound = gunmodel.Components.Sounds.MiniGunFiring:Clone()

			firesound.Parent = game.Workspace
			firesound.Parent = nil
			firesound:Destroy()
		end)()


		coroutine.wrap(function()
			wait(0.2)
			recoilspring:shove(Vector3.new(-2.8,math.random(-1,1),-10))
		end)()
		
		 mainmodule.cast(gunmodel.Components.Barrel.Position,game.Players.LocalPlayer:GetMouse().Hit.Position,5,Damage)
		recoilspring:shove(Vector3.new(1,0,0))
		mainmodule.cast(gunmodel.Components.Barrel.Position, 
game.Players.LocalPlayer:GetMouse().Hit.Position,60,Damage)
		wait(Delay_)
		CanFire = true
	end
end
end)



game:GetService("UserInputService").InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
	IsPlayerHoldingMouse = true
	firing:Play()
elseif input.KeyCode == Enum.KeyCode.R then
	local reloadsound = gunmodel.Components.Sounds.MiniGunReload
	reloadsound:Play()
	firing:Stop()
	task.wait(5)
	ammo = mainmodule.reload()
	
end
end)



game:GetService("UserInputService").InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
	IsPlayerHoldingMouse = false
	firing:Stop()
end
end)


fire.OnClientEvent:Connect(function(client,origin,endposition)
if client ~= game.Players.LocalPlayer then
	mainmodule.cast(origin,endposition,5,nil)
end
end)
end)

tool.Unequipped:Connect(function()
viewportmodel.Parent = game.ReplicatedStorage
end)

Nothing happend when i unequipped/equipped.

try making a event so when they click the play button it start a remote function and creates a weapon in their camera once they spawn in the game

1 Like

You need to come up with a loadout system but basically you just need to set the Handle Motor6D’s Part1 to nil, store the current gun, then set the Handle Motor6D’s to the second gun and parent it to the viewmodel. That’s how you switch guns. This tutorial isn’t really meant for a loadout system but it is certainly possible with some of your own adjustments.

Do you have a g̶a̶m̶e̶ “experience” where we can test your FPS Framework? Just to be able to test the “final product” before making it (it’s not actually the final product since it was made to be changed/customized/expanded by the game creator)
By the looks of things, this looks amazing and i’m 100% gonna do it tomorrow :slightly_smiling_face:

2 Likes

Sorry for late reply (irl stuff), but check the Handle make sure it’s unacnhored and Motor is wielded correctly.

hi, not sure if you’ll see this but im having an error for the
“local UpdateRecoilSpring = RecoilSpring:update(dt)” part, im getting the error “attempt to index nil with update” and i’m not sure how to fix it.

help would be appreciated

Make sure RecoilSpring actually exist.

I can’t open the spring module for some reason. Can you send a link to the module, please?

Yep!

1 Like

yea i figured that, thanks
i just have another question, im trying to add a fire animation but when i try it, it works but only works once if i full auto it, is there any way i can make the animation play again before the last fire animation ends?

i put the script in the input service began function

local GunModel = game.ReplicatedStorage:WaitForChild("SG542")
local Viewmodel = game.ReplicatedStorage:WaitForChild("VM_SG542")
local AnimationFolder = game.ReplicatedStorage:WaitForChild("SG542_Animations")
local SpringModule = require(game.ReplicatedStorage.SpringModule)
local RecoilSpring = SpringModule.new()

--mainmodule
local MainModule = require(game.ReplicatedStorage.MainModule)

Viewmodel.Parent = game.Workspace.Camera
MainModule.weldgun(GunModel)

-- vm position

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


MainModule.equip(Viewmodel, GunModel, AnimationFolder.Idle)

local IsPlayerHoldingMouse
local CanFire = true
local Latency = 0.1


game:GetService("UserInputService").InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		IsPlayerHoldingMouse = true
		local Fire = game.ReplicatedStorage.SG542_Animations.Fire:Clone()--here--
		local FireTrack = Viewmodel.AnimationController:LoadAnimation(Fire)
		FireTrack:Play()--here--
	end
end)


game:GetService("UserInputService").InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		IsPlayerHoldingMouse = false
	end
end)


game:GetService("RunService").Heartbeat:Connect(function(dt)
	if IsPlayerHoldingMouse then
		if CanFire then
			CanFire = false
			
			RecoilSpring:shove(Vector3.new(3, math.random(-2, 2), 10))
			
			coroutine.wrap(function()

				for i, v in pairs(GunModel.GunComponents.Barrel:GetChildren()) do
					if v:IsA("ParticleEmitter") then
						v:Emit()
					end
				end
				
				local FireSound = GunModel.GunComponents.Sounds.Fire:Clone()
				
				FireSound.Parent = game.Workspace
				FireSound.Parent = nil
				FireSound:Destroy()
				
			end)()
			
			coroutine.wrap(function()
				
				wait(0.2)
				RecoilSpring:shove(Vector3.new(-2.8, math.random(-1, 1), -10))
				
			end)()
			
			--RecoilSpring:shove(Vector3.new(1 ,0 ,0))
			MainModule.cast(GunModel, game.Players.LocalPlayer:GetMouse().Hit.Position, 60)

			wait(Latency)
			CanFire = true
		end
	end
end)
1 Like