Welcome to the Beginner’s Guide on scripting VFX (Visual Effects) [Part 1]
Note: This is my first ever tutorial, if I made a mistake or I did not explain some parts, pls let me know
When I started scripting VFX on Roblox, I couldn’t find any useful tutorials (neither on the Devforum nor Youtube). However, after learning scripting for a few days I was able to start scripting some VFX, which were, of course, incredibly imperfect. After 2 months of scripting I was good enough to start getting commissioned in scripting VFX for high prices, and for rather big in-development games.
Here is a fraction of my work:
Show
Fire Fist and Flame Commandment (streamable.com)
Light Bombardment (streamable.com)
Blackhole (gyazo.com)
Thunderclap and Flash (gyazo.com)
Light Speed Fists (gyazo.com)
This is a beginner’s guide, and throughout the tutorial you will learn the basic knowledge of effectively scripting visual effects, as well as a basic fireball which this tutorial will follow. Do note this will cover:
- Receiving inputs
- Client-Server Communication
- Debounce
- Damage detection
- VFX Spawning (Server-sided (Easy, but not recommended), Client-Replication (Not very beginner-friendly, but I highly recommend this; reasons are inside the article))
Important: You must have some experience in scripting beforehand, or some parts of this guide might confuse you.
Step 1: Receiving Inputs
There are two main ways of detecting inputs:
- UserInputService
- ContextActionService
For this “Beginner’s Guide” I will be using UserInputService, as it is easier to understand.
Let us first get the service itself.
Now that we got the service, let’s create a function to help us with firing servers, and what not later.
Now let’s use the InputBegan event to detect inputs from the player, and connect it to the DetectInput() function we just created.
As of now, it takes in every input - and we don’t want that. What we do want, is to detect a specific input, so we’re going to check the input that the InputBegan has returned.
What we have done here is to take in the Input and GameProcessedEvent which were returned by the InputBegan event. GameProcessedEvent is true if the inputs were observed by the game (e.g typing in a textbox, typing in chat), so we will return the function if this is true. Next we’re checking if the Input is the keycode which we specifically want (in our case this is Q), if it matches the script will print (You can visit here for more information about these two parameters).
Congrats, you now have a working input detection! Let’s move onto step 2
Step 2: Client-Server Communication
Now let’s create a remote event to help us communicate between the client and server.
Create a variable for the event, and let’s fire the server from the local script.
The code should now look like this:
Let’s now create a function on a server script, and connect it to an OnServerEvent event which listens to the FireServer() called from a local script.
Now if you test it, it should be working like this:
Congratulations! You now have a way to communicate between the client and the server
Step 3: Debounce
There are generally two main ways of creating debounces:
-
Using a bool like this:
-
Using time like this:
The two ways mentioned above are both client-sided debounces which are easy to understand for beginners but they are easily exploitable, so unless you’re just practicing I highly recommend you reading the article I made on how to create server-sided debounce below.
Server-sided Debounce
This comes incredibly handy when coming into contact with exploiters. Exploiters can do anything a local script can do, which includes firing servers and with client-sided debounces, they are granted the ability to spam FireServer(), which nobody wants. In this case, there is a simple solution; using server-sided debounces.
Due to the simplicity of the first method, I will be using the bool debounce in this guide. First we’ll need to construct a table in the server script which acts as a debounce (Since there can be multiple players in a server, we cannot use a single variable - like how we do it in a local script).
Our next step is to get the player from the OnServerEvent, so add a player parameter into the ServerReceived function we made earlier.
We will now need a way to check if they are on cooldown or not. For this we can add an if statement to see if the player is on cooldown by simply adding their name into the table. If they are in the table, they’re currently in cooldown, else they’re not.
The code should now be somewhat similar to this:
Congratulations once again! You now have an extremely reliable debounce, which exploiters in no way can abuse!
Step 4: Damage detection
We’re getting closer to the end! In this step you will learn how to handle the damage. First of all let’s start by creating a hitbox, which will not only help us visually, but also help in detecting targets.
I have created a simple hitbox for the fireball:
Properties if you're interested
Color = [255, 0, 0]
CastShadow = false
Material = Neon
Transparency = .5
Size = 2, 2, 2
Anchored = false
CanCollide = false
CanTouch = true
You can put it wherever the server script can access, and as for my liking I put it right under the server script with the name “Hitbox”. Let’s start by creating a variable for the hitbox.
For this beginner’s tutorial, we’ll be using the Touched event to detect targets. Let’s get onto the main detection!
Where the server is received, we’ll first get the player’s character, then we’ll clone this hitbox and launch it forwards (based on player’s character), and add a Touched event so it listens to parts that get touched. The code should look like this:
And currently, it spawns in front of the player’s character!
To launch it forwards, we’ll be using TweenService for the sole purpose of being beginner-friendly. To launch it forward relative to the character’s position we’ll use the HumanoidRootPart’s cframe and multiply it with a negative Z vector. However the Touched event will not detect movements made by cframes, so we’ll turn the cframes into position by simply doing .Position; which should look something like this:
Remember to delete the hitbox once the tween is done! (In my case 1 second)
Now that it’s working it’s time to connect a touched event to the hitbox so it can listen to detections.
We can see that it’s working fine, however let’s just add a little filtering so it will never touch the character who casted the fireball by using IsDescendantOf().
After this, let’s make it detect only viable humanoid targets and deal damage. For this we will use :FindFirstChild(). First we’ll check if Hit.Parent(Hit is a basepart and is likely to be a body part, so Hit.Parent is referring the the enemy character) contains a humanoid. If there is then take damage, else do nothing.
Great we’re once again closer to the main step! However after testing for a few times, you might notice that the actual damage dealt doesn’t equate to what you really wanted to deal!
In this case it’s being dealt 25 damage(we added 5 in the damage parameter!). You might or might not be wondering - why is this happening? It’s simply because it detected several times. This is an incredibly easy fix; by the usage of values and debounces. There are several ways to approach this but I will be using values so beginners can understand.
We’ll need to check if a value exists in the humanoid or not, to check it we’ll use FindFirstChild and check the name of the debounce (in my case I made it use the player’s name and concatenated with a “special key”; FireballDebounce) - the code should be similar to this:
For it to actually work, let’s create a value under take damage to act as our debounce, which of course we’ll need to concatenate the player’s name with FireballDebounce. This is to prevent different players getting debounced by just 1 debounce.
Important Note: If you’re making different attacks you’ll want to change the name to different names or different moves will detect different debounces
And finally we’ll delete it after 1-4 seconds so the debounce actually gets reset. I prefer using Debris service but you can delete it however you like.
If you made it this far and all’s working fine, Congratulations yet again!
The next step is the final as well as the most important step! Make sure to understand it thoroughly. If you don’t that’s fine! - feel free to ask me in the comments
Step 5: Spawning VFX (Very Important!)
As beginners, you (have) probably spawned VFX server-sided(This means spawning the vfx on a server script, which increases the server load thus increases ping tremendously)! As someone who’s been mainly doing visual effects on roblox, it is not an exaggeration to say that server-sided effects are a sin ()! However if client-replication is too hard for you it is understandable. I, myself, made server-sided vfx for more than a month until I learned of this client-replication technique.
Server-sided VFX spawning for beginners
I have created this artistically beautiful fireball to serve as our main effect.
You can put it anywhere the server script can access so I’m putting it directly under the script. Let’s give it a variable:
Now let’s clone it right where we clone the hitbox so they are aligned.
Let’s also make it do whatever the hitbox does - of course, aside the Touched event.
Scripting Practice Place - Roblox Studio (gyazo.com)
Great! It’s in sync with the hitbox as of now. To spice some things up you could:
- Make it spawn elsewhere (e.g fireballs raining from the sky, fireballs shooting from your eyes)
- Make it explode when Touched
- Add particle(s) or trail(s) to the fireball
Congrats! You have finished the part 1 of the beginner’s guide on scripting visual effects by SushiInASuit/SushiScripter.
Client-Replication VFX spawning (hard to understand for beginners, but highly recommended)
Firstly, you’ll have to understand how the idea of client-replication works.
A player will fire the remote to the server:
The server receives this signal, and the positions of where to spawn the vfx are defined here, which are then fired to all clients with that information:
Because the position sent is the same to all clients, the vfx will be positioned at the same place for everyone (reducing server load which results in faster vfx loading and doesn’t load up the server anymore).
Because we’ll need them to be in sync, the cframes have to be the same so instead of this:
we’ll need to do this:
We’ll also need to replace the tween goal, which will be changed from this:
to this:
Now that we have replaced these, it’s time for the star of the show - FireAllClients().
This is the leading function for client-replication, so let’s get started!
Let’s call this function right under the creation of the hitbox, so it gets synced as much as possible. We’ll also add CF and Pos so the client will know where to spawn and where to tween.
Now that that’s done let’s create a function to help us spawn the effects.
Add the parameters CF and Pos which were sent by the server - these are incredibly important.
I made this artistically beautiful fireball to use:
Put it wherever the client can access it (I like putting it in ReplicatedStorage), and give it a variable.
Now we’ll clone it and set cframe to the cframe sent by the server so it aligns with the hitbox (Don’t forget to delete it afterwards!).
And let’s use the Position sent by the server to tween the fireball in sync with the hitbox.
And…drumrolls
You should now be able to acheive something like this:
Scripting Practice Place (gyazo.com)
If u reached this far then congratulations! You may or may not have scripted your very first client-replication vfx which will play a huge role later onwards, especially if you’re going to focus on making visual effects.You have finished the part 1 of the beginner’s guide on scripting visual effects by SushiScripter/SushiInASuit.
Important: This is part 1 of the beginner’s guide, it only covers the basic structure of scripting vfx. Part 2 will feature a more complicated structure of visual effects, ranging from raycasting, how trigonometry can be used, useful math functions and tons more
Feel free to ask me any questions in the comments!