Well you guys seemed to like the tutorial so heres a Part 2. ¯\_(ツ)_/¯ (thanks for 33 likes!!!)
If you just came, you’ll have to first see the original post to continue else it might get confusing for you, check it out here.
Also this tutorial is a bit long might take you a day or two to complete this. (also sorry if it’s a bit complex and confusing for some people, tried explain it as much as I can)
Objectives:
- Recoil, Muzzle flash and sounds
- Bobble & Swaying
- Hit detection
- Bullet replication
- Aiming
- Reload
Changes regarding the previous post
So in the projectile module I forgot to put local Loop
and dt * 60
. here is the editted version:
(srry)
Before continuing, you need to have some knowledge on:
- Raycasting (Hit detection)
- Remote events (Server replication)
- Tween Service (Aiming)
- Lerping (Aiming)
Step 1: Recoil
We’re going to use this SpringModule.rbxm (1.3 KB) to make recoil, bobble and swaying.
When your done downloading the spring module, put the module inside ReplicatedStorage and require it in LocalHandler.
We will be using 3 functions from the SpringModule:
-
SpringModule.new()
(creates a new spring function, btw you can put params into the brackets but I just put them as default) -
Spring:shove(Vector3.new())
(sets the goal of the spring) -
Spring:update(dt)
(updates the spring)
Alright so the concept is when the guns shoots we want to shove the spring. This might get confusing but if you understand how it works it’ll be easy.
So first let’s create a new spring function
So now with this variable we’re going to do RecoilSpring:shove(Vector3.new(1, 0, 0)
every time the gun fires, you can change the 1 to any number you like.
Now we’re going to update the spring, so we will need to update it every frame. So instead of creating a new RenderStepped we can just use the existing one. So let’s pass in RecoilSpring as a parameter
Then in the MainModule, we’re going to make a param for the RecoilSpring. After that we’re gonna update the spring: local UpdatedRecoilSpring = RecoilSpring:update(dt)
.
After we get the updated spring, we will apply it to the camera and viewmodel so it has a bounce / recoil effect anddd
wow… thats pretty lame . Let’s do some adjustments.
- First we’re gonna bump the recoil
- Second we’re gonna make random sideways recoil and screen-shake
- Third we’re gonna make it so it comes back down.
If you’re experienced and know how this spring module works maybe you can make bullet patterns!
then update on the MainModule…
That’s more like it!
Step 2: Muzzle Flash and Sounds
So for the muzzle flash, I’m just going to use a particle emitter from the toolbox. When you’re done finding the perfect muzzle flash for your gun. Go ahead and put it inside your barrel and set ‘Enabled’ to false (aka disabling). We will just be using :Emit()
to emit them.
Tip: Turn on LockedToPart so the particles stick to the part
Alright, sounds… I sometimes have a mental crisis when determining where I want to put the gun’s SFX but for this tutorial I’ll make a folder inside GunComponents called ‘Sounds’ and put all of the gun’s SFX in there.
Also make sure your SFX has ‘PlayOnRemove’ set to true
Alright so the concept is when the gun fires, we will get every particle emitter inside the barrel and :Emit()
then the sound will get cloned and parent to workspace (because sound origin is cringe) and play it.
Works beautifly!
Step 3: Bobble and swaying
I’m just going to make a simple bobble and swaying using springs. So lets create 2 new spring variables and put the spring in the .update
parameter
In a nutshell, we’re shoving the spring according to the player’s velocity with the bobble formula then we apply that to the HumanoidRootPart.
I underlined everything new that is added
Here is our bobbing
For swaying I won’t be going indepth with this one. But in a nutshell it gets the mouse “velocity” and apply the offset to the viewmodel.
Here is the swaying. (quite beautiful innit?)
Step 4: Hit detection
If you use FastCast you can skip this step!
Alright so the way I did my hit detection is by using raycasting. It’s hard to explain it by words but it’s like putting your hand out and walking blindly, when your hand hits something you stop. We’re raycasting to the bullet’s future position. If it hits something we destroy the bullet, disconnect the loop and do something about it. (lol)
First we’re gonna raycast to the bullet’s future position time 1.5 just to be safe.
We’re gonna check if ‘Hit’ hit something and then check if it hit a player or a wall.
Let’s make a remote event to damage the player. Make a remote event and then put it in ReplicatedStorage, I named the remote event “Damage” to keep it simple.
So let’s make a variable for the remote event, I named the remote event ‘Damage’ and then pass it through .cast()
.
Now in the MainModule lets fire the remote event whenever it hits a player. Also we’re gonna handle the else
statement later after we’re done with this damage thing.
DISCLAIMER: please DO NOT pass the damage as a parameter for server side damage, im doing this to keep it simple for beginners. (also when you’re getting a bit more advance in making fps frameworks you will need to start integrating sanity checks to make sure your server side is safe, for example another invisible bullet cast to make sure it’s actually valid)
So we’re gonna pass in the victim and the damage.
Now make a server script inside a ServerScriptService. We’re gonna handle remote events signals inside the server script. I named it ‘ServerHandler’
So lets make .OnServerEvent
event for the damage. I know we already checked if “Hit” was valid but I’ll just check it on the server just to be safe.
anddd
Alright time to put some code on the else
statement. Basically we will disconnect the loop then destroy the bullet. You can make it so an impact sound plays or make bullet holes, but I’m just going destroy the bullet nothing fancy.
Honestly you can stop reading the tutorial here, but let’s make bullet replication so other players can see the bullets.
Step 5: Bullet replication
Basically the concept it, when we fire a bullet it will tell the server that we casted a bullet. Then the server will tell all clients (except you) to cast a bullet.
Client > Server > All Client
First let’s make a remote event. I’ll just name it “Fire”
Before we proceed, let’s make some changes to the .cast()
function. The changes will be important for bullet replication.
So first instead of passing the GunModel, we will directly pass the origin Vector3.
and we will change inside the LocalHandler
Alright let’s continue. So as I was saying, we we will make a variable for the “Fire” remote event, I called it ‘Fire’.
We will do :FireServer()
every time we fire the gun and then we will pass the origin and the end position as the parameter.
Then on the server we will make a variable for the remote event and do .OnServerEvent
.
Also we’re gonna use the same remote event to do :FireAllClient
. We’re gonna pass the original client that sent the signal, the origin and the end position.
Now let’s go back to the LocalHandler and do a .OnClientEvent
for the “Fire” remote event.
We’re gonna make sure that we’re not replicating our own bullets so lets add an if statement check if the client is us.
Now we’re gonna use the same .cast()
to replicate our bullets but set the remote event parameter to nil since it’s just a fake bullet.
Now let’s make some little changes to the .cast()
function. Basically we will check if the remote event is nil or not, if it’s not nil then damage the player else just destroy the bullet.
anddddd
Step 6: Aiming
Alright so there are multiple ways but I’m going to do it how I do it. First let’s make a value to be our alpha value and make sure it is a NumberValue not an IntegerValue
So how this will work is upon aiming we will Tween the “AimAlpha” value to 1 and then lerp the Sight to the viewmodel’s camera. Alright alright, you may not understand but just follow along.
Now we will create a new function called .Aim()
. We will pass in 3 parameters, ToAim, viewmodel and the gun. As I said earlier we will tween the Alpha value to 1 if we’re aiming and to 0 if we’re un-aiming. Oh yeah don’t forget to play the Tween after being created.
Now we will lerp the Sight to the Camera. So in the .update()
we will pass a new parameter which is the gun model.
Now in the LocalHandler we want to detect when the client or the player is holding the right mouse or not. If the player is clicking the mouse we will aim, if the player is holding it we will fire the .Aim()
function.
Alright now it works, we’re done… or have we…?
Now you might see a problem, if your gun has a scope you should notice that when you aim and shoot it wouldn’t damage the player because your mouse is detecting the scope. My scope doesn’t have a mounted scope so Ill have to simulate one:
(recorded 16/8/2024 sorry if its not consistent)
Now a solution for this is to make our own custom MouseModule, now you would make a seperate module for this but I’m just going to do a simple version of it.
So first let’s make the function module.GetMouse()
. We will pass in 2 parameters: Distance and RaycastParams
What we’re doing here basically is raycasting from the origin (or the camera) do it’s set distance if it hit something we will return the Position else we will return the max distance. Won’t explain much here, but it basically uses some funny functions and raycasting.
Now let’s replace the :GetMouse()
with this brand new function
Now let’s test
Step 7: Reload
Honestly I didn’t feel like making this sections as I thought it was simple to implement. (That also explains for the sudden change of the theme color, since I’m not using Rojo right now.) Anyways let’s get on to it
The concept:
We will have an ammo variable containing how many ammo we have currently, before firing we will check if ammo is 0 or not, if it is 0 reject the shot and for every shot we will subtract 1 from it. If the player wants to reload we will reset the ammo variable
So now let’s implement the reloading mechanic
Let’s say we want to play a reload animation when player reloads, and refill our ammo variable only when the animation end. So just put your reload animation in the AnimationsFolder. (You can reuse the old equip animation to make a reload animation).
Now for the .reload()
function.
Now for the result.
Note: you can add more to this like SFX, and screen shake effects but I’m trying to keep it as simple as possible.
We’re finished hooray!!! I hope you guys learned something from this tutorial once you know the basics and requirements to make an FPS framework, you can start advancing to make more complex FPS frameworks!
If you have any questions you can join the discord server