Writing an FPS framework, part 2 (2020)

Can you reply with the link of the “Weld Editor” Plugin you were using here?


If rigedit works, use that, otherwise an old version of moon animator


For some reason, the event always says that the bullet is nil

function rayUpdated(_, segmentOrigin, segmentDirection, length, velocity, bullet)
	local BulletLength = bullet.Size.Z / 2 -- This is used to move the bullet to the right spot based on a CFrame offset
	bullet.CFrame = CFrame.new(segmentOrigin, segmentOrigin + segmentDirection) * CFrame.new(0, 0, -(length - BulletLength))


(this also happens with the ray hit function)

Has anybody tried making crouch/laying and sprinting for this framework? I’m trying to make my own system for that but i always end up giving up.



  • grab and modify an existing foot planting script (it doesn’t have to be inside the framework at all, as long as you have a Crouching/Prone variable to modify)
  • modify the character’s RootJoint C0/C1 to look like they’re crouching
  • play animations for each stance

I have not tried the last one, but the base thing is adding a :crouch() function to the framework and doing whatever in there


The first option sounds the easiest, thank you BlackShibe! I didn’t think of using something outside the framework so i just overcomplicated everything.


How would I go about making the arms look the same as my characters arms? I understand that I could just change the Arm’s BrickColor but how would I go about making the shirt visible there too? instead of just a being naked arm.

like make the blue sleeve show up


You have to either make a custom hand with UV mapping that matches the shirt texture or insert a humanoid where the arms are with a shirt instance


Thank you so much for this! It was so helpful, it even made me make this:


check ur animations. that might’ve been messed up

1 Like


This tutorial was really useful! I learned a lot and I’m even going to make my own FPS
game, but I have one question, you said that we shouldn’t use the “Hit” remote event on a real game, so, what are the other ways of detecting the ray hit? or is there a more efficient way of doing it?

1 Like

I was more talking about your common script kiddie doing Hit:FireServer(essentialy everyone in the map) than detecting the hit itself


Oh! Well, thanks for replying!

1 Like

i cant observe the conventions of common english and grammer ;-;


What’s your studio theme looks good on the eyes.

studio dark and a custom code editor theme i havent used for over a year now

1 Like

I want to get some clarification on this. With my game, I am doing this from the client (I’m using FastCast too)


but I’m unsure what sanity checks I can use on the server, this is all I really have atm :confused:

--// Ray hit
local function Hit(player, playerHit, hitInstance, hitPosition, castData)
	if player == playerHit then return end -- Creator can't tag themselves
	local Teams = GameSettings:GetAttribute("Teams")
	if player.Team == playerHit.Team and Teams then return end -- Can't tag your own team
	local Humanoid = playerHit.Character:FindFirstChildWhichIsA("Humanoid")
	if not Humanoid then return end
1 Like

so for anyone that wants to add a new offset to the system, heres how i did it after messing around with a lot of stuff in the update function:
(blackshibe approved)

first make your offset, like idle, aim, etc. in my case im making a sprint offset

next, copy the equip offset’s lines of code, so you’ll have something like this

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

but replace the “equipOffset” with the last offset you’ve added before the current one you’re writing, in my case the last one is equipOffset.

now, do this

local finalOffset = sprintOffset -- replace sprintOffset with your own

this should hopefully work, if blackshibe wants to correct anything wrong i did, hes free to, ill edit it if theres anything wrong i did, this is just how i did it myself


This is a really amazing tutorial! The functionality is versatile and I can add onto it with more features, and I’ve learned a lot of things, especially how metatables are useful. I’ve scrapped my old FPS project in favour of this framework tutorial and the development’s gone much better.

I haven’t really encountered problems until now, I can’t really get ammo working. I keep a realtime ammo table on the client and a secure version on the server (updates every fire/reload), but there’s an issue. Getting the ammo table from server to client isn’t working because the table becomes nil when the server returns it to the client, even though it was there when I printed it before return?

In the fire event I subtract 1 from the current ammo table on the server, then I pass that new ammo table from the server onto the FastCastHandler that fired the event. Then the FastCastHandler returns that to the client. But the table doesn’t exist when the server script returns it?

Server code below:

if weaponTable.ammoData[weaponTable.weaponIndex].current < 1 then return end
-- the statement above is not the issue, I tested it
remotes.Fire:FireAllClients(player, rawOrigin, rawDirection)
weaponTable.ammoData[weaponTable.weaponIndex].current -= 1
-- this line of code above works
print(weaponTable.ammoData[weaponTable.weaponIndex].current) -- this doesn't print nil?

-- animation code in this space

return weaponTable.ammoData -- It returns a nil value for some reason when the event is fired on the FastCastHandler, even though it just printed the actual table??

Code on FastCastHandler:
(isReplicated is false when fired from the client, I’m sure)

function fastcastHandler:fire(origin, direction, properties, isReplicated, repCharacter)

	local rawOrigin = origin
	local rawDirection = direction
	local ammoData
	-- raycast code here
	if not isReplicated then
		ammoData = replicatedStorage.WeaponRemotes.Fire:FireServer(rawOrigin, direction, id)

	-- blacklist code here

	-- fire the caster
	local activeCast = mainCaster:Fire(origin, direction * properties.firing.range, properties.firing.velocity, castBehavior)
	activeCast.UserData = {properties = properties, id = id}
	return ammoData

Code on client:

if self.ammoData[self.weaponIndex].current < 1 then return end
-- this condition works above, but doesn't work after the first shot because the new ammoData table is nil

-- raycast code and muzzle flash here

-- this is where the module breaks later because the new ammoData table from the server is nil. I don't know why
local ammoData = fastCastHandler:fire(origin, direction, self.settings, false, self.character)
self.ammoData = ammoData

I’m trying to see if I can solve this issue, but right now I’m pretty much stumped.
Thanks if you can provide any assistance.

1 Like

You can’t return values to the client via RemoteEvents, and neither should you (Invoking RemoteFunctions is rather slow)

You should only synchronize the client and server ammo when reloading, otherwise it’s a bit too much for the network