Menu
CoddyTech

Pembekuan

Pelajaran 14 dari 18 dalam kursus Game Roblox: Tower Defense di Coddy.

Menara Frost hampir tidak melukai musuh: tembakannya memperlambat musuh hingga kecepatannya menjadi sebagian kecil dari kecepatan semula, info.Slow, selama 2 detik, sehingga menara lain bisa menembaknya lebih banyak.

Dua hal bisa membuat efek perlambatan tidak bekerja dengan benar. Musuh yang sudah diperlambat lalu terkena tembakan lagi tidak boleh menjadi semakin lambat, jadi tandai musuh dengan atribut dan lewati musuh itu selama tanda tersebut masih ada. Kecepatan yang dipulihkan haruslah kecepatan asli musuh dari pengaturan, bukan kecepatan berjalannya saat ini. task.delay menjalankan fungsi nanti tanpa menahan menara:

task.delay(2, function()
	-- dua detik kemudian
end)
challenge icon

Tantangan

Sedang
  1. Tulis slow(enemy, factor): kembalikan fungsi jika musuh memiliki atribut Slowed; jika tidak, atur atribut tersebut, atur WalkSpeed-nya ke kecepatan Settings dikalikan factor, lalu setelah 2 detik, jika musuh masih ada di dalam game, kembalikan kecepatannya dan hapus atribut tersebut.
  2. Di dalam shoot, jika info.Slow diatur dan target masih ada di dalam game, perlambat target.

Tekan Mainkan, lalu Periksa.

Coba sendiri

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

local remotes = game.ReplicatedStorage.Remotes
local enemies = workspace.Enemies
local towers = workspace.Towers
local towerPad = {} -- setiap menara -> pad tempatnya berdiri

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

-- Waypoint yang berurutan (dinamai 1, 2, 3...)
local waypoints = workspace.Path.Waypoints:GetChildren()
table.sort(waypoints, function(a, b)
	return tonumber(a.Name) < tonumber(b.Name)
end)

-- Nyawa markas sebagai atribut workspace: setiap client dapat membacanya
workspace:SetAttribute("BaseHealth", Settings.BaseHealth)

-- Setiap pemain mendapat Money di 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)

-- Musuh mencapai markas
local function damageBase(amount)
	local health = math.max(0, workspace:GetAttribute("BaseHealth") - amount)
	workspace:SetAttribute("BaseHealth", health)
end

-- Gerakkan musuh menyusuri jalur. MoveTo berhenti mencoba setelah 8 detik dan memberi tahu
-- (MoveToFinished dengan false), jadi musuh yang lambat diperintahkan bergerak lagi sampai tiba.
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
	-- musuh mencapai markas
	damageBase(Settings.Enemies[enemy.Name].Damage)
	enemy:Destroy()
end

-- Musuh baru di gerbang, berjalan menyusuri jalur
local function spawnEnemy(kind)
	local enemy = ServerStorage.Enemies[kind]:Clone()
	-- ditempatkan tepat di atas waypoint pertama
	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
	enemy.Humanoid.Died:Connect(function()
		-- setiap pemain mendapat hadiah
		for _, player in Players:GetPlayers() do
			player.leaderstats.Money.Value += info.Reward
		end
		enemy:Destroy()
	end)
	task.spawn(walkPath, enemy)
	return enemy
end

-- Musuh hidup terdekat dalam jangkauan dari suatu titik, atau 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

-- Kilatan cahaya dari menara menuju targetnya
local function beam(from, to)
	local ray = Instance.new("Part")
	ray.Anchored = true
	ray.CanCollide = false
	ray.CanTouch = false
	ray.Material = Enum.Material.Neon
	ray.Color = Color3.fromRGB(255, 240, 150)
	ray.Size = Vector3.new(0.2, 0.2, (to - from).Magnitude)
	ray.CFrame = CFrame.lookAt((from + to) / 2, to)
	ray.Parent = workspace
	Debris:AddItem(ray, 0.1)
end

-- Tembakan: lukai target (dan, jika dari Cannon, semua musuh di dekatnya)
local function shoot(tower, target, info)
	local damage = info.Damage * tower:GetAttribute("Level")
	local at = target.HumanoidRootPart.Position
	if info.Splash then
		for _, enemy in enemies:GetChildren() do
			local root = enemy:FindFirstChild("HumanoidRootPart")
			if root and (root.Position - at).Magnitude <= info.Splash then
				enemy.Humanoid:TakeDamage(damage)
			end
		end
	else
		target.Humanoid:TakeDamage(damage)
	end
	beam(tower.Head.Position, at)
end

-- Rutinitas menara: bidik musuh terdekat dalam jangkauan, tembak, lalu tunggu hingga siap menembak lagi
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)
			shoot(tower, target, info)
			task.wait(info.Cooldown)
		else
			task.wait(0.1)
		end
	end
end

-- Bangun menara di atas landasan
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:SetAttribute("Level", 1)
	tower.Parent = towers
	pad:SetAttribute("Used", true)
	towerPad[tower] = pad
	task.spawn(runTower, tower)
end

-- client meminta menara di atas landasan. Periksa semuanya: landasan yang valid dan belum terpakai,
-- jenis menara yang valid, serta uang yang cukup untuk membelinya.
remotes.Place.OnServerEvent:Connect(function(player, pad, kind)
	if typeof(pad) ~= "Instance" or pad.Parent ~= workspace.Pads or pad:GetAttribute("Used") then
		return
	end
	local info = typeof(kind) == "string" and Settings.Towers[kind]
	if not info then
		return
	end
	local money = player.leaderstats.Money
	if money.Value < info.Price then
		return
	end
	money.Value -= info.Price
	placeTower(pad, kind)
end)

-- Peningkatan: biaya untuk naik ke level berikutnya makin besar, dan setiap level kembali menambah
-- daya serang menara
remotes.Upgrade.OnServerEvent:Connect(function(player, tower)
	if typeof(tower) ~= "Instance" or tower.Parent ~= towers then
		return
	end
	local level = tower:GetAttribute("Level")
	local cost = math.floor(Settings.Towers[tower.Name].Price * Settings.UpgradeCost * level)
	local money = player.leaderstats.Money
	if money.Value < cost then
		return
	end
	money.Value -= cost
	tower:SetAttribute("Level", level + 1)
end)

-- Penjualan: separuh harga dikembalikan, dan landasan bisa digunakan lagi
remotes.Sell.OnServerEvent:Connect(function(player, tower)
	if typeof(tower) ~= "Instance" or tower.Parent ~= towers then
		return
	end
	player.leaderstats.Money.Value += math.floor(Settings.Towers[tower.Name].Price / 2)
	towerPad[tower]:SetAttribute("Used", nil)
	towerPad[tower] = nil
	tower:Destroy()
end)

-- Satu gelombang: munculkan musuh satu per satu, lalu tunggu sampai semuanya hilang
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

-- Gelombang-gelombang musuh, masing-masing setelah jeda
for i, list in Settings.Waves do
	task.wait(Settings.WaveBreak)
	runWave(list)
end
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Game Roblox: Tower Defense

Latihan sendiri: Playground Roblox