Skip to main content

Stage 5: Fireball Cannon

Course progressStage 5 of 10
~55 min
Before you start

Finish Stage 4: KillBrick Path first. You've written the Touched → Humanoid → Health = 0 pattern, and you have a violet pad at the end of the hazard path.

Build

a 2-part cannon model that fires self-killing fireballs across a narrow path

Learn

how to create new Parts in code, give them physics with BodyVelocity, and clean them up with the Debris service

Ship

the sixth checkpoint and the biggest script of the course so far — about 25 lines of working Lua

The big idea

Stage 4 hazards were static: placed once, never moved.

Stage 5 hazards are spawned. The script creates fireballs, moves them, then cleans them up.

The cannon model is only the launcher. The fireballs do not exist until your script makes them.

This stage introduces three Roblox patterns: Instance.new, BodyVelocity, and the Debris service.

It also introduces a named function: a function you can call again and again.

The design lesson is timing pressure. There is one narrow path. The question is not where to walk; it is when to walk.

New words
Instance.new
creates a new Roblox object of a given type, like a Part or BodyVelocity
BodyVelocity
an object you put inside a Part that gives it constant velocity (ignores gravity if Force is high)
Debris service
a built-in service whose :AddItem(thing, seconds) destroys `thing` after that many seconds
Explosion
a Roblox object that creates a visual blast effect at a position when its Parent is set to Workspace
named function
a function declared with `local function name(args) … end` that you can call later by name

Build it

Step 1 — Build the narrow path

Narrower than Stage 4 — the player should have nowhere to dodge sideways. Timing is the only strategy.

A narrow path with cannons firing across

Build this part

CannonPath

Block
Open recipe
Size
4 × 1 × 30
Color
Dark stone grey
Material
Slate
Anchored
✓ Yes
Place
Starting at the Stage 5 violet pad, stretching forward 30 studs

4 studs wide — just enough to walk, not enough to dodge.

Step 2 — Build the cannon model (base + barrel)

A cannon is two parts working together. The base is a flat cylinder it sits on; the barrel is the cylinder that aims across the path.

Build this part

CannonBase

Cylinder
Open recipe
Size
2 × 3 × 3
Color
Black
Material
Metal
Anchored
✓ Yes
Place
Beside the path, about halfway along, on the ground

When Shape is Cylinder, the X size is the cylinder's length, Y and Z are its diameter.

Build this part

CannonBarrel

Cylinder
Open recipe
Size
5 × 1.5 × 1.5
Color
Dark stone grey
Material
Metal
Anchored
✓ Yes
Place
On top of CannonBase, rotated so it points across the path toward the player's lane

Rotate using the Properties panel's Orientation — try Orientation = [0, 90, 0] to aim across the path. The cannon's tip is where fireballs spawn.

The cannon is decorative — it doesn't actually fire anything yet. The fireballs come from the script. Build a second identical cannon further down the path (Ctrl+D both parts, position elsewhere) so the player faces two firing zones.

Step 3 — Add the sixth checkpoint

Build this part

SpawnLocation (Stage 6 — past the cannons)

Block
Open recipe
Size
6 × 1 × 6
Color
Bright bluish green
Material
Plastic
Anchored
✓ Yes
Place
At the far end of CannonPath

Also: check AllowTeamChangeOnTouch. Uncheck Neutral. Set TeamColor to Bright bluish green. In Teams, insert a Team named 'Stage 6', uncheck AutoAssignable, set its TeamColor to match.

Step 4 — Write the fireball spawner

This is the biggest script you've written. We'll build it in four passes — a working version after every pass.

Right-click CannonBarrelInsert ObjectScript. Open the editor.

Pass 1 — Create a single fireball, no motion, just to see it appears

local barrel = script.Parent

local function spawnFireball()
local fireball = Instance.new("Part")
fireball.Shape = Enum.PartType.Ball
fireball.Size = Vector3.new(2, 2, 2)
fireball.Color = Color3.fromRGB(255, 100, 0)
fireball.Material = Enum.Material.Neon
fireball.Position = barrel.Position
fireball.Parent = workspace
end

spawnFireball()

Press Play. A glowing orange ball appears at the cannon, then falls.

Good. It exists, but it does not move forward or kill yet. Stop.

Pass 2 — Give the fireball motion with BodyVelocity

local barrel = script.Parent

local function spawnFireball()
local fireball = Instance.new("Part")
fireball.Shape = Enum.PartType.Ball
fireball.Size = Vector3.new(2, 2, 2)
fireball.Color = Color3.fromRGB(255, 100, 0)
fireball.Material = Enum.Material.Neon
fireball.Position = barrel.Position

local velocity = Instance.new("BodyVelocity")
velocity.Velocity = barrel.CFrame.LookVector * 40
velocity.MaxForce = Vector3.new(40000, 40000, 40000)
velocity.Parent = fireball

