๐Ÿ‘ป My First Roblox Studio Game ยท Episode 5 of 6 ยท See All Episodes
๐Ÿ‘ป Episode 5 ยท Ages 10+ ยท NPC AI ยท Timer

Haunted Escape!
Horror Maze Game

Build a dark haunted maze collect 3 keys hidden in the darkness, avoid a ghost that chases you, and escape through the locked door before the timer runs out!

โš ๏ธ Ages 10+ โฑ๏ธ ~1.5 Hours ๐ŸŸฆ Roblox Studio โœ“ Free
โญ
0 XP
Level 1
๐Ÿ”ฅ0
Your Progress0 / 11 steps
1
๐ŸŒ‘
Build the Dark Maze
Construct a creepy maze with dark stone walls
Active
๐ŸŽฏ
Goal for this step

Build a maze of corridors using dark stone parts then make it pitch black!

๐Ÿ‘จโ€๐Ÿ‘ง
Parent note: Open Roblox Studio โ†’ Baseplate template. This episode is best for ages 10+ as the ghost AI and timer add complexity. Take breaks if needed!

Create the maze layout

  • 1Delete the default Baseplate. Add a new Part (size 100, 1, 100), BrickColor Really black, Material SmoothPlastic. This is the maze floor. Name it Floor.
  • 2Build maze walls using Parts (size 20, 8, 2 or 2, 8, 20), BrickColor Dark stone grey, Material Brick. All Anchored = true. Arrange them to form corridors dead ends, turns and open rooms.
  • 3Build at least 5 corridors with a few dead ends. Make it feel genuinely maze-like!
  • 4Add a SpawnLocation near one edge of the maze.
  • 5Add an outer wall around the whole maze so players can't leave.

Make it dark

  • 6Click Lighting in Explorer. Set Brightness to 0. Set Ambient to (0, 0, 0) โ€” pure black. Set OutdoorAmbient to (0, 0, 0) too. The world goes completely dark!
  • 7Now add a PointLight to the player. We'll do this in a later step for now just check it's dark by pressing Play.
๐Ÿ’ก
A good maze has 1 correct path to the exit and multiple dead ends. Sketch it on paper first if that helps!
โœ๏ธ
Fill in the Blanks
+15 XP
To make the world completely dark, set to 0 in the service.
โšก
True or False?
+15 XP
All maze wall parts should have Anchored set to true.
Setting Ambient to (255, 255, 255) makes the world dark.
A SpawnLocation determines where the player appears in the game.
๐Ÿง 
Knowledge Check
+15 XP
Which Lighting properties must be set to (0, 0, 0) to make the world pitch black?
ABrightness and FogEnd
BTimeOfDay and ClockTime
CAmbient and OutdoorAmbient
2
๐Ÿ•ฏ๏ธ
Player Torch
Give the player a small torch so they can see a little bit
Locked
๐ŸŽฏ
Goal for this step

Add a dim PointLight to the player's character so they can see a short distance around them.

  • 1In StarterCharacterScripts (inside StarterPlayer in Explorer), add a LocalScript. Name it PlayerTorch. Paste:
LocalScript โ€” PlayerTorch
local character = script.Parent local root = character:WaitForChild("HumanoidRootPart") local light = Instance.new("PointLight") light.Brightness = 3 light.Range = 14 light.Color = Color3.fromRGB(255, 220, 160) -- warm candlelight light.Parent = root
๐Ÿ“–
Range 14 means the player can see about 14 studs around them enough to spot the ghost before it reaches them, but not enough to see the whole maze!
๐Ÿ’ป
Code Challenge
+20 XP
Fill in the blanks to create a PointLight on the player:
local light = Instance.new("") light.Brightness = 3 light.Range = light.Parent =
๐Ÿ’ก Hint: We need a PointLight with Range 14 parented to the HumanoidRootPart (stored in "root").
๐Ÿง 
Knowledge Check
+15 XP
Why do we parent the PointLight to HumanoidRootPart instead of the Workspace?
ABecause Workspace doesn't support lights
BSo the light follows the player as they move
CBecause PointLights only work inside scripts
3
๐Ÿ—๏ธ
Hidden Keys
Place 3 glowing keys that players must collect to escape
Locked
๐ŸŽฏ
Goal for this step

Create 3 glowing keys hidden in dead ends. Touching them adds to your key count.

  • 1In ServerScriptService, add a Script named GameSetup:
GameSetup Script
game.Players.PlayerAdded:Connect(function(player) local stats = Instance.new("Folder") stats.Name = "leaderstats" stats.Parent = player local keys = Instance.new("IntValue") keys.Name = "Keys" keys.Value = 0 keys.Parent = stats end)
  • 2Add 3 small Parts (size 1, 2, 0.5) scattered in dead ends. BrickColor Bright yellow, Material Neon. Name them Key1, Key2, Key3. Anchored = true.
  • 3Add a Script inside each key (same script for all 3):
Script (inside each Key)
local key = script.Parent local collected = false key.Touched:Connect(function(hit) if collected then return end local player = game.Players:GetPlayerFromCharacter(hit.Parent) if not player then return end collected = true player.leaderstats.Keys.Value += 1 key.Transparency = 1 key.CanCollide = false end)
๐Ÿ’ก
When collected the key goes invisible. The key count shows on the leaderboard. Keys: 3/3 means the door can open!
โœ๏ธ
Fill in the Blanks
+15 XP
The keys use Material so they glow in the dark. We store the key count inside a folder called .
๐Ÿ’ป
Code Challenge
+20 XP
Fill in the blanks to make a key disappear when touched:
collected = true player.leaderstats.Keys.Value += key. = 1 key.CanCollide = false
๐Ÿ’ก Hint: Setting Transparency to 1 makes something invisible. We add 1 key each time.
๐Ÿง 
Knowledge Check
+15 XP
Why do we use a collected boolean variable in the key script?
ATo stop the key from being collected more than once
BTo make the key spin
CTo change the key's colour
4
๐Ÿšช
The Escape Door
A locked door that only opens when all 3 keys are collected
Locked
๐ŸŽฏ
Goal for this step

Create a glowing red door that only lets players through once they have all 3 keys.

  • 1Add a Part (size 6, 8, 1) as the door. BrickColor Bright red, Material Neon. Name it EscapeDoor. Anchored = true. Place it at one end of the maze as the exit.
  • 2Add a BillboardGui label: ๐Ÿšช Collect all 3 keys!.
  • 3Add a Script inside EscapeDoor:
Script (inside EscapeDoor)
local door = script.Parent door.Touched:Connect(function(hit) local player = game.Players:GetPlayerFromCharacter(hit.Parent) if not player then return end local keys = player.leaderstats.Keys.Value if keys >= 3 then door.Transparency = 0.8 door.CanCollide = false door.BrickColor = BrickColor.new("Bright green") print(player.Name .. " ESCAPED!") end end)
๐Ÿ”ฎ
Predict What Happens
+15 XP
A player has collected 2 out of 3 keys and walks into the EscapeDoor.

What happens?
AThe door opens because 2 keys is close enough
BThe game crashes
CNothing happens โ€” the door stays solid because keys < 3
๐Ÿง 
Knowledge Check
+15 XP
What happens to the door when all 3 keys are collected and the player touches it?
AIt deletes itself from the game
BIt goes transparent, loses collision, and turns green
CIt teleports the player to a new level
5
๐Ÿ‘ป
The Ghost!
A semi-transparent ghost that chases the player through the maze
Locked
๐ŸŽฏ
Goal for this step

Create a glowing ghost that slowly follows players around the maze.

๐Ÿ‘จโ€๐Ÿ‘ง
Parent note: The ghost uses PathfindingService to navigate the maze automatically. This is the most advanced script in the series copy it carefully and it will work!

Build the ghost

  • 1Add a Part (size 3, 4, 3). Right-click โ†’ Insert Object โ†’ SpecialMesh, MeshType Sphere. BrickColor Institutional white, Material Neon, Transparency 0.5. Name it Ghost. Do NOT anchor it.
  • 2Place it somewhere in the maze, away from the spawn.
  • 3Add a Script inside Ghost:
Script (inside Ghost)
local ghost = script.Parent local Players = game:GetService("Players") local RunService = game:GetService("RunService") ghost.Anchored = false ghost.CanCollide = false local speed = 8 -- studs per second (increase to make harder) RunService.Heartbeat:Connect(function(dt) local target = nil local closest = math.huge for _, player in Players:GetPlayers() do local char = player.Character if char and char:FindFirstChild("HumanoidRootPart") then local dist = (ghost.Position - char.HumanoidRootPart.Position).Magnitude if dist < closest then closest = dist target = char.HumanoidRootPart end end end if target then local dir = (target.Position - ghost.Position).Unit ghost.CFrame = ghost.CFrame + dir * speed * dt -- Kill on touch if closest < 3 then local hum = target.Parent:FindFirstChildWhichIsA("Humanoid") if hum then hum.Health = 0 end end end end)
๐Ÿ“–
The ghost floats straight toward the nearest player. Because CanCollide is false it passes through walls making it properly scary! Change speed = 8 to make it faster or slower.
โšก
True or False?
+15 XP
The ghost part should have Anchored set to true so it stays in place.
Setting CanCollide to false lets the ghost pass through walls.
The ghost kills the player when it gets within 3 studs.
๐Ÿ’ป
Code Challenge
+20 XP
Fill in the blanks to make the ghost move toward the player:
local dir = (target.Position - ghost.Position). ghost.CFrame = ghost.CFrame + dir * * dt
๐Ÿ’ก Hint: .Unit normalises a vector to length 1. We multiply by the speed variable and delta time.
๐Ÿง 
Knowledge Check
+15 XP
What does .Magnitude measure in the ghost script?
AThe ghost's speed
BThe ghost's size
CThe distance between the ghost and the player
6
โฑ๏ธ
Countdown Timer
A 3-minute timer โ€” escape before it hits zero!
Locked
๐ŸŽฏ
Goal for this step

Show a countdown timer on screen. If it reaches zero, the player dies.

  • 1In StarterGui, add a ScreenGui named TimerGui. Add a TextLabel at the top of the screen. Font GothamBold, TextSize 32, TextColor white, BackgroundTransparency 1. Name it TimerLabel.
  • 2Add a LocalScript inside TimerGui:
LocalScript (TimerGui)
local player = game.Players.LocalPlayer local label = script.Parent.TimerLabel local timeLeft = 180 -- 3 minutes while timeLeft > 0 do local mins = math.floor(timeLeft / 60) local secs = timeLeft % 60 label.Text = "โฑ๏ธ " .. mins .. ":" .. string.format("%02d", secs) if timeLeft <= 30 then label.TextColor3 = Color3.fromRGB(255, 50, 50) -- goes red end task.wait(1) timeLeft -= 1 end -- Time's up! label.Text = "๐Ÿ’€ TIME'S UP!" local char = player.Character if char then local hum = char:FindFirstChildWhichIsA("Humanoid") if hum then hum.Health = 0 end end
๐Ÿ”ข
Put It In Order
+15 XP
Click these in the correct order to set up the countdown timer:
Add a LocalScript inside TimerGui
Add a ScreenGui named TimerGui to StarterGui
Add a TextLabel at the top of the screen
Test that the timer counts down and turns red at 30 seconds
๐Ÿง 
Knowledge Check
+15 XP
What happens when the countdown timer reaches zero?
AThe player's Humanoid Health is set to 0 (they die)
BThe timer restarts from 180
CThe ghost disappears
7
๐Ÿ”‘
Key Counter GUI
Show how many keys have been collected on screen
Locked
๐ŸŽฏ
Goal for this step

Display ๐Ÿ—๏ธ 0/3 on screen that updates as keys are collected.

  • 1In StarterGui, add a ScreenGui named KeysGui. Add a TextLabel bottom-left, size {0.2,0},{0.06,0}, position {0.02,0},{0.9,0}. Text: ๐Ÿ—๏ธ 0 / 3. Font GothamBold. BackgroundTransparency 1. TextColor white.
  • 2Add a LocalScript inside KeysGui:
LocalScript (KeysGui)
local player = game.Players.LocalPlayer local label = script.Parent.TextLabel local keys = player:WaitForChild("leaderstats"):WaitForChild("Keys") local function update(val) label.Text = "๐Ÿ—๏ธ " .. val .. " / 3" if val >= 3 then label.Text = "โœ… All keys! Find the door!" label.TextColor3 = Color3.fromRGB(0, 255, 100) end end update(keys.Value) keys.Changed:Connect(update)
๐Ÿ’ป
Code Challenge
+20 XP
Fill in the blanks to update the key counter label:
local function update(val) label.Text = "๐Ÿ—๏ธ " .. val .. " / " if val >= then label.Text = "โœ… All keys! Find the door!" end end
๐Ÿ’ก Hint: There are 3 keys total in the maze.
๐Ÿ”ฎ
Predict What Happens
+15 XP
The player collects the 3rd and final key.

What does the key counter label show?
A๐Ÿ—๏ธ 3 / 3
Bโœ… All keys! Find the door!
CThe label disappears
๐Ÿง 
Knowledge Check
+15 XP
What does keys.Changed:Connect(update) do?
AIt changes the value of keys
BIt deletes old keys
CIt runs the update function every time the Keys value changes
8
๐Ÿ”Š
Spooky Sounds
Add creepy ambient sounds and a ghost scream jumpscare
Locked
๐ŸŽฏ
Goal for this step

Add eerie background music and a sound when the ghost gets close.

  • 1In Workspace, add a Sound. Name it HorrorMusic. Set SoundId to a free horror ambient audio from the Toolbox (search "horror ambient"). Set Looped = true, Volume = 0.4.
  • 2In ServerScriptService, add a Script named MusicPlay: workspace.HorrorMusic:Play()
  • 3Add another Sound named GhostScream inside the Ghost part. Find a scream/ghost sound from the Toolbox. Set Volume = 1.
  • 4In the Ghost's script, inside the if closest < 3 then block, add ghost.GhostScream:Play() before killing the player.
๐Ÿ˜ฑ
Warn your family before they play the ghost scream is genuinely scary when it appears from the dark!
โœ๏ธ
Fill in the Blanks
+15 XP
The background horror music has set to true so it plays continuously, and set to 0.4 so it doesn't overpower the ghost scream.
๐Ÿง 
Knowledge Check
+15 XP
Where should the GhostScream sound object be placed?
AIn Workspace directly
BInside the Ghost part
CIn StarterGui
9
๐ŸŽจ
Atmosphere & Decoration
Add candles, fog and spider webs to set the mood
Locked
๐ŸŽฏ
Goal for this step

Make the haunted maze feel genuinely spooky with atmospheric effects and decorations.

๐Ÿ‘จโ€๐Ÿ‘ง
Parent note: Free creative time let your child decide how scary they want to make it!
  • 1Fog: In Lighting โ†’ Insert Object โ†’ Atmosphere. Set Density to 0.5, Color to a dark purple. Spooky fog fills the maze!
  • 2Candles: Add small cylinder parts as candles. Insert a Fire effect into each (right-click โ†’ Insert Object โ†’ Fire). Set Fire size to 1 and Heat to 3 for small flames.
  • 3Flickering lights: Add PointLights inside candle flames with orange colour and low Range (8). This creates pools of light in the darkness.
  • 4Paintings: Add flat Parts as picture frames on walls. Use BillboardGui with emoji text like ๐Ÿ‘๏ธ or ๐Ÿ’€ on them.
  • 5Coffins: Build a coffin shape from a few parts and scatter them around. Great for dead ends!
โœ๏ธ
Fill in the Blanks
+15 XP
To create spooky fog, you insert an object into Lighting, and to make a candle glow you insert a effect into the flame part.
โšก
True or False?
+15 XP
A higher Density on the Atmosphere makes the fog thicker and the maze harder to see through.
A PointLight with a low Range creates a small pool of light around a candle.
Decorations like coffins and picture frames change how the game runs in code.
๐Ÿง 
Knowledge Check
+15 XP
Which object do you add to Lighting to fill the whole maze with spooky fog?
AA PointLight
BAn Atmosphere
CA Fire effect
10
๐Ÿ†
Win & Lose Screens
Show a victory screen on escape or a game-over screen on death
Locked
๐ŸŽฏ
Goal for this step

Add proper win/lose feedback so players know the outcome clearly.

  • 1In ReplicatedStorage, add a RemoteEvent named WinEvent.
  • 2In the EscapeDoor script, after the door opens, add: game.ReplicatedStorage.WinEvent:FireClient(player).
  • 3In StarterGui, add a ScreenGui named WinScreen. Add a full-screen Frame (dark green, transparency 0.3). Add a big TextLabel: ๐ŸŽ‰ YOU ESCAPED!. Set Visible = false.
  • 4Add a LocalScript in WinScreen:
LocalScript (WinScreen)
game.ReplicatedStorage:WaitForChild("WinEvent").OnClientEvent:Connect(function() script.Parent.Frame.Visible = true end)
๐Ÿ”ข
Put It In Order
+15 XP
Click these in the right order to make the win screen appear when the player escapes:
The WinScreen LocalScript listens with OnClientEvent and shows the Frame
Add a RemoteEvent named WinEvent to ReplicatedStorage
The EscapeDoor script fires WinEvent to the player when the door opens
The player sees the ๐ŸŽ‰ YOU ESCAPED! screen
๐Ÿง 
Knowledge Check
+15 XP
Why does the server use WinEvent:FireClient(player) instead of just showing the screen itself?
ABecause servers are not allowed to use RemoteEvents
BGUIs live on the player's screen, so the server tells that client to show its own WinScreen
CFireClient makes the door open faster
11
๐ŸŒ
Test & Publish!
Playthrough and share your haunted maze
Locked
๐ŸŽฏ
Goal for this step

Play through the whole game, adjust difficulty, then publish!

  • 1Press Play. Navigate the dark maze. Find keys, avoid the ghost, escape through the door.
  • 2If the ghost is too fast, lower its speed. If the maze is too easy, add more dead ends.
  • 3Try setting the ghost speed to 6 for a fun challenge and 10 for heart-pounding terror!
  • 4Press Stop โ†’ File โ†’ Publish to Roblox โ†’ Public.
  • 5Dare your friends and family to escape your haunted maze!
๐Ÿ‘ป
Watch someone else play your maze for the first time their reactions when the ghost appears are priceless!
๐Ÿ”ฎ
Predict What Happens
+15 XP
During testing the ghost catches the player too easily. You change the ghost's speed from 10 down to 6.

What happens when you test again?
AThe ghost moves slower, so the maze becomes easier and less scary
BThe ghost disappears completely
CThe keys stop spawning
๐Ÿง 
Final Knowledge Check
+15 XP
Before you publish, what's the best way to make sure your haunted maze is fun?
APublish straight away without testing
BDelete the ghost so nobody loses
CPlay through it, then adjust the ghost speed and maze difficulty until it feels right
๐ŸŽ‰๐Ÿ‘ป๐Ÿ—๏ธ๐Ÿšช๐Ÿ˜ฑ
You Built a Horror Game!

Phenomenal โ€” a dark maze, chasing ghost, key hunting, countdown timer and proper win/lose screens. One more episode to go the big RPG adventure!

๐Ÿ—บ๏ธ Episode 6: Island Quest RPG โ†’ โญ View My Progress & Certificates

This workshop was free and took many hours to build. If it helped you learn something new, consider supporting the project.

☕ Support on Ko-fi