אין מספיק כסף
שיעור 12 מתוך 22 בקורס משחק Roblox: הגנת מגדלים של Coddy.
עד עכשיו מגדלים הם בחינם, והשרת בונה כל מה שמבקשים ממנו. שחקן שולט במכשיר שלו, ולכן מי שמנצל לרעה את המערכת יכול להפעיל את Remotes.Place עם כל דבר: מגדל על גבי מגדל אחר, על לוח הבסיס, מגדל שלא קיים, בלי כסף בכלל.
לכן השרת בודק כל בקשה לפני שהוא בונה, ומפסיק בבדיקה הראשונה שנכשלת:
- המשטח הוא Instance שנמצא בתוך
workspace.Padsועדיין אין עליו מגדל (מאפייןUsedש-placeTowerמגדירה), - הסוג הוא מחרוזת שמציינת מגדל אמיתי,
- יש לשחקן מספיק כסף; ואז השרת לוקח אותו.
if typeof(pad) ~= "Instance" or pad.Parent ~= workspace.Pads then
return
endהלקוח מבקש, השרת מחליט. בשיעור הזה הבדיקות מדמות את מי שמנצל לרעה את המערכת.
אתגר
בינוניגרמו למגדלים לעלות כסף, וסרבו לבקשות לא תקינות.
- במטפל
Place, החזירו אלא אם כן המשטח הוא מופע בתוךworkspace.Padsואין לו את המאפייןUsed. - החזירו אלא אם כן הסוג הוא מחרוזת המציינת מגדל בתוך
Settings.Towers. - החזירו אם כמות ה־Money נמוכה מהערך של
Price; אחרת, הפחיתו את הסכום ובנו את המגדל. placeTowerמגדירה אתUsedשל המשטח לערך true.
לחצו על „שחק” וקנו מגדלים עד שכמות ה־Money תרד: בקשתכם למגדל הבא תידחה. לאחר מכן לחצו על „בדיקה”.
נסו בעצמכם
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
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)
print("Base: " .. 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.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
-- ירייה: פגעו במטרה
local function shoot(tower, target, info)
local damage = info.Damage
local at = target.HumanoidRootPart.Position
target.Humanoid:TakeDamage(damage)
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.Parent = towers
task.spawn(runTower, tower)
end
-- הלקוח מבקש להציב מגדל על משטח
remotes.Place.OnServerEvent:Connect(function(player, pad, kind)
-- שיעור 12: Can't Afford It יתווסף כאן
placeTower(pad, kind)
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
task.wait(Settings.WaveBreak)
runWave(list)
end
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה משחק Roblox: הגנת מגדלים
תרגלו בעצמכם: Roblox אונליין