Game genres

How to make a killer vs survivors game in Roblox Studio

In a killer vs survivors game, one player hunts and everyone else tries to get out. It is the most lopsided setup in multiplayer, and that is the appeal: being the killer feels powerful, surviving as a group feels clever. It is also hard to balance, because one side has one player and the other has many. This guide covers the loop, the systems, a build plan, a fair killer pick and an objective system in Luau, and how to keep both sides fun.

Updated 26 October 20268 min readGenre guide

In short

An asymmetric game is a round with two roles. The server picks one killer, preferably with a chance that grows each round a player is not picked, and puts everyone else on the Survivors team with a head start. Survivors complete objectives, such as repairing generators with a hold prompt, which open an exit. The killer wins by taking every survivor out before the exit opens or the timer ends. Dead survivors spectate, and the next round starts after a short intermission.

  • Balance is three numbers: killer speed against survivor speed, how long objectives take, and how big the map is. Change one at a time.
  • Do every hit check on the server. A killer's attack that only the client checks can be fired from anywhere.
  • Give dead survivors something to do, or they leave and the next round has fewer players.
On this page
  1. What makes it work
  2. Systems
  3. Build plan
  4. Round code
  5. Objective code
  6. Balance
  7. Monetization
  8. Retention
  9. With RoCode
  10. Questions
  11. Sources

What makes a killer vs survivors game work

The loop in one line: survivors split up to finish objectives while the killer hunts them; the round ends in an escape or a wipe. Each round is a short story with a beginning (the head start), a middle (chases) and an end (the exit opening or the last survivor caught).

What each side does, by time scale
Time scaleSurvivorKiller
Every 10 secondsRepairs, listens, watches for the killer, runsSearches, follows sounds and tracks, attacks
Every minuteA generator finished, a chase escaped, a teammate savedA survivor downed, a generator area patrolled
Every round (a few minutes)Escape or die, then spectateWipe the group, or lose them at the exit
Every sessionCurrency for new survivors and skinsCurrency for new killers and abilities

The games that last get three things right. First, both roles are fun: if the killer always wins, survivors quit, and if the killer always loses, nobody wants the role. Second, the killer pick feels fair: players who wanted to be killer for ten rounds and never were will leave. Third, chases have counterplay: obstacles survivors can loop around or block, places to hide, and abilities with cooldowns, so a chase is a contest instead of a straight line the faster player always wins.

The systems you need

Killer vs survivors systems and where to learn each one
SystemWhat it doesRoblox piecesGuide
RoundsIntermission, map, round timer, resultsScript, workspace attributesRound system
Roles and teamsPicks the killer, puts others on SurvivorsTeams, Team, SpawnLocationTeam system
Killer attackA melee hit checked on the serverTool, server hitbox, Humanoid:TakeDamageCombat, Sword
ObjectivesGenerators repaired with a hold promptProximityPrompt, CollectionService, attributesThis page
ExitOpens when objectives are doneParts, TouchedThis page
Stamina and sprintShort bursts of speed for chasesHumanoid.WalkSpeed, UserInputServiceSprint
Health and downsHealth bars, a downed stateHumanoid, BillboardGuiHealth bar
SpectatingDead survivors watch the livingCamera.CameraSubject, LocalScriptThis page
Unlocks and savingCurrency for new characters and skinsDataStoreServiceDataStore tutorial

Build plan: from a baseplate to launch

First playable (greybox)

  1. In the Teams service add three Team objects: Lobby (AutoAssignable on), Survivors and Killer (AutoAssignable off), each with its own TeamColor.
  2. Build a lobby and one map from grey parts: walls with gaps to loop around, a few rooms, four generator parts tagged Generator and a part named ExitGate. Add a SpawnLocation for each team with Neutral off and the matching TeamColor.
  3. Add the two scripts below: the round with the killer pick, and the generators with the exit.
  4. Give the killer a melee tool from the sword guide, with the hit checked on the server.
  5. Play a local server test with 4 players in the Test tab. Check the round ends when the killer wins, when survivors escape and when the killer leaves.

First public test

  1. Add a round timer on screen, a results screen and spectating for dead survivors.
  2. Add sprint with stamina for survivors and a slightly faster base speed for the killer.
  3. Publish and watch win rates by role over many rounds. If one side wins far more often, adjust one number and test again.

Before launch

  1. Two or three killers with one ability each (a dash, a trap, a short reveal of survivors) and survivors with one ability each (a heal, a speed burst, a decoy), all on server-checked cooldowns.
  2. A second map, chase music that gets louder near the killer, and a currency earned every round.
  3. Saving, a shop for characters and skins, and AFK handling so an idle killer does not ruin a round.

A fair killer pick and the round

Each player has a KillerChance attribute starting at 1. Every round they are not picked, it goes up by 1; the killer's resets to 1. The pick is weighted by that number, so it is still random, but a player who has waited six rounds is far more likely to be next than one who just played killer. Survivors spawn first, and the killer waits in the lobby for the head start.

Script in ServerScriptService named KillerRound
-- Script in ServerScriptService named KillerRound.
-- Picks a killer with a chance that grows each round you are not picked, gives
-- survivors a head start, and ends when no survivor is left in the round.
local CollectionService = game:GetService("CollectionService")
local Players = game:GetService("Players")
local Teams = game:GetService("Teams")

local ROUND_SECONDS = 240
local HEAD_START = 15
local killerTeam = Teams:WaitForChild("Killer") :: Team
local survivorTeam = Teams:WaitForChild("Survivors") :: Team
local lobbyTeam = Teams:WaitForChild("Lobby") :: Team

local function chance(p: Player): number
	return tonumber(p:GetAttribute("KillerChance")) or 1
end

local function pickKiller(players: { Player }): Player
	local total = 0
	for _, p in players do
		total += chance(p)
	end
	local roll = math.random() * total
	for _, p in players do
		roll -= chance(p)
		if roll <= 0 then
			return p
		end
	end
	return players[#players]
end

local function playRound(players: { Player })
	workspace:SetAttribute("Escaped", 0)
	workspace:SetAttribute("ExitOpen", false)
	for _, gen in CollectionService:GetTagged("Generator") do
		gen:SetAttribute("Progress", 0)
	end
	local killer = pickKiller(players)
	for _, p in players do
		if p == killer then
			p:SetAttribute("KillerChance", 1)
			p.Team = lobbyTeam -- the killer waits in the lobby during the head start
		else
			p:SetAttribute("KillerChance", chance(p) + 1)
			p.Team = survivorTeam
			p:LoadCharacterAsync() -- spawns at the team's SpawnLocation
			local humanoid = p.Character and p.Character:FindFirstChildOfClass("Humanoid")
			if humanoid then
				humanoid.Died:Connect(function()
					p.Team = lobbyTeam -- out of the round: respawns in the lobby to watch
				end)
			end
		end
	end
	workspace:SetAttribute("Status", "Hide! The killer arrives in " .. HEAD_START .. " seconds")
	task.wait(HEAD_START)
	killer.Team = killerTeam
	killer:LoadCharacterAsync()
	local endsAt = os.clock() + ROUND_SECONDS
	while os.clock() < endsAt and #survivorTeam:GetPlayers() > 0 and killer.Parent do
		workspace:SetAttribute("TimeLeft", math.ceil(endsAt - os.clock()))
		task.wait(1)
	end
	local escaped = tonumber(workspace:GetAttribute("Escaped")) or 0
	local survived = escaped + #survivorTeam:GetPlayers()
	workspace:SetAttribute("Status", if survived > 0 then survived .. " survived" else "The killer wins")
	for _, p in Players:GetPlayers() do
		p.Team = lobbyTeam
		p:LoadCharacterAsync()
	end
end

while true do
	task.wait(20) -- intermission
	local players = Players:GetPlayers()
	if #players >= 2 then
		playRound(players)
	end
end

A survivor who dies moves to the Lobby team, so they respawn there to spectate, and the round loop only has to count the Survivors team. The round ends when that team is empty (everyone escaped or died), the timer runs out, or the killer leaves the game. For the intermission screen and map voting, the round system guide goes further.

Screenshot from a Roblox Studio playtest: the round timer bar from RoCode's candy UI kit
Studio screenshot. The round timer screen RoCode can add from its UI kits, in the candy theme. Point it at the TimeLeft attribute the round script sets.

Generators and the exit

Each generator takes 10 holds of 1.5 seconds, so a lone survivor needs at least 15 seconds of uninterrupted work, and the killer has time to notice. Letting go only loses the current hold. Only players on the Survivors team can repair, and when every generator reaches 100 the exit opens.

Script in ServerScriptService named Generators
-- Script in ServerScriptService named Generators.
-- Survivors repair parts tagged "Generator" a step at a time. When every one is
-- done, the part named ExitGate opens and survivors who touch it escape.
local CollectionService = game:GetService("CollectionService")
local Players = game:GetService("Players")
local Teams = game:GetService("Teams")

local STEP = 10 -- progress per finished hold, out of 100
local survivorTeam = Teams:WaitForChild("Survivors") :: Team
local lobbyTeam = Teams:WaitForChild("Lobby") :: Team
local exitGate = workspace:WaitForChild("ExitGate") :: BasePart

local function allDone(): boolean
	for _, gen in CollectionService:GetTagged("Generator") do
		if (tonumber(gen:GetAttribute("Progress")) or 0) < 100 then
			return false
		end
	end
	return true
end

local function setUpGenerator(gen: Instance)
	if not gen:IsA("BasePart") then
		return
	end
	local prompt = Instance.new("ProximityPrompt")
	prompt.ActionText = "Repair"
	prompt.HoldDuration = 1.5 -- let go and you lose only the current step
	prompt.Parent = gen
	prompt.Triggered:Connect(function(player: Player)
		local progress = tonumber(gen:GetAttribute("Progress")) or 0
		if player.Team ~= survivorTeam or progress >= 100 then
			return -- only survivors repair, and only unfinished generators
		end
		gen:SetAttribute("Progress", math.min(progress + STEP, 100))
		if allDone() then
			workspace:SetAttribute("ExitOpen", true)
		end
	end)
end

for _, gen in CollectionService:GetTagged("Generator") do
	setUpGenerator(gen)
end
CollectionService:GetInstanceAddedSignal("Generator"):Connect(setUpGenerator)

exitGate.Touched:Connect(function(hit: BasePart)
	local character = hit.Parent
	local player = if character and character:IsA("Model") then Players:GetPlayerFromCharacter(character) else nil
	if player and player.Team == survivorTeam and workspace:GetAttribute("ExitOpen") == true then
		player.Team = lobbyTeam
		workspace:SetAttribute("Escaped", (tonumber(workspace:GetAttribute("Escaped")) or 0) + 1)
		player:LoadCharacterAsync() -- back to the lobby, safe from the killer
	end
end)
  • Show progress with a BillboardGui over each generator that reads its Progress attribute, and show the killer a flash when a generator finishes.
  • Two survivors on one generator finish it twice as fast with this script, which rewards teamwork. If that makes rounds too short, lower STEP so each generator needs more holds.
  • More generators than are needed (for example five, with four required) stops the killer from guarding the last one.

Balancing one against many

Characters walk at 16 studs per second by default. A killer at exactly survivor speed never catches anyone in the open, and a much faster killer ends every chase in seconds. One approach is a small speed edge for the killer, balanced by survivor tools: sprint bursts with stamina, obstacles to loop and doors to close. Tune with these levers, one at a time:

Balance levers and what they change
LeverRaise it and...Watch for
Killer speedChases end sooner; killer wins moreSurvivors who feel nothing they do matters
Objective timeRounds last longer; killer has more timeSurvivors bored holding a prompt
Map sizeMore hiding; harder for the killer to find anyoneRounds where nothing happens
Head startSurvivors spread out before the huntA first minute with no tension
Survivor count per killerMore objectives done in parallelOne killer cannot pressure everyone; consider two killers in big servers

Set Max Players for the round you designed. Write down how many players a round is meant for, and test at that size, at the smallest size you allow, and at full.

Monetization that fits the genre

What players accept, and what breaks the balance
ProductRoblox featureFits the genre?
Killer and survivor skinsPass or developer productYes. The genre's main purchase: looks, same stats.
New charactersCurrency earned in rounds, or a passYes, if each is a sidegrade (different, not stronger) and earnable without paying.
Killer chance boostDeveloper productCan fit, if you say exactly what it does in numbers. It lowers everyone else's chance too, so keep it small.
Stronger abilities or more healthAnyNo. One paid killer with more speed decides every round they play.
VIP serverPaid private serversYes. Friends want to play rounds together.
Monthly perksSubscriptionCan fit: a monthly skin and bonus currency, honoured for the full term.

Keep paid perks out of the round itself. A purchase that makes a killer faster or a survivor tougher shows up in every round that player is in, and the other side notices quickly. Rewarded video ads, if your game is eligible, belong in the lobby between rounds; Roblox's rules say the ad should not harm the player's character, and an ad during a chase would.

What brings players back

  • Unlocks. Currency every round, spent on new killers, survivors and skins, is the main long-term goal.
  • New killers and maps. Each new killer changes how every map plays. The genre's updates are characters.
  • Daily quests. "Repair 5 generators", "Escape twice", "Win as killer once" push players to try both roles.
  • Social play. Escaping with friends is the best moment the genre has. Make joining friends easy and give private servers a reason to exist.
  • Daily rewards with a streak, paid in the round currency.

Build it with RoCode

RoCode is an AI agent that builds in the Roblox Studio place you have open: you describe the game in a web chat and its free Studio plugin creates the scripts, parts and UI. For a killer vs survivors game it can set up the teams, the round loop with a fair killer pick, generators and an exit, and a killer melee tool with server hit checks, and add UI kit screens such as a round timer, a shooter style HUD or health bars. It cannot make killer models or attack animations, and it cannot tell you whether the balance is fun; that takes real rounds with real players. A full game is built in rounds, one working part at a time.

You type

Make the first playable of a killer vs survivors game: Lobby, Survivors and Killer teams, a round every 20 seconds that picks a killer with a chance that grows each round you are not picked, a 15 second head start, 4 generators survivors repair with a hold prompt, an exit that opens when all are done, and a killer sword with server hit checks.

RoCode does
  • Creates the teams, spawns, generators and exit, and writes the round and objective scripts
  • Can add UI kit screens such as a round timer, health bars and notifications
  • Can run a test robot that walks to a generator and holds the prompt, and report PASS or FAIL
  • Takes screenshots in edit mode to check the map layout

Build this in Studio

You still balance it by playing many rounds with real people. How RoCode builds a game in rounds.

Questions

How do you make a killer vs survivors game on Roblox?

Build a round loop that picks one killer and puts the others on a Survivors team, give survivors objectives that open an exit, give the killer a server-checked attack, and end the round on an escape, a wipe or the timer.

How do I pick the killer fairly?

Use a weighted pick: each player has a chance value that grows every round they are not the killer and resets when they are. It stays random but stops long droughts.

How many players does an asymmetric game need?

At least two to run a round, but it plays as designed only at the size you balanced for. Test at your smallest and largest server sizes, and consider two killers in big servers.

Should the killer be faster than survivors?

Usually a little, with survivors given sprint bursts, obstacles and hiding places to make up for it. Equal speed makes chases endless; much faster makes them pointless.

Can I make this game without scripting?

No: rounds, roles, objectives and hits all need scripts. You can start from the scripts on this page, or describe the game to an AI agent such as RoCode and test what it builds.

Sources and further reading

How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 26 October 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.

Try RoCode on your own place.

Free every day, no card required. Install the plugin, describe the feature, and review what lands in Studio.