FFORGEBLOX

Tutorial · 10 min read · Updated October 2026

How to make a Roblox horror game with AI, step by step

A good Roblox horror game is mostly three things: a map that is small and dark enough to get lost in, a monster that feels like it is hunting you rather than following a rail, and pacing that keeps you waiting for the scare. AI can build all of the systems. This guide gives you the message sequence that does it inside your own Roblox Studio place, and points out the parts you still have to tune by playing.

Before you start

Message 1: the map

Premise: night shift in an abandoned hospital. Build Workspace/Map: a start
room, two corridors with 6 patient rooms, a stairwell, a basement generator
room and a locked Exit door. Tight corridors (8 studs wide), doors that open
on ProximityPrompt, some rooms dead-ends. Use Creator Store furniture with no
scripts inside the assets. Put invisible PatrolPoint parts in every room and
corridor. Screenshot from above with lighting at full bright.

Small and confusing beats big and empty. If the map takes more than two minutes to walk end to end, ask the agent to cut it down; a monster has to be able to find the player.

Message 2: lighting, fog, sound and a flashlight

Set the mood. Lighting: ClockTime 0, Brightness 0.5, Ambient and
OutdoorAmbient near black, an Atmosphere with Density 0.45 and a cold colour,
ColorCorrection with lower saturation. A few flickering ceiling lights
(client-side flicker, random timing). Ambient hum and distant sounds from the
Creator Store at low volume. Give every player a flashlight: SpotLight on the
character, toggled with F or a mobile button, battery that drains and
recharges slowly, shown on the HUD. Playtest, screenshot a corridor with the
flashlight on and off.

Check the screenshot honestly. If you can see the end of the corridor without the flashlight, it is too bright.

Message 3: the monster patrols

Add a monster in ServerStorage/Monster (R15 rig, slow walk speed 10). A
server script spawns it in the basement, sets network owner of its
HumanoidRootPart to the server, and patrols: pick a random PatrolPoint, walk
there with PathfindingService (recompute if blocked), pause 2-5s, look around,
repeat. Never pick the same point twice in a row. Footstep sounds that
players can hear through walls. Playtest 60s, screenshot, read the console.

Random patrol targets and pauses are what make it feel alive. A monster on a fixed loop is learnable in one round, and learnable is not scary.

Message 4: sight, hearing and the chase

Give the monster senses on the server. Sight: every 0.2s raycast from its
head to each player's HumanoidRootPart within 60 studs and a 100 degree
cone; walls block it. Hearing: sprinting players make noise within 30 studs;
the monster goes to investigate the last noise position. Chase: on sight,
speed 18 (player sprint is 20, walk 12), recompute the path every 0.5s; if it
has not seen the player for 4s, investigate the last seen position, then
return to patrol. Touching a player ends their run (jumpscare on that client
only, then respawn in the start room). Playtest, get chased, screenshot.

The line-of-sight check the agent writes should look like this. The important parts are excluding the monster itself from the raycast and confirming the hit is the player, not a wall in between:

local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { monster }

local function canSee(head: BasePart, character: Model): boolean
	local target = character:FindFirstChild("HumanoidRootPart")
	if not target then return false end
	local offset = target.Position - head.Position
	if offset.Magnitude > 60 then return false end
	-- inside the vision cone? (cos 50 degrees = half of a 100 degree cone)
	if head.CFrame.LookVector:Dot(offset.Unit) < math.cos(math.rad(50)) then
		return false
	end
	local hit = workspace:Raycast(head.Position, offset, params)
	return hit ~= nil and hit.Instance:IsDescendantOf(character)
end

And the chase step, recomputed often because the player keeps moving:

local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath({ AgentRadius = 2.5, AgentHeight = 6, AgentCanJump = false })

local function stepToward(humanoid: Humanoid, root: BasePart, goal: Vector3)
	local ok = pcall(function() path:ComputeAsync(root.Position, goal) end)
	if not ok or path.Status ~= Enum.PathStatus.Success then
		humanoid:MoveTo(goal) -- fall back to a straight line
		return
	end
	local waypoints = path:GetWaypoints()
	-- walk only the first few waypoints, then the caller recomputes
	for i = 2, math.min(#waypoints, 4) do
		humanoid:MoveTo(waypoints[i].Position)
		humanoid.MoveToFinished:Wait()
	end
end

Speed is the main tuning knob. The monster should be slower than a sprint and faster than a walk, so escaping costs stamina and hiding is a real choice. If the chase feels unfair, lower the monster speed by 1 or 2 before changing anything else.

Message 5: objectives and the exit

Add objectives: 3 fuses hidden in random rooms each round (from a list of 8
spawn spots), collected with a ProximityPrompt and tracked on the server per
round. Placing all 3 in the generator turns on the lights in the stairwell and
unlocks the Exit door. Reaching the exit shows a win screen with the time.
Objective text on the HUD ("Fuses 1/3"). Playtest a full round, screenshot.

Message 6: pacing and scares

Add pacing on the server: for the first 45s of a round the monster stays in
the basement. Add 4 one-time scripted moments triggered by entering zones (a
door slam, lights dying in one corridor, a distant scream, the monster
crossing the end of a corridor and leaving). Each fires at most once per round
and never within 20s of another. Keep the music silent except during a chase,
when a chase track fades in. Playtest, summarise when each scare fired.

The most common mistake, by people and by AI, is too many scares. Long quiet stretches are what make the rare moment land.

Message 7: mobile, lobby and multiplayer check

Add touch buttons for Sprint and Flashlight, scale-based, ScreenInsets
DeviceSafeInsets, and a stamina bar. Add a small lobby with a Start button
that teleports the party into the map together. Check the iPhone preset in the
device emulator and run Test ▸ Clients and Servers with 2 players: confirm
the monster chases whoever it sees and the jumpscare only plays for the
caught player. Screenshot both clients.

Want the HUD, stamina bar and win screen to match your art direction? Attach screenshots or mockups of the look you want; the Roblox UI guide covers the rest.

What it costs

On Sonnet 5.5 a build step typically costs 20–150 credits ($0.20–$1.50). This sequence is in the same range as the tycoon guide, roughly 2,500–6,000 credits, and more if you iterate on the monster a lot (most people do). Every message shows its price before you send it; see the pricing page. If the chase logic gets stubborn, switching that one message to Opus 5.5 is often cheaper than several rounds of fixes.

Before you publish

  1. Play five full rounds yourself and with a friend. Note every moment you were bored and every death that felt unfair.
  2. Complete the Maturity and Compliance questionnaire in the Creator Dashboard. Horror themes such as jump scares affect your game's content label; answer honestly.
  3. Make the thumbnail dark but readable at small size, and put "horror" in the first sentence of the description.
  4. Publish private first and play it on your phone in a dark room. That is how many of your players will.

Questions

Can AI make a Roblox horror game?

An agent working inside Studio can build the map, lighting, flashlight, monster AI, objectives and UI, and playtest them. Fear comes from pacing, sound and restraint, which you judge by playing it yourself.

How do I make a Roblox monster chase the player?

Run it on the server. Use PathfindingService to compute a path to the player, walk the first few waypoints with Humanoid:MoveTo, and recompute about every half second while chasing. Use a raycast for line of sight and return to patrol after a few seconds without seeing the player.

Why is my Roblox monster jittery?

Usually a player client has network ownership of it. Call SetNetworkOwner(nil) on its HumanoidRootPart so the server simulates it.

Do horror games need a content maturity label on Roblox?

Every published experience should complete the Maturity and Compliance questionnaire, and horror themes affect the label your game gets. Answer it honestly before you publish.

Related guides

Build the map and the monster in your own Studio. Paste Message 1 and get a screenshot back. Free trial credits, no card. Open ForgeBlox →