How to Make a Kill Brick in Roblox (Touched Events)

Last updated:

Quick answer: A kill brick is a server Script that listens to a part's Touched event, checks hit.Parent for a Humanoid, and sets Humanoid.Health to 0 — with a debounce keyed on os.clock() so the event firing many times per contact cannot kill or damage the player over and over.

A kill brick is a part that kills (or damages) whatever player touches it, and the whole thing is one server Script with a Touched event connection: when a limb touches the part, you look for a Humanoid in the touching part's parent and set its health to zero. The only real subtlety is that Touched fires far more often than it appears it should — once per limb, repeatedly while overlapping — so a production kill brick needs a debounce. Below is the minimal script, an honest look at how Touched actually fires, the debounce fix, and variants for lava floors and damage zones.

What does the simplest kill brick script look like?

Create a Part in Workspace, insert a normal Script into it (not a LocalScript — more on that below), and paste this:

local brick = script.Parent

brick.Touched:Connect(function(hit)
	local humanoid = hit.Parent and hit.Parent:FindFirstChildOfClass("Humanoid")
	if humanoid then
		humanoid.Health = 0
	end
end)

That is the whole kill brick, and here is what every line does:

  1. local brick = script.Parent — the script sits inside the part, so script.Parent is the brick itself. If you prefer, you can reference the part by name with workspace.KillBrick instead.
  2. brick.Touched:Connect(function(hit) ... end) — Touched fires whenever something physically contacts the part. The hit argument is the specific part that made contact — a player's LeftFoot, their Torso, a dropped tool, a rolling ball — never the player object directly.
  3. hit.Parent and hit.Parent:FindFirstChildOfClass("Humanoid") — characters are Models whose direct children include the limbs and a Humanoid, so the character Model is hit.Parent. FindFirstChildOfClass("Humanoid") returns the Humanoid if that Model is a character, and nil if it is just a random part, which makes the check safe for debris.
  4. humanoid.Health = 0 — setting health to zero kills the character, exactly like falling into lava. The alternative humanoid:TakeDamage(25) subtracts damage without an instant kill.

Use :Connect with a capital C. The lowercase :connect still happens to exist for legacy reasons but is deprecated; modern Luau code always writes :Connect.

How does Touched actually fire?

This is where most beginners get surprised: one jump onto the brick does not produce one Touched call. The event is raised per part, per contact, so:

For a deeper dive into the physics behind this, see the Touched event guide. The practical takeaway for a kill brick: assume Touched fires many times per contact and design the callback to be idempotent, or gate it with a debounce.

There is a second trap hiding in hit: it is the part that touched, so hit.Parent is the character only when the touching part is a limb. Touched also fires for hats and accessories welded to the character — those live under a different parent level, which is why FindFirstChildOfClass("Humanoid") on hit.Parent quietly does nothing for them and you only kill when an actual limb hits. That is usually the behavior you want.

Why does my kill brick kill instantly multiple times, and how does debounce fix it?

Without a guard, every repeated Touched call sets health to zero again. For an instant kill that looks harmless, but the moment you switch to damage — humanoid.Health = humanoid.Health - 25 — one landing deals 250 damage because the callback ran ten times. Even for kills, re-running the callback fires your scoring code ten times and awards ten kills for one touch.

The fix is a debounce table: remember the last time each character touched, and ignore any call that arrives too soon after. Use os.clock() for timestamps — it is a monotonic timer meant for measuring elapsed time, and unlike the legacy tick() it is not affected by wall-clock changes.

local Players = game:GetService("Players")

local brick = script.Parent
local COOLDOWN_SECONDS = 1
local lastTouchAt = {} -- [character or player] = os.clock() timestamp

brick.Touched:Connect(function(hit)
	local character = hit.Parent
	local humanoid = character and character:FindFirstChildOfClass("Humanoid")
	if not humanoid or humanoid.Health <= 0 then
		return
	end

	local player = Players:GetPlayerFromCharacter(character)
	local key = player or character
	local now = os.clock()
	if lastTouchAt[key] and now - lastTouchAt[key] < COOLDOWN_SECONDS then
		return -- still inside the cooldown window from the previous contact
	end
	lastTouchAt[key] = now

	humanoid.Health = 0
end)

Players.PlayerRemoving:Connect(function(player)
	lastTouchAt[player] = nil -- no leaks when players leave
end)

Walk through the moving parts:

If you inherited older code that used wait(), spawn(), delay(), or tick(), treat it as a migration callout: those globals are deprecated scheduling primitives with sloppy timing behavior. Replace wait(n) with task.wait(n), spawn(fn) with task.spawn(fn), delay(n, fn) with task.delay(n, fn), and every tick() timestamp with os.clock(). A kill brick needs none of these — the debounce above is purely event-driven.

How do you make a lava floor or a damage zone instead of an instant kill?

Same skeleton, different line at the bottom. For damage, replace humanoid.Health = 0 with:

	local DAMAGE = 25
	humanoid:TakeDamage(DAMAGE)

TakeDamage respects the Humanoid's MaxHealth and kills naturally at zero, and it is the idiomatic way to subtract health. A lava floor is just a large anchored part with this script and a short cooldown (around half a second) so a player standing in it takes steady damage instead of dying in one frame.

For a damage zone that only hurts while you stand in it, listen for both contact and separation:

local Players = game:GetService("Players")

local zone = script.Parent
local DAMAGE_PER_SECOND = 15
local inside = {} -- [character] = true while touching

zone.Touched:Connect(function(hit)
	local character = hit.Parent
	local humanoid = character and character:FindFirstChildOfClass("Humanoid")
	if humanoid then
		inside[character] = true
	end
end)

zone.TouchEnded:Connect(function(hit)
	local character = hit.Parent
	if character then
		inside[character] = nil
	end
end)

task.spawn(function()
	while true do
		task.wait(1)
		for character in inside do
			local humanoid = character:FindFirstChildOfClass("Humanoid")
			if humanoid and humanoid.Health > 0 then
				humanoid:TakeDamage(DAMAGE_PER_SECOND)
			else
				inside[character] = nil
			end
		end
	end
end)

Here TouchEnded (the counterpart to Touched) marks when contact breaks, and one shared loop ticks damage once per second for everyone inside — much tidier than per-part timers.

Should the kill brick be a Script or a LocalScript?

Always a server Script. A LocalScript changing Humanoid.Health only changes the health on that one client: the server and every other player see nothing happen, and an exploiter can simply delete your LocalScript. Kill logic belongs on the server, which is the same authority principle behind Script vs LocalScript and server-validated currency.

How do you connect a kill brick to a scoring system?

Once the kill is server-side you can count it. The simplest version tracks deaths: when your kill brick fires, increment a leaderstat on the dying player's data. Because the debounce already guarantees one callback per contact, the counter stays honest.

local Players = game:GetService("Players")

local brick = script.Parent
local COOLDOWN_SECONDS = 1
local lastTouchAt = {}

local function onTouched(hit)
	local character = hit.Parent
	local humanoid = character and character:FindFirstChildOfClass("Humanoid")
	if not humanoid or humanoid.Health <= 0 then
		return
	end

	local player = Players:GetPlayerFromCharacter(character)
	local key = player or character
	local now = os.clock()
	if lastTouchAt[key] and now - lastTouchAt[key] < COOLDOWN_SECONDS then
		return
	end
	lastTouchAt[key] = now

	if player then
		local deaths = player:FindFirstChild("leaderstats")
			and player.leaderstats:FindFirstChild("Deaths")
		if deaths then
			deaths.Value += 1
		end
	end

	humanoid.Health = 0
end

brick.Touched:Connect(onTouched)

If you want a scoring system where pushing someone into the brick awards the pusher, tag the character with CollectionService or a LastAttacker attribute when damage is dealt, then read that tag inside onTouched to find who to reward. The leaderstats tutorial covers building the stats folder this code expects.

Common mistakes

Learn Luau by doing

A kill brick is a perfect first gameplay system: one event, one condition, one effect. Once the debounce pattern clicks, the same shape powers lava, poison swamps, and racing reset zones. Practice the building blocks interactively on /theory, drill event handling on /problems, and build up to full game systems with structured Luablox lessons.

Frequently asked questions

Why does my kill brick kill instantly multiple times?

Because Touched fires once per limb per contact and re-fires constantly while the character overlaps the brick — one jump can produce ten to thirty calls. Add a debounce table keyed on the character or player using os.clock() timestamps, and ignore any call that arrives within your cooldown window.

How do I make a kill brick only kill players and not NPCs?

Use Players:GetPlayerFromCharacter(hit.Parent) and only run the kill when it returns a player. NPCs and mobs have a Humanoid but no associated player, so the check skips them cleanly.

How do I make a kill brick damage instead of kill?

Replace humanoid.Health = 0 with humanoid:TakeDamage(25) (or subtract from humanoid.Health) and keep the debounce, usually with a shorter cooldown around 0.5 seconds. TakeDamage is preferred because it handles max health and natural death at zero.

Can a kill brick be a LocalScript?

No. A LocalScript only changes Humanoid.Health on that single client, so other players and the server see nothing and exploiters can delete the script. Kill logic must live in a server Script so the server is the one setting health.

Why does my kill brick script error when random parts touch it?

The Touched event fires for every physical contact, including tools, projectiles, and map debris. hit.Parent can be anything and even nil. Guard with hit.Parent and hit.Parent:FindFirstChildOfClass("Humanoid") before touching health.

Learn core Roblox scripting and Luau programming concepts, including: how to make a kill brick in roblox, kill brick script, roblox touched event kill, roblox lava brick script, debounce roblox touched.

Related pages