Note
This tutorial is older and uses the deprecated form of raycasting which is now replaced by RaycastParams. I may update this tutorial.
This is something many beginner scripters are so inspired to do the moment they want to script, so I made a tutorial for it.
Let’s start with what you should know:
-Remote events and how client to server works
-UserInputService
Animation
First, create the animation for throwing your fireball. Don’t include any limbs that won’t be useful, so if you only throw a fireball with an arm, only use that arm. Make sure your animation has Action priority.
I create a keyframe that represents the arm going upward and the other two are just the arm’s original position and rotation. You can use your own animation if you’d like.
Right click the keyframe that represents when the fireball is going to be thrown and click Add Animation Event Here:
Create an animation event to something you will remember later, I will use “Activate”.
Publish your animation and remember the ID for it:
Local script
Make a local script into either StarterGui or StarterPack, I will put it in StarterGui for my case.
Setup the code like so:
UIS is the service we will use to detect input (when you press any keyboard button)
KeyToUse is the KeyCode Enum we will use to get what the player is pressing. We will use “E” since we want the player to shoot a fireball when they press “E”.
The animation is an Animation Instance we will create, change its AnimationId to the animation ID you want after “rbxassetid://”.
We will then try to retrieve the humanoid from the player’s character like this:
We are using a loop to get the humanoid since sometimes, the humanoid is not a descendant of game
which breaks the script. (Find out why here)
Next, load the animation by using :LoadAnimation(animation instance)
on the humanoid,
We then will start detecting when the player presses E by using the InputBegan event of UserInputService (we defined as UIS).
It will return to us two parameters which is the InputObject and a boolean called gameProcessed (true or false). gameProcessed if true usually means that the player has clicked on a GUI like the chat. We don’t want them to start activating fireballs while typing in the chat so we will use a guard clause.
The guard clause looks like this:
if gameProcessed then return end -- return to stop the function from continuing
A guard clause looks more readable and will do exactly what we want, to stop the function if the gameProcessed variable is true.
The properties you see on the right is the properties of an InputObject, which we are getting from the parameter called input
.
Define the camera for what we will do soon and the remote for later under our loaded animation variable.
After the if statement we create to check if the pressed input was exactly what we wanted, we will first define the location of our mouse as a variable.
:GetMouseLocation()
is something we can call on UserInputService to get a Vector2 value of where the mouse is (Vector2 is a X and Y value, just two coordinates).
Then, play our animation and we wait for the marker event we created earlier in the Animation Editor using the AnimationTrack we got from loading the animation and using :GetMarkerReachedSignal(name of our event)
to get the event and then use :Wait()
on it.
We will then proceed to use the mouse location to raycast from the camera to shoot our fireball towards.
We use :ViewportPointToRay
which will shoot the ray for us with the mouse location we got and then we will redefine the ray variable as a new Ray using the ray we got from :ViewportPointToRay
.
A ray has an origin property and direction property so we will utilize those. We multiply the direction by 200 as in that the ray will go in its direction by 200 studs, which would be the range the fireball can reach.
We then calculate the pos to shoot the fireball towards, we don’t need to know if a target was hit or not since the fireball isn’t going to be instantly getting there so we leave the first variable with an underscore (_
) to indicate we don’t need it.
Server script
Now, create a server script and place it in ServerScriptService then create a RemoteEvent in ReplicatedStorage. In the server script we will start with the following code:
Define our Tween variables for later:
Then setup a variable that was our range from before and an OnServerEvent function for the RemoteEvent we defined, it will give us the parameter for player which we use to get the HumanoidRootPart.
Our second parameter will be the position that the client sent us as a Vector3.
We will also have setup a guard clause for changing the Magnitude of the distance from each other to make sure they’re within range as a tiny anti-exploit protection.
Add the right hand or right arm variable as you want. If you want to make it R6 only then just replace that line where rightHand
is defined with:
local rightHand = player.Character["Right Arm"]
Then, we create a part as well with what we want as our fireball
And we CFrame it to what we want and set its Parent to workspace so that it exists:
We are basically moving it from the right hand’s front (which is downward in the Y direction) and then adding the part’s size so it won’t be colliding with the hand then we add an offset of 1 stud to make it a little farther away from the hand.
We divide the Y property in Size by 2 since we are using the center of the rightHand which is the position then going half the size to the surface of the hand.
Next, use the Create method of TweenService to start making our tween. We will utilize the tweenInfo variable from earlier with the TweenService. The third argument we send is a table of properties we want to change, in this case we want to change the Position value to our desired endpoint.
We then play the tween then make a Touched event that checks when something with a Humanoid has touched it.
Next we will check if what we hit was actually our character to prevent damaging ourselves and to deal damage if it’s a different person.
We are making the humanoid take 25 damage, adding a fire object that we edit the heat property to 20 and then removing it 2 seconds later, disconnecting our event, and then destroying the part. EDIT: Since we destroy the part anyways, you may skip the disconnection and setting the connection as a variable.
Since there’s a possibility the part would be destroyed of course after we wait for our tween to be completed, if the part remains we destroy it.
Next, we’ll implement our debounce. We do it server-side as it prevents exploitation. We’ll first have to go back to where we created our OnServerEvent
connection.
Then we’ll create a table that will store our players as a debounce when they shoot a fireball and use a guard clause to prevent them from shooting a fireball too fast by checking if they’re in the table. We’ll also add them to the table if they’re not in the table already.
And then go to the end of the code to implement a delay which will run a function for us 1 second ahead of time which is our debounce.
Result
Local script code
local UIS = game:GetService("UserInputService")
local KeyToUse = Enum.KeyCode.E
local animation = Instance.new("Animation")
animation.AnimationId = "rbxassetid://5000364357"
local player = game.Players.LocalPlayer
local humanoid = player.Character:WaitForChild("Humanoid")
while humanoid and not humanoid:IsDescendantOf(workspace) do
humanoid.AncestryChanged:Wait()
end
local anim = humanoid:LoadAnimation(animation)
local event = game.ReplicatedStorage.FireballRemote
local camera = workspace.CurrentCamera
UIS.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then return end
if input.KeyCode == KeyToUse then
local mouseLocation = UIS:GetMouseLocation()
anim:Play()
anim:GetMarkerReachedSignal("Activate"):Wait()
local ray = camera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
ray = Ray.new(ray.Origin, ray.Direction * 200)
local _, pos = workspace:FindPartOnRay(ray, player.Character)
event:FireServer(pos)
end
end)
Server script code
local replicatedStorage = game.ReplicatedStorage
local fireballRemote = replicatedStorage.FireballRemote
local maxRange = 200
local TweenService = game:GetService("TweenService")
local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local playerDebounces = {}
fireballRemote.OnServerEvent:Connect(function(player, targetPos)
if table.find(playerDebounces, player) then return end
local humanoidRootPart = player.Character.Humanoid.RootPart
if not humanoidRootPart then return end
if (humanoidRootPart.Position - targetPos).Magnitude > maxRange then return end
local rightHand = player.Character.RightHand
table.insert(playerDebounces, player)
local part = Instance.new("Part")
part.Anchored = true
part.CanCollide = false
part.Shape = Enum.PartType.Ball
part.BrickColor = BrickColor.new("Medium red")
local size = Vector3.new(3, 3, 3)
part.Size = size
part.CFrame = rightHand.CFrame * CFrame.new(0, -(rightHand.Size.Y/2 + size.Y + 1), 0)
part.Parent = workspace
local createTween = TweenService:Create(part, tweenInfo, {
Position = targetPos
})
createTween:Play()
hasBeenTouched = part.Touched:Connect(function(hit)
local parentOfHit = hit.Parent
local humanoid = parentOfHit:FindFirstChild("Humanoid")
if not humanoid then return end
if parentOfHit == player.Character then return end
humanoid:TakeDamage(25)
local fire = Instance.new("Fire")
fire.Heat = 20
fire.Parent = hit
game.Debris:AddItem(fire, 2)
hasBeenTouched:Disconnect()
part:Destroy()
end)
createTween.Completed:Wait()
if part then
part:Destroy()
end
delay(1, function()
if not player then return end
table.remove(playerDebounces, table.find(playerDebounces, player))
end)
end)
Here is a place file of the fireball product after I added the extras from below.
Fireball.rbxl (38.7 KB)
Extras
If you want to smoothen the surfaces of the part you can do this:If you want to create a burning audio effect upon hitting something you can do go to where we created our fire instance before in the server script and create a sound instance and set its ID.
You can similarly create a trailing fireball audio effect by doing the same (without using Debris:AddItem
since it is going to be destroyed anyways) but parent it inside the part after it’s made.