Roblox · Tower Defense · урок 4
Кнопки башен строятся сами по папке Towers. У каждой башни свои радиус, урон, скорость атаки и цвет луча.
Синим — что добавляем на этом уроке.
ReplicatedStorage └─ Towers (Folder) └─ Tower (Model) └─ Configuration (Configuration) ├─ Range (NumberValue) — радиус атаки ├─ Damage (NumberValue) — урон за выстрел ├─ Cooldown (NumberValue) — секунд между выстрелами ├─ Image (Decal) — Texture = картинка башни └─ Trail (Color3Value) — цвет луча StarterGui └─ TowerGui (ScreenGui) ├─ TowerFrame (Frame) — панель кнопок │ ├─ Title (TextLabel) — «Башни» │ ├─ UICorner (UICorner) — скругление углов │ ├─ UIListLayout (UIListLayout) — кнопки встают в ряд сами │ └─ Sample (ImageButton) — шаблон кнопки, Visible выкл. └─ TowerPlacementPreview (LocalScript)
Где: StarterGui → TowerGui · LocalScript
Зачем: Заменяет три строки gui.Tower.Activated: по кнопке на каждую башню.
-- по каждой башне из ReplicatedStorage.Towers
for _, tower in pairs(towers:GetChildren()) do
-- копия шаблона кнопки
local button = gui.TowerFrame.Sample:Clone()
-- характеристики башни
local configuration = tower:WaitForChild("Configuration")
-- кнопка называется как башня
button.Name = tower.Name
-- картинка башни на кнопке
button.Image = configuration.Image.Texture
-- шаблон скрыт, копию показываем
button.Visible = true
-- в список кнопок
button.Parent = gui.TowerFrame
-- нажали на кнопку
button.Activated:Connect(function()
-- превью этой башни
AddTowerPreview(tower.Name)
-- конец обработчика
end)
-- следующая башня
endГде: ServerScriptService → MainScript · ModuleScript
Зачем: Радиус, урон и перезарядка берутся из Configuration; при выстреле — неоновый луч.
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- удаление объектов по таймеру
local Debris = game:GetService("Debris")
-- сигнал «поставь башню» от клиента
local towerEvent = RS.Events:WaitForChild("TowerEvent")
-- таблица модуля
local tower = {}
-- ищет ближайшего живого врага в радиусе range
local function FindNearestTarget(towerModel, range)
-- пока никого
local nearestTarget = nil
-- все враги на карте
for _, target in ipairs(workspace.Mobs:GetChildren()) do
-- мёртвых не считаем
if target.Humanoid.Health > 0 then
-- расстояние до башни
local distance = (target.HumanoidRootPart.Position - towerModel.PrimaryPart.Position).Magnitude
-- ближе лучшего найденного
if distance < range then
-- запоминаем врага
nearestTarget = target
-- теперь ищем ещё ближе
range = distance
-- конец проверки расстояния
end
-- конец проверки здоровья
end
-- конец перебора
end
-- ближайший враг или nil
return nearestTarget
-- конец функции
end
-- луч от башни к врагу
local function Trail(towerModel, target)
-- новый парт-луч
local trail = Instance.new("Part")
-- длина луча
local distance = (towerModel.PrimaryPart.Position - target.PrimaryPart.Position).Magnitude
-- тонкий и длинный
trail.Size = Vector3.new(0.1, 0.1, distance)
-- сдвиг на полдлины, чтобы луч начинался у башни
local offset = CFrame.new(0, 0, -distance / 2)
-- направляем на врага
trail.CFrame = CFrame.new(towerModel.PrimaryPart.Position, target.PrimaryPart.Position) * offset
-- не падает
trail.Anchored = true
-- ни с чем не сталкивается
trail.CanCollide = false
-- полупрозрачный
trail.Transparency = 0.5
-- цвет луча этой башни
trail.Color = towerModel.Configuration.Trail.Value
-- светится
trail.Material = Enum.Material.Neon
-- показываем
trail.Parent = workspace
-- через 0,1 с удаляется сам
Debris:AddItem(trail, 0.1)
-- конец функции
end
-- башня стреляет, пока стоит на карте
function tower.Attack(towerModel)
-- характеристики башни
local configuration = towerModel:WaitForChild("Configuration")
-- башня ещё на карте
while towerModel.Parent do
-- цель в радиусе Range
local target = FindNearestTarget(towerModel, configuration.Range.Value)
-- цель есть
if target then
-- рисуем луч
Trail(towerModel, target)
-- урон Damage
target.Humanoid:TakeDamage(configuration.Damage.Value)
-- перезарядка Cooldown
task.wait(configuration.Cooldown.Value)
-- целей нет
else
-- проверим снова чуть позже
task.wait(0.1)
-- конец проверки
end
-- следующий выстрел
end
-- конец функции
end
-- ставит башню по сигналу от клиента
function tower.Spawn(player, name, cframe)
-- образец башни
local towerModel = RS.Towers:FindFirstChild(name)
-- такая башня есть
if towerModel then
-- копия
local towerClone = towerModel:Clone()
-- ставим всю модель туда, где выбрал игрок
towerClone:PivotTo(cframe)
-- на карту
towerClone.Parent = workspace.Towers
-- все детали башни
for _, obj in pairs(towerClone:GetDescendants()) do
-- только физические
if obj:IsA("BasePart") then
-- группа коллизий Tower
obj.CollisionGroup = "Tower"
-- конец проверки
end
-- конец перебора
end
-- атака параллельно, скрипт не ждёт
coroutine.wrap(tower.Attack)(towerClone)
-- конец проверки
end
-- конец функции
end
-- клиент прислал сигнал → ставим башню
towerEvent.OnServerEvent:Connect(tower.Spawn)
-- отдаём таблицу модуля
return towerЗапусти Play (или Run) и убедись: