דלג וזכה
שיעור 17 מתוך 18 בקורס משחק Roblox: הגנת מגדלים של Coddy.
ההמתנה לגל הבא משעממת כשאתם מוכנים. הכפתור Skip wait ב־HUD מבקש מהשרת להתחיל את הגל הבא עכשיו, דרך Remotes.Skip.
השרת ממתין להפסקה בצעדים קטנים, כדי שיוכל לעצור מוקדם. מאפיין של סביבת העבודה Break מציין אם ההפסקה פעילה, ומשתנה skipped מתאפס בתחילת כל הפסקה:
skipped = false
local started = time()
while time() - started < Settings.WaveBreak and not skipped do
task.wait(0.1)
endכשהגל האחרון מסתיים והבסיס עדיין עומד: השרת מגדיר את מאפיין סביבת העבודה Status לערך "Won", והלקוח מציג את Message של ה־HUD.
אתגר
קשה- ב־
DefenseServer, הוסףlocal skipped = false, וחבר אתremotes.Skip.OnServerEventכך שיציב אותו ל־true בזמן שהתכונהBreakפעילה. - לפני כל גל: הגדר את
Breakל־true, אפס אתskipped, המתן עד שההפסקה תסתיים או תידלג (בצעדים של 0.1), והגדר אתBreakל־false. - אחרי הלולאה, הגדר את התכונה
Statusשל סביבת העבודה ל־"Won". - ב־
DefenseClient, הכפתורSkipמפעיל אתremotes.Skip; והפונקציהshowEnd(), שנקראת פעם אחת ובכל שינוי שלStatus, מציגה את התוויתMessageעם הכיתובYou defended the base!כאשר הערך שלו הוא"Won".
בעולם של השיעור הזה יש שני גלים קצרים. לחץ על Play, ואז על Check.
נסו בעצמכם
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 = {} -- כל מגדל -> המשטח שעליו הוא עומד
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)
-- גל אחד: האויבים שבו מופיעים בזה אחר זה, ואז ממתינים עד שכולם ייעלמו
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)
task.wait(Settings.WaveBreak)
runWave(list)
end
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.