Skip to main content

Stage 7: Rolling Rocks

Course progressStage 7 of 10
~35 min
Before you start

Make sure you've finished Stage 6: Hidden Hazard Field.

Build

a ramp with rolling rocks and cover

Learn

how moving obstacles create timing pressure

Ship

a dodge section that rewards smart movement


Stage preview
Stage 7
Preview the rolling-rock ramp with cover spots that create a sprint-hide-sprint rhythm.

Preview the rolling-rock ramp with cover spots that create a sprint-hide-sprint rhythm.

Build this stage below

The big idea

Stage 5's fireballs flew across the path. Stage 6's hazards hid in plain sight. Today's hazard — rolling boulders — comes down the path the player is on. You aren't dodging across danger anymore. You're racing up the same lane as the danger.

The new design lesson is cover gives the player a beat. Without cover, the player would have to dash from the bottom to the top while boulders rain down — almost no game ever shipped this works. With one cover spot in the middle, the long climb breaks into two short ones. The player can stop, watch the pattern, and dash when it's safe. That stop-and-dash rhythm is what every action game uses to make hard sections feel fair.

The cover is a design tool, not a Roblox feature. It's just a wide flat block. The thing that makes it special is where you place it — between the danger and the goal, in a spot the player can reach in a single dash.

Build it

Step 1 — Build the ramp, cover, and spawner

A ramp with a cover spot halfway up. Boulders roll down. The player dodges up.

stage7Easy

Build this part

RockRamp

Wedge
Open recipe
Size
6 × 8 × 30
Color
Dark stone grey
Material
Slate
Anchored
✓ Yes
Place
Right in front of the Stage 7 checkpoint, sloping up. The X (6) is the width, Y (8) is the rise, Z (30) is the length.

Wedges have a flat bottom and a sloped top. If the slope points the wrong way, change Orientation Y in Properties.

Build this part

CoverBlock

Block
Open recipe
Size
3 × 4 × 1
Color
Reddish brown
Material
Wood
Anchored
✓ Yes
Place
Standing upright on the ramp, about halfway up, slightly off to one side so a player can duck behind it

The cover is the key design move. Without it, the ramp is a brutal dash. With it, the climb becomes 'sprint, hide, sprint.'

Build this part

BoulderSpawner

Block
Open recipe
Size
2 × 2 × 2
Color
Bright red
Material
Neon
Anchored
✓ Yes
Place
At the very top of RockRamp, slightly above its surface

Bright red + Neon while testing so you can see where it is. Later you can set Transparency = 1 in Properties to hide it from players — the spawner is mechanical, not part of the level.

Step 2 — Add the script that rolls boulders

The script makes the spawner create a boulder every 2 seconds and tween it smoothly down the ramp. Right-click BoulderSpawnerInsert ObjectScript. Delete the placeholder.

Paste into Script
Script

BoulderSpawner

The Script goes inside BoulderSpawner. The variables at the top are the tunable parts — see 'Tune it' below.

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

-- Tune these to change how the rocks feel
local spawnInterval = 2 -- seconds between boulders
local rollSeconds = 3 -- how long a boulder takes to reach the bottom
local boulderSize = 3 -- stud diameter of each boulder
local rampDrop = -8 -- studs down (negative = down)
local rampRun = -30 -- studs forward (negative = away from spawner)

local function spawnBoulder()
local boulder = Instance.new("Part")
boulder.Shape = Enum.PartType.Ball
boulder.Size = Vector3.new(boulderSize, boulderSize, boulderSize)
boulder.Color = Color3.fromRGB(80, 60, 50)
boulder.Material = Enum.Material.Slate
boulder.Position = spawner.Position
boulder.Anchored = true

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

boulder.Parent = workspace

local destination = spawner.Position + Vector3.new(0, rampDrop, rampRun)
local info = TweenInfo.new(rollSeconds, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)
local tween = TweenService:Create(boulder, info, { Position = destination })
tween:Play()

Debris:AddItem(boulder, rollSeconds + 1)
end

while true do
spawnBoulder()
wait(spawnInterval)
end

Press ▶ Play. A grey boulder spawns at the top of the ramp every 2 seconds and rolls smoothly down. Touch one and you respawn.

Tune it

The first five lines of the script are the variables you'll change most often. You can open the Script in Studio and tweak any of them:

  • spawnInterval = 2 — how often a boulder spawns. Bigger = more time to dash.
  • rollSeconds = 3 — how long the boulder takes to roll down. Smaller = faster boulder.
  • boulderSize = 3 — bigger boulders are harder to dodge.
  • rampDrop and rampRun — match these to your ramp's height and length if you build a longer or steeper ramp.

Try a few different values and feel the difference.

Step 2 — Add the next checkpoint

Stage 8 checkpoint goes at the top of the ramp — the reward for surviving the climb.

2.1 Add another SpawnLocation

In Explorer, right-click WorkspaceInsert ObjectSpawnLocation.

2.2 Set its properties

  • BrickColor → a new color.
  • AllowTeamChangeOnTouch → checked.
  • Neutral → unchecked.
  • TeamColor → matches the BrickColor.

2.3 Tag it with its stage number

Same gesture as Stage 1. In the Attributes section of Properties, add a StageNumber attribute (number type) and set its value to 8.

2.4 Add a Team

  • In Teams, insert a new Team named Stage 8.
  • Uncheck AutoAssignable.
  • Set TeamColor to match.

Drag the SpawnLocation onto the top of the ramp.

Understand it

The reason rolling boulders feel different from Stage 5's flying fireballs is the direction matters. A fireball crosses the player's path — they can stand still and let it pass. A boulder follows the player's lane — standing still doesn't help. Same kind of timing hazard, but now the player has to move through it. Game designers call this "lane-shared" danger, and it's the basic mechanic of every endless runner.

The cover spot is the smallest possible piece of level architecture. Roblox doesn't have a "cover" feature — we built it out of one regular block placed in one specific spot. Real action games are full of moments like this: a wall the player can crouch behind, a pillar that breaks line of sight. The idea of cover is what matters; the implementation is whatever part of the world you bend to that purpose.

The BoulderSpawner script has a row of tunable variables at the top (spawnInterval, rollSeconds, boulderSize). That's a real engineering move: the recipe for one boulder is the long part of the code, but the rhythm and feel live in a few short variables you can adjust without rewriting anything. Real game scripts are built that way on purpose — change a number, feel the difference, change it back.

Try this

Learning beat

Try this

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

Predict first

Delete the cover block and play through. Predict how it feels. Then put cover back and play again. How much harder was it without the cover? What does that tell you about how much work a single block can do in a level?

Compare

Try placing the cover block at the very bottom of the ramp instead of the middle. Does it still feel like cover? Why or why not? Where you place safety is as important as whether it exists.

Connect

Stage 8 swaps moving-down hazards for spinning-in-place hazards. Look at your ramp. What's harder to time: a danger that comes at you in a line, or a danger that spins in a circle? Why?

Test your stage

Before moving on:

  • Press ▶ Play and start from the Stage 7 checkpoint.
  • Wait behind cover and watch the boulder pattern.
  • Climb the ramp without getting knocked back.
  • Touch the Stage 8 checkpoint, reset, and confirm you respawn there.
  • Design check. Does the boulder timing feel fair, or does the player have to get lucky?

If it breaks

  • The BoulderSpawner doesn't roll any boulders. The Script is probably outside the spawner. In Explorer, the Script should be indented underneath BoulderSpawner. Also check Output (View → Output) for red errors — a typo in the script will be shown there.
  • Boulders roll up the ramp instead of down. Your ramp is angled the opposite way from the script's assumption. Either rotate the ramp so its low end points away from BoulderSpawner, or open the Script and change rampRun = -30 to rampRun = 30 (flip the sign).
  • The boulders feel impossible to dodge. Open the Script and bump spawnInterval from 2 to 3 or 4. Bigger number = more time between boulders = more time to react. You can also drop boulderSize to make them smaller.
  • The cover spot is too small to hide behind. Resize it. Make it [6, 4, 3] or whatever fits — the cover needs to be at least as tall as a Roblox character to actually block boulders.
  • My boulders go right through the cover. Cover block's CanCollide is unchecked. Click the cover, find CanCollide in Properties, check it.