Menu
CoddyTech

המשחק נגמר

שיעור 18 מתוך 18 בקורס משחק Roblox: הגנת מגדלים של Coddy.

המשחק שלך כמעט מוכן. ההגדרות מסתיימות ב־Boss: 200 נקודות בריאות, איטי, והוא מוריד 20 מהבסיס, כך שהגנה שלא יכולה לעצור אותו מפסידה בגל האחרון.

הפסד הוא הכלל האחרון. כשהבסיס מגיע ל־0, המשחק נגמר: השרת מגדיר את Status ל־"Lost", מסלק את כל האויבים מהמסלול ולא מתחיל גלים נוספים. משתנה gameOver מעביר את המידע הזה לכל לולאה שזקוקה לו:

if gameOver then
	break
end

ניצחון בסוף נחשב רק אם הבסיס החזיק מעמד: if not gameOver then.

אחר כך לחצו על Play, בנו הגנה והביסו את כל חמשת הגלים. שנו את Settings כדי לאזן את המשחק לטעמכם.

challenge icon

אתגר

קשה
  1. הוסף local gameOver = false. בתוך damageBase, כשהבריאות מגיעה ל־0 בפעם הראשונה, הגדר אותו, הגדר את Status ל־"Lost" ונקה את כל האויבים.
  2. בתוך runWave, הפסק להוסיף אויבים ברגע שהמשחק מסתיים; בראש לולאת הגלים, עצור כשהמשחק מסתיים.
  3. הגדר את Status ל־"Won" רק כשהמשחק לא הסתיים.
  4. בתוך showEnd של DefenseClient, הצג את The base has fallen עבור "Lost".

לחץ על Play, ואז על Check: הבדיקות מאפשרות לשלושה Zombies לעבור בבסיס עם 3 נקודות בריאות.

נסו בעצמכם

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 = {} -- כל מגדל -> המשטח שעליו הוא עומד
local skipped = false

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

-- נקודות הציון, לפי הסדר (שמותיהן 1, 2, 3...)
local waypoints = workspace.Path.Waypoints:GetChildren()
table.sort(waypoints, function(a, b)
	return tonumber(a.Name) < tonumber(b.Name)
end)

-- הבריאות של הבסיס, כתכונה של workspace: כל לקוח יכול לקרוא אותה
workspace:SetAttribute("BaseHealth", Settings.BaseHealth)

-- כל שחקן מקבל Money בלוח המובילים
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)

-- אויב הגיע לבסיס
local function damageBase(amount)
	local health = math.max(0, workspace:GetAttribute("BaseHealth") - amount)
	workspace:SetAttribute("BaseHealth", health)
end

-- הולך עם אויב לאורך המסלול. MoveTo מוותר אחרי 8 שניות ומדווח על כך
-- (MoveToFinished עם false), לכן שולחים אויב איטי שוב עד שהוא מגיע.
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
	-- הוא הגיע לבסיס
	damageBase(Settings.Enemies[enemy.Name].Damage)
	enemy:Destroy()
end

-- אויב חדש בשער, הולך לאורך המסלול
local function spawnEnemy(kind)
	local enemy = ServerStorage.Enemies[kind]:Clone()
	-- מופיע ממש מעל נקודת הציון הראשונה
	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.HealthChanged:Connect(function(health)
		enemy.HealthBar.Back.Fill.Size = UDim2.fromScale(health / enemy.Humanoid.MaxHealth, 1)
	end)
	enemy.Humanoid.Died:Connect(function()
		-- כל שחקן מקבל את הפרס
		for _, player in Players:GetPlayers() do
			player.leaderstats.Money.Value += info.Reward
		end
		enemy:Destroy()
	end)
	task.spawn(walkPath, enemy)
	return enemy
end

-- האויב החי הקרוב ביותר לנקודה ונמצא בטווח, או 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

-- הבזק אור ממגדל אל המטרה שלו
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

-- Frost: אויב נע במהירות שהיא חלק ממהירותו הרגילה במשך 2 שניות
local function slow(enemy, factor)
	if enemy:GetAttribute("Slowed") then
		return
	end
	enemy:SetAttribute("Slowed", true)
	local speed = Settings.Enemies[enemy.Name].Speed
	enemy.Humanoid.WalkSpeed = speed * factor
	task.delay(2, function()
		if enemy.Parent then
			enemy.Humanoid.WalkSpeed = speed
			enemy:SetAttribute("Slowed", nil)
		end
	end)
end

-- ירייה: פוגעת במטרה (ואם היא נורית מ-Cannon, גם בכל מי שסביבה)
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
	if info.Slow and target.Parent then
		slow(target, info.Slow)
	end
	beam(tower.Head.Position, at)
end

-- פעולת המגדל: לכוון אל האויב הקרוב ביותר שבטווח, לירות ולהמתין עד שיוכל לירות שוב
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

-- לבנות מגדל על משטח
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

-- הלקוח מבקש להציב מגדל על משטח. יש לבדוק הכול: שהמשטח קיים ופנוי,
-- שהמגדל קיים ושיש מספיק כסף לשלם עליו.
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)

-- שדרוג: העלות של הרמה הבאה גדלה בכל פעם, וכל רמה מוסיפה שוב את
-- הנזק של המגדל
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)

-- מכירה: מקבלים בחזרה חצי מהמחיר, והמשטח מתפנה שוב
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)

-- דילוג: להתחיל את הגל הבא מוקדם יותר (רק בזמן הפסקה)
remotes.Skip.OnServerEvent:Connect(function()
	if workspace:GetAttribute("Break") then
		skipped = true
	end
end)

-- גל אחד: האויבים שלו מופיעים בזה אחר זה, ואז ממתינים עד שכולם נעלמים
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

-- הגלים, כל אחד לאחר הפסקה
for i, list in Settings.Waves do
	workspace:SetAttribute("Wave", i)
	workspace:SetAttribute("Break", true)
	skipped = false
	local started = time()
	while time() - started < Settings.WaveBreak and not skipped do
		task.wait(0.1)
	end
	workspace:SetAttribute("Break", false)
	runWave(list)
end

-- כל הגלים הסתיימו: הבסיס החזיק מעמד
workspace:SetAttribute("Status", "Won")
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה משחק Roblox: הגנת מגדלים