Menu
CoddyTech

Fire

Lesson 8 of 18 in Coddy's Roblox Game: Tower Defense course.

Facing the enemy is half the job. shoot hurts the target with Humanoid:TakeDamage and draws a short flash of light from the head to the target: a thin neon part that Debris removes a tenth of a second later.

Debris:AddItem(ray, 0.1) -- destroyed in 0.1 seconds

After a shot the tower waits its Cooldown before it can shoot again; that is what makes an Archer fast and a Cannon slow.

When a Humanoid's health reaches 0 its Died event fires. A beaten enemy should leave the game at once, so spawnEnemy connects Died to destroy it: then walkPath sees it gone and stops.

challenge icon

Challenge

Medium
  1. Get the Debris service. Write beam(from, to): an anchored neon part, 0.2 by 0.2 and as long as the distance, set with CFrame.lookAt((from + to) / 2, to), removed by Debris after 0.1 seconds.
  2. Write shoot(tower, target, info): TakeDamage(info.Damage) on the target's Humanoid, then a beam from the head to the target.
  3. In runTower, shoot after turning, then wait info.Cooldown instead of 0.1 (keep 0.1 when there is no target).
  4. In spawnEnemy, destroy the enemy when its Humanoid dies.

Press Play, then Check.

Try it yourself

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local Settings = require(game.ReplicatedStorage.Settings)

local remotes = game.ReplicatedStorage.Remotes
local enemies = workspace.Enemies
local towers = workspace.Towers

print("The base can take " .. Settings.BaseHealth .. " hits")

-- The waypoints, in order (they are named 1, 2, 3...)
local waypoints = workspace.Path.Waypoints:GetChildren()
table.sort(waypoints, function(a, b)
	return tonumber(a.Name) < tonumber(b.Name)
end)

-- The base's health, as an attribute of the workspace: every client can read it
workspace:SetAttribute("BaseHealth", Settings.BaseHealth)

-- Every player gets Money on the leaderboard
Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local money = Instance.new("IntValue")
	money.Name = "Money"
	money.Value = Settings.StartMoney
	money.Parent = leaderstats
end)

-- An enemy reached the base
local function damageBase(amount)
	local health = math.max(0, workspace:GetAttribute("BaseHealth") - amount)
	workspace:SetAttribute("BaseHealth", health)
end

-- Walk an enemy along the path. MoveTo gives up after 8 seconds and says so
-- (MoveToFinished with false), so a slow enemy is sent again until it arrives.
local function walkPath(enemy)
	local humanoid = enemy.Humanoid
	for i = 2, #waypoints do
		repeat
			humanoid:MoveTo(waypoints[i].Position)
		until humanoid.MoveToFinished:Wait() or not enemy.Parent
		if not enemy.Parent then
			return
		end
	end
	-- it reached the base
	damageBase(Settings.Enemies[enemy.Name].Damage)
	enemy:Destroy()
end

-- A new enemy at the gate, walking the path
local function spawnEnemy(kind)
	local enemy = ServerStorage.Enemies[kind]:Clone()
	-- dropped in just above the first waypoint
	enemy:PivotTo(waypoints[1].CFrame + Vector3.new(0, 5, 0))
	local info = Settings.Enemies[kind]
	enemy.Humanoid.MaxHealth = info.Health
	enemy.Humanoid.Health = info.Health
	enemy.Humanoid.WalkSpeed = info.Speed
	enemy.Parent = enemies
	task.spawn(walkPath, enemy)
	return enemy
end

-- The nearest living enemy within range of a point, or nil
local function nearestEnemy(position, range)
	local best, bestDistance = nil, range
	for _, enemy in enemies:GetChildren() do
		local root = enemy:FindFirstChild("HumanoidRootPart")
		if root and enemy.Humanoid.Health > 0 then
			local distance = (root.Position - position).Magnitude
			if distance <= bestDistance then
				best, bestDistance = enemy, distance
			end
		end
	end
	return best
end

-- A tower's life: aim at the nearest enemy in range
local function runTower(tower)
	local info = Settings.Towers[tower.Name]
	while tower.Parent do
		local target = nearestEnemy(tower.Head.Position, info.Range)
		if target then
			tower.Head.CFrame = CFrame.lookAt(tower.Head.Position, target.HumanoidRootPart.Position)
			task.wait(0.1)
		else
			task.wait(0.1)
		end
	end
end

-- Build a tower on a pad
local function placeTower(pad, kind)
	local tower = ServerStorage.Towers[kind]:Clone()
	tower:PivotTo(pad.CFrame + Vector3.new(0, pad.Size.Y / 2 + 1, 0))
	tower.Parent = towers
	task.spawn(runTower, tower)
end

-- The client asks for a tower on a pad
remotes.Place.OnServerEvent:Connect(function(player, pad, kind)
	placeTower(pad, kind)
end)

-- One wave: its enemies one by one, then wait until they are all gone
local function runWave(list)
	for _, kind in list do
		spawnEnemy(kind)
		task.wait(Settings.SpawnGap)
	end
	while #enemies:GetChildren() > 0 do
		task.wait(0.2)
	end
end

-- The waves, each after a break
for i, list in Settings.Waves do
	task.wait(Settings.WaveBreak)
	runWave(list)
end
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Roblox Game: Tower Defense