fireball.Parent = workspace
end

spawnFireball()

Press Play. The fireball shoots in the direction the barrel is aimed.

barrel.CFrame.LookVector means "the direction this part is facing." Multiplying by 40 makes it move 40 studs per second.

Pass 3 — Make the fireball kill on touch, and clean it up after 3 seconds

local barrel = script.Parent
local Debris = game:GetService("Debris")

local function spawnFireball()
local fireball = Instance.new("Part")
fireball.Shape = Enum.PartType.Ball
fireball.Size = Vector3.new(2, 2, 2)
fireball.Color = Color3.fromRGB(255, 100, 0)
fireball.Material = Enum.Material.Neon
fireball.Position = barrel.Position

local velocity = Instance.new("BodyVelocity")
velocity.Velocity = barrel.CFrame.LookVector * 40
velocity.MaxForce = Vector3.new(40000, 40000, 40000)
velocity.Parent = fireball

fireball.Touched:Connect(function(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
humanoid.Health = 0
end

local explosion = Instance.new("Explosion")
explosion.Position = fireball.Position
explosion.BlastRadius = 4
explosion.BlastPressure = 0
explosion.Parent = workspace

fireball:Destroy()
end)

fireball.Parent = workspace

Debris:AddItem(fireball, 3)
end

spawnFireball()

Press Play. The fireball shoots, hits something, creates an Explosion effect, and disappears.

Debris:AddItem(fireball, 3) is the backup cleanup. If the fireball never hits anything, Roblox removes it after 3 seconds.

Pass 4 — Loop the spawn so the cannon fires forever

Wrap the spawnFireball() call in a while true do at the bottom of the script. The function stays the same; only the bottom changes.

Replace spawnFireball() at the end with:

while true do
spawnFireball()
wait(1.5)
end

Press Play. The cannon fires every 1.5 seconds.

Walk the path. Time when to run. You built a cannon from scratch.

Copy the Script to the second cannon's barrel (drag in Explorer or insert + paste). Now both cannons fire — try to make it across.

Understand it

The whole script is one named function (spawnFireball) called from a while loop. This separation is intentional: the function describes "how to make one fireball," the loop says "make one every 1.5 seconds." If you wanted to add a third cannon that fires faster, you could call spawnFireball() from a different loop with wait(0.5). The function is reusable.

Instance.new("Part") creates a brand-new Part that doesn't exist in Workspace yet. It's a floating Part until you set its .Parent = workspace, which is when it actually appears in the game. We set every property (Shape, Size, Color, Material, Position) before parenting — that's a habit worth keeping, because each property change can cause a brief visual hitch if the part is already in the world.

BodyVelocity gives the fireball constant motion. The trick is MaxForce must be high enough to overcome gravity (40,000 in each axis is plenty for a 2-stud ball). Without high MaxForce, the BodyVelocity tries to push but gravity wins and the ball just drops. With high MaxForce, the ball flies in a straight line.

barrel.CFrame.LookVector is the direction the barrel is facing in world space. CFrame is Roblox's combined position + rotation type (Stage 8 goes deep on this). LookVector gives you the "forward" direction as a normalized Vector3 — you multiply it by your desired speed to get a velocity vector.

Debris:AddItem(fireball, 3) is insurance. Even though the fireball self-destroys when it hits something via :Destroy(), sometimes fireballs miss everything (fly off the edge of the world). The Debris service guarantees cleanup after 3 seconds no matter what. Always pair Instance.new with a cleanup strategy — otherwise you accumulate parts and the game slows down.

Script anatomy

How this script builds, launches, and cleans up fireballs

Read this as two pieces: the function is the recipe for one fireball, and the loop at the bottom keeps using that recipe every 1.5 seconds.

local barrel = script.Parent
local Debris = game:GetService("Debris")

local function spawnFireball()
local fireball = Instance.new("Part")
fireball.Shape = Enum.PartType.Ball
fireball.Size = Vector3.new(2, 2, 2)
fireball.Color = Color3.fromRGB(255, 100, 0)
fireball.Material = Enum.Material.Neon
fireball.Position = barrel.Position

local velocity = Instance.new("BodyVelocity")
velocity.Velocity = barrel.CFrame.LookVector * 40
velocity.MaxForce = Vector3.new(40000, 40000, 40000)
velocity.Parent = fireball

fireball.Touched:Connect(function(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
humanoid.Health = 0
end

local explosion = Instance.new("Explosion")
explosion.Position = fireball.Position
explosion.BlastRadius = 4
explosion.BlastPressure = 0
explosion.Parent = workspace

fireball:Destroy()
end)

fireball.Parent = workspace
Debris:AddItem(fireball, 3)
end

while true do
spawnFireball()
wait(1.5)
end
  1. Lines 1–2Find the cannon barrel and the cleanup service.

    `barrel` is the part this Script lives inside, so it becomes the fireball's starting point. `Debris` is Roblox's cleanup helper; we use it so missed fireballs do not stay in the world forever.

  2. Lines 4–10Create one fireball, but keep it offstage for a moment.

    `Instance.new("Part")` makes a new part in script memory. The script sets its shape, size, color, material, and starting position before parenting it to Workspace, so the fireball appears already configured.

  3. Lines 12–15Give the fireball forward motion.

    `barrel.CFrame.LookVector` means 'the direction the barrel points.' Multiplying by 40 turns that direction into speed. `MaxForce` is high so the motion wins against gravity and the ball flies forward.

  4. Lines 17–31Decide what happens when the fireball hits something.

    This is the Stage 4 touch pattern inside a moving object. If the thing hit belongs to a player, set the Humanoid's Health to 0. Either way, make an Explosion effect and destroy this fireball so it does not keep hitting things.

  5. Lines 33–34Put it into the game, then schedule cleanup.

    `fireball.Parent = workspace` is the moment players can see and touch it. `Debris:AddItem(fireball, 3)` is the backup plan: if the fireball misses everything, Roblox still removes it after 3 seconds.

  6. Lines 37–40Use the one-fireball recipe over and over.

    `spawnFireball()` makes one fireball. The loop waits 1.5 seconds and makes another. This is why named functions matter: the recipe stays in one place while the loop controls the rhythm.

Try this

Learning beat

Try this

Three short experiments. Predict before you run, then test your guess.

Predict first

Comment out the Debris:AddItem(fireball, 3) line on line 34. Press Play and let the cannon fire for 30 seconds without walking onto the path. Open the Output and check Studio's performance. What happens to the workspace? Why?

Compare

Change the velocity multiplier on line 13 from 40 to 15. Then change it to 100. Play both. Which speed makes the cannon feel like a fair timing challenge — and what cues does the player use to predict where the fireball will land?

Connect

The structure is function spawnFireball() … end plus while true do spawnFireball(); wait(1.5) end. Stage 7's TweenService and Stage 8's Heartbeat connection are different ways to schedule recurring work. What's the trade-off between using a while true do versus a named event subscription like Heartbeat:Connect?

Test your stage

  • Both cannons fire a fireball every 1.5 seconds.
  • Standing in front of a fireball kills you; you respawn on the violet pad.
  • Fireballs that miss everything disappear (don't pile up on the floor).
  • Fireballs that hit the path create a small Explosion effect.
  • You can time the cannons and reach the bluish-green pad.
  • Design check. Walk the path with both cannons firing. Are the timings staggered enough that there's always a safe window? Or are there moments where both cannons fire at once and the path is impassable? Adjust by changing one cannon's wait(1.5) to wait(1.7) to desync them.

If it breaks

  • The fireball appears but drops straight down. The BodyVelocity's MaxForce isn't high enough to overcome gravity. Re-check line 14 — both axes should be at least 40000.
  • The fireball appears inside the cannon. fireball.Position = barrel.Position puts it at the barrel's CENTER. You can offset it forward: fireball.Position = barrel.Position + barrel.CFrame.LookVector * 3 (spawns 3 studs in front of the barrel).
  • The fireball flies the wrong way. The barrel's Orientation determines LookVector. If the cannon fires away from the path, rotate the barrel: in Properties → Orientation, try [0, 90, 0], [0, -90, 0], or [0, 180, 0] until it aims correctly.
  • Studio freezes. You probably have while true do without a wait() somewhere. Press Esc, find the while true do in your script, and confirm there's a wait(1.5) (or similar) inside.
  • Fireballs pile up at the cannon and never move. The cannon barrel might be too thick — the fireball is spawning inside the barrel and getting stuck. Make the barrel longer (X size from 5 to 7) or spawn the fireball further out (see the offset trick above).
Coach notes

This is the largest script in the course. Budget the full 55 minutes. Strategy: do not let campers type the whole Pass 3 script as one block. They will make a typo somewhere in 25 lines and not know where to look. Insist on Pass 1, then Pass 2, then Pass 3 — running between each.

  • Common typo: BodyVelocity vs BodyVelosity. Lua doesn't autocomplete, so the misspelling silently fails. Check for it before debugging anything else.
  • barrel.CFrame.LookVector confuses campers — it's hard to predict the cannon's facing direction in 3D. Have campers add a print: print("LookVector:", barrel.CFrame.LookVector). The output (a Vector3 like 1, 0, 0) tells them which axis the cannon points along.
  • The Debris service is easy to skip. Force-walk through Try-This's first prediction (commenting out Debris) so campers SEE the workspace fill with stale fireballs. The lesson sticks better as a visceral demo than as a warning.
  • If a camper is way ahead, push them to the hard stretch — variable-speed fireballs are a real upgrade.