Roblox · Песочница · урок 3
Кирка ломает камни, топор рубит деревья — за несколько ударов, с полоской прочности. Инструменты изнашиваются. Лопата копает ямы и насыпает холмы в ландшафте.
Синим — что добавляем на этом уроке.
StarterPack ├─ Pickaxe, Axe (Tool) │ ├─ Handle (Part) — Swing (Sound) │ ├─ Mining (BoolValue) — true — идёт удар │ ├─ Wear (IntValue) — прочность, 50 │ └─ ToolScript (Script) — Animation — взмах └─ Shovel (Tool) — Handle, Terraform (RemoteEvent), ShovelLocal, ShovelServer Workspace └─ Nature (Folder) └─ Tree, Rock (Model) — тег Breakable; атрибуты Tool, Drop, Respawn ├─ MainPart (Part) — невидимая коробка вокруг объекта, CanCollide выкл.; PrimaryPart │ └─ Health (BillboardGui) — Back, Green (Frame) └─ HitsToBreak (IntValue) — сколько ударов выдержит ServerScriptService └─ BreakServer (Script)
Где: StarterPack → Pickaxe / Axe · Script
Зачем: Взмах и износ.
-- кирка или топор
local tool = script.Parent
-- взмах
local anim = script:WaitForChild("Animation")
-- звук взмаха
local swingSound = tool:WaitForChild("Handle"):WaitForChild("Swing")
-- BoolValue: true — идёт удар
local mining = tool:WaitForChild("Mining")
-- IntValue: прочность
local wear = tool:WaitForChild("Wear")
-- взмах идёт
local busy = false
-- клик
tool.Activated:Connect(function()
-- кто держит
local humanoid = tool.Parent:FindFirstChildOfClass("Humanoid")
-- взмах идёт или погиб
if busy or not humanoid or humanoid.Health <= 0 then return end
-- начали
busy = true
-- номер взмаха — один удар за взмах
tool:SetAttribute("Swing", (tool:GetAttribute("Swing") or 0) + 1)
-- звук
swingSound:Play()
-- взмах
if anim.AnimationId ~= "" then humanoid:FindFirstChildOfClass("Animator"):LoadAnimation(anim):Play() end
-- удар
mining.Value = true
-- длина взмаха
task.wait(0.4)
-- конец удара
mining.Value = false
-- можно снова
busy = false
-- конец функции
end)
-- прочность изменилась
wear.Changed:Connect(function(value)
-- сломался
if value <= 0 then tool:Destroy() end
-- конец функции
end)Где: ServerScriptService · Script
Зачем: Разрушение по тегу.
-- теги: все ломаемые объекты помечены «Breakable»
local CS = game:GetService("CollectionService")
-- игроки
local Players = game:GetService("Players")
-- тег
local TAG = "Breakable"
-- через сколько секунд вырастает дерево или камень
local RESPAWN = 60
-- настраиваем один ломаемый объект
local function Setup(model)
-- образец в хранилище или уже настроен
if not model:IsDescendantOf(workspace) or model:GetAttribute("Ready") then return end
-- MainPart — невидимая коробка вокруг объекта
local main = model:IsA("Model") and model.PrimaryPart
-- сколько ударов выдержит
local hits = model:FindFirstChild("HitsToBreak")
-- объект собран неправильно
if not main or not hits then return end
-- копия — чтобы вырастить снова
local sample = model:Clone()
-- настроен
model:SetAttribute("Ready", true)
-- полная прочность
local maxHits = hits.Value
-- полоска прочности
local gui = main:FindFirstChild("Health")
-- пока не били — не видно
if gui then gui.Enabled = false end
-- инструмент → номер последнего засчитанного взмаха
local lastSwing = {}
-- чего-то коснулись
main.Touched:Connect(function(hit)
-- инструмент
local tool = hit:FindFirstAncestorOfClass("Tool")
-- идёт ли удар
local mining = tool and tool:FindFirstChild("Mining")
-- не удар или не тот инструмент
if not mining or not mining.Value or tool.Name ~= model:GetAttribute("Tool") then return end
-- номер взмаха
local swing = tool:GetAttribute("Swing")
-- этот взмах уже засчитан
if lastSwing[tool] == swing then return end
-- запоминаем
lastSwing[tool] = swing
-- удар
hits.Value -= 1
-- прочность инструмента
local wear = tool:FindFirstChild("Wear")
-- изнашивается
if wear then wear.Value -= 1 end
-- полоска есть
if gui then
-- показываем
gui.Enabled = true
-- сколько осталось
gui.Green.Size = UDim2.fromScale(math.max(hits.Value, 0) / maxHits, 1)
-- конец проверки
end
-- ещё держится
if hits.Value > 0 then return end
-- где стоял
local parent = model.Parent
-- сломан
model:Destroy()
-- природный объект
if model:GetAttribute("Respawn") then
-- через минуту вырастет снова
task.delay(RESPAWN, function() sample:Clone().Parent = parent end)
-- конец проверки
end
-- конец функции
end)
-- конец функции
end
-- все, что уже есть
for _, model in ipairs(CS:GetTagged(TAG)) do Setup(model) end
-- новые: поставленные блоки, выросшие деревья
CS:GetInstanceAddedSignal(TAG):Connect(Setup)Где: StarterPack → Shovel · LocalScript
Зачем: Клики лопатой.
-- игроки
local Players = game:GetService("Players")
-- лопата
local tool = script.Parent
-- просим сервер изменить ландшафт
local terraform = tool:WaitForChild("Terraform")
-- куда указывает курсор
local mouse = Players.LocalPlayer:GetMouse()
-- лопата в руках?
local equipped = false
-- взяли
tool.Equipped:Connect(function() equipped = true end)
-- убрали
tool.Unequipped:Connect(function() equipped = false end)
-- уже в руках
if tool.Parent and tool.Parent:FindFirstChildOfClass("Humanoid") then equipped = true end
-- ЛКМ
tool.Activated:Connect(function()
-- копать
terraform:FireServer("Dig", mouse.Hit.Position)
-- конец функции
end)
-- ПКМ
mouse.Button2Down:Connect(function()
-- насыпать землю
if equipped then terraform:FireServer("Raise", mouse.Hit.Position) end
-- конец функции
end)Где: StarterPack → Shovel · Script
Зачем: Яма или холмик.
-- игроки
local Players = game:GetService("Players")
-- лопата
local tool = script.Parent
-- команда от клиента
local terraform = tool:WaitForChild("Terraform")
-- размер ямы или холмика
local RADIUS = 3
-- дальше не дотянуться
local MAX_DISTANCE = 25
-- не чаще
local COOLDOWN = 0.3
-- время прошлого копка
local last = 0
-- клиент копает
terraform.OnServerEvent:Connect(function(player, action, position)
-- лопата не у него или мусор
if Players:GetPlayerFromCharacter(tool.Parent) ~= player or typeof(position) ~= "Vector3" then return end
-- слишком часто или далеко
if os.clock() - last < COOLDOWN or player:DistanceFromCharacter(position) > MAX_DISTANCE then return end
-- запоминаем
last = os.clock()
-- копать
if action == "Dig" then
-- шар воздуха — яма
workspace.Terrain:FillBall(position, RADIUS, Enum.Material.Air)
-- насыпать
elseif action == "Raise" then
-- шар земли — холмик
workspace.Terrain:FillBall(position, RADIUS, Enum.Material.Ground)
-- конец проверки
end
-- конец функции
end)Запусти Play (или Run) и убедись: