Roblox · Tower Defense · урок 5
Враг, дошедший до конца пути, бьёт базу. Над базой — полоска здоровья; при нуле волны останавливаются.
Синим — что добавляем на этом уроке.
ServerStorage └─ Bindables (Folder) ├─ DamageEvent (BindableEvent) — Mob → Base └─ GameOverEvent (BindableEvent) — Base → MainScript Workspace └─ Grass (Folder) └─ Base (Model) — в конце маршрута └─ HealthGui (BillboardGui) — AlwaysOnTop, Adornee = парт базы ├─ MaxHealth (Frame) — красный, Size 1,0,0.5,0, Position 0,0,0.5,0 ├─ Health (Frame) — зелёный, такой же размер и позиция └─ Title (TextLabel) — Size 1,0,0.5,0 ServerScriptService └─ MainScript (Script) ├─ Mob (ModuleScript) ├─ Tower (ModuleScript) └─ Base (ModuleScript)
Где: ServerScriptService → MainScript · ModuleScript
Зачем: В конце пути сообщает базе урон (оставшееся здоровье врага).
-- хранилище с образцами врагов
local SS = game:GetService("ServerStorage")
-- папка с событиями сервер → сервер
local bindables = SS:WaitForChild("Bindables")
-- событие «враг дошёл до базы»
local damageEvent = bindables:WaitForChild("DamageEvent")
-- таблица модуля — сюда кладём функции
local mob = {}
-- ведёт врага по маршруту
function mob.Move(enemy, map)
-- Humanoid управляет движением
local humanoid = enemy:WaitForChild("Humanoid")
-- точки маршрута
local waypoints = workspace:FindFirstChild("Waypoints")
-- анимация ходьбы внутри врага
local animation = enemy:FindFirstChild("Animation")
-- загружаем её в Humanoid
local animTrack = humanoid:LoadAnimation(animation)
-- запускаем ходьбу
animTrack:Play()
-- по точкам по порядку
for waypoint = 1, #waypoints:GetChildren() do
-- идём к точке
humanoid:MoveTo(waypoints[waypoint].Position)
-- ждём, пока дойдёт
humanoid.MoveToFinished:Wait()
-- враг погиб по дороге — дальше не идёт
if humanoid.Health <= 0 then return end
-- следующая точка
end
-- урон базе = оставшееся здоровье врага
damageEvent:Fire(humanoid.Health)
-- дошёл до конца — убираем
enemy:Destroy()
-- конец функции
end
-- создаёт quantity врагов с именем name
function mob.Spawn(name, quantity, map)
-- ищем образец
local mobModel = SS.Mobs:FindFirstChild(name)
-- образец найден
if mobModel then
-- нужное количество раз
for i = 1, quantity do
-- пауза между врагами
task.wait(0.5)
-- копия врага
local mobClone = mobModel:Clone()
-- ставим на старт
mobClone.PrimaryPart.CFrame = map.SpawnPart.CFrame
-- кладём на карту
mobClone.Parent = workspace.Mobs
-- все детали врага
for _, obj in pairs(mobClone:GetDescendants()) do
-- только физические
if obj:IsA("BasePart") then
-- группа коллизий Mob
obj.CollisionGroup = "Mob"
-- конец проверки
end
-- конец перебора
end
-- враг погиб
mobClone.Humanoid.Died:Connect(function()
-- короткая пауза
task.wait(0.5)
-- убираем тело с карты
mobClone:Destroy()
-- конец обработчика смерти
end)
-- движение параллельно — не ждём, пока враг пройдёт путь
coroutine.wrap(mob.Move)(mobClone, map)
-- следующий враг
end
-- конец проверки
end
-- конец функции
end
-- отдаём таблицу тому, кто вызвал require
return mobГде: ServerScriptService → MainScript · ModuleScript
Зачем: Хранит здоровье базы, рисует полоску, при нуле сообщает о конце игры.
-- серверное хранилище
local SS = game:GetService("ServerStorage")
-- события сервер → сервер
local bindables = SS:WaitForChild("Bindables")
-- враг дошёл до базы
local damageEvent = bindables:WaitForChild("DamageEvent")
-- база разрушена
local gameOverEvent = bindables:WaitForChild("GameOverEvent")
-- таблица модуля
local base = {}
-- задаёт базу и её здоровье
function base.SetParams(map, health)
-- модель базы на карте
base.Model = map:WaitForChild("Base")
-- максимум здоровья
base.MaxHealth = health
-- текущее здоровье
base.Health = health
-- сразу показываем полоску
base.NewHealth(0)
-- конец функции
end
-- получает урон и обновляет полоску
function base.NewHealth(damage)
-- база уже разрушена — ничего не делаем
if base.Health <= 0 then return end
-- вычитаем урон, но не ниже нуля
base.Health = math.max(base.Health - damage, 0)
-- полоска над базой
local gui = base.Model.HealthGui
-- доля оставшегося здоровья
local percent = base.Health / base.MaxHealth
-- зелёная полоса укорачивается
gui.Health.Size = UDim2.new(percent, 0, 0.5, 0)
-- здоровье кончилось
if base.Health <= 0 then
-- пишем, что база разрушена
gui.Title.Text = "Base: Destroyed"
-- сообщаем главному скрипту
gameOverEvent:Fire()
-- база ещё стоит
else
-- текст «Base: 450 / 500»
gui.Title.Text = "Base: " .. base.Health .. " / " .. base.MaxHealth
-- конец проверки
end
-- конец функции
end
-- враг дошёл → урон базе
damageEvent.Event:Connect(base.NewHealth)
-- отдаём таблицу модуля
return baseГде: ServerScriptService · Script
Зачем: Задаёт базе 500 здоровья и останавливает волны при поражении.
-- модуль врагов
local mob = require(script.Mob)
-- модуль башен
local tower = require(script.Tower)
-- модуль базы
local base = require(script.Base)
-- карта
local map = workspace.Grass
-- серверное хранилище
local SS = game:GetService("ServerStorage")
-- события сервер → сервер
local bindables = SS:WaitForChild("Bindables")
-- база разрушена
local gameOverEvent = bindables:WaitForChild("GameOverEvent")
-- игра ещё идёт
local gameOver = false
-- пришёл сигнал о разрушении базы
gameOverEvent.Event:Connect(function()
-- игра окончена
gameOver = true
-- конец обработчика
end)
-- у базы 500 здоровья
base.SetParams(map, 500)
-- волны по очереди
for wave = 1, 5 do
-- обычные: 2, 4, 6…
mob.Spawn("NormalRig", 2 * wave, map)
-- быстрые
mob.Spawn("FastRig", 2 * wave, map)
-- сильные
mob.Spawn("StrongRig", 2 * wave, map)
-- ждём: карта пуста или база разрушена
repeat task.wait(1) until #workspace.Mobs:GetChildren() == 0 or gameOver
-- база разрушена — волн больше нет
if gameOver then break end
-- следующая волна
endЗапусти Play (или Run) и убедись: