Roblox · Песочница · урок 6

Крафт

Верстак: E — меню рецептов, выбираешь предмет, видишь ингредиенты, «Скрафтить» — сервер забирает ресурсы и выдаёт предмет.

Структура

Синим — что добавляем на этом уроке.

Workspace
└─ Workbench (Model)
   └─ Table (Part)
      └─ ProximityPrompt (ProximityPrompt) — «Крафт»
         └─ OpenCraft (Script)
ReplicatedStorage
├─ CraftOpenEvent (RemoteEvent)
└─ CraftFunction (RemoteFunction)
ServerStorage
└─ Tools (Folder) — Pickaxe, Axe, Shovel — образцы для крафта
ServerScriptService
└─ CraftServer (Script)
StarterGui
└─ CraftGui (ScreenGui) — ResetOnSpawn выкл.
   └─ MainFrame (Frame) — Visible выкл.
      ├─ Workbench (TextLabel) — заголовок
      ├─ CloseButton (TextButton)
      ├─ RecipesFrame (ScrollingFrame) — UIListLayout, Sample (TextButton, Visible выкл.)
      ├─ CraftFrame (Frame) — Object, Ingredients, Message, CraftButton, BackButton; Visible выкл.
      └─ CraftLocal (LocalScript)

Шаги

  1. Принципы крафта: созданные вещи изнашиваются (кирка ломается) и добывают лучше, чем руки — так чувствуется прогресс.
  2. Рецепты — в модуле Items (Items.Recipes): Glass = {Stone = 2}, Pickaxe = {Wood = 1, Stone = 2} и т. д. Новый рецепт — новая строчка.
  3. Верстак: модель со столом, в нём ProximityPrompt (ActionText «Крафт») со Script «OpenCraft».
  4. CraftGui → MainFrame: TextLabel «Workbench», «CloseButton», ScrollingFrame «RecipesFrame» с UIListLayout и шаблоном кнопки «Sample», Frame «CraftFrame» (Object, Ingredients, Message, CraftButton, BackButton).
  5. Кнопки рецептов CraftLocal делает сам по модулю Items. Кнопка «Скрафтить» подписана один раз (в методичке — при каждом открытии рецепта, и крафт шёл по нескольку раз).
  6. CraftServer проверяет ингредиенты в инвентаре, забирает их и выдаёт: инструмент — в рюкзак (из ServerStorage.Tools), блок — в инвентарь.

Скрипты

OpenCraft

Где: Workspace → Workbench → Table → ProximityPrompt · Script
Зачем: Открывает меню.

-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- ProximityPrompt верстака
local prompt = script.Parent
-- открыть меню крафта
local openEvent = RS:WaitForChild("CraftOpenEvent")

-- игрок нажал E у верстака
prompt.Triggered:Connect(function(player)
    -- меню открывается только у него
    openEvent:FireClient(player)
-- конец функции
end)

CraftLocal

Где: StarterGui → CraftGui → MainFrame · LocalScript
Зачем: Меню крафта.

-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- рецепты и названия
local Items = require(RS:WaitForChild("Items"))
-- верстак позвал
local openEvent = RS:WaitForChild("CraftOpenEvent")
-- попросить сервер скрафтить
local craftFunction = RS:WaitForChild("CraftFunction")
-- окно крафта
local mainFrame = script.Parent
-- список рецептов
local recipesFrame = mainFrame:WaitForChild("RecipesFrame")
-- шаблон кнопки рецепта
local sample = recipesFrame:WaitForChild("Sample")
-- один рецепт
local craftFrame = mainFrame:WaitForChild("CraftFrame")
-- что сейчас крафтим
local selected = nil

-- открыть рецепт
local function Show(item)
    -- запоминаем
    selected = item
    -- название
    craftFrame.Object.Text = Items.Names[item] or item
    -- строчки ингредиентов
    local parts = {}
    -- каждый ингредиент
    for name, amount in pairs(Items.Recipes[item]) do
        -- «Дерево × 2»
        table.insert(parts, (Items.Names[name] or name) .. " × " .. amount)
    -- конец перебора
    end
    -- список
    craftFrame.Ingredients.Text = "Нужно: " .. table.concat(parts, ", ")
    -- без старых сообщений
    craftFrame.Message.Text = ""
    -- прячем список
    recipesFrame.Visible = false
    -- показываем рецепт
    craftFrame.Visible = true
-- конец функции
end

-- кнопка на каждый рецепт — сама
for item in pairs(Items.Recipes) do
    -- копия шаблона
    local button = sample:Clone()
    -- имя
    button.Name = item
    -- название по-русски
    button.Text = Items.Names[item] or item
    -- шаблон был скрыт
    button.Visible = true
    -- в список
    button.Parent = recipesFrame
    -- клик — рецепт
    button.MouseButton1Click:Connect(function() Show(item) end)
-- конец перебора
end

-- «Скрафтить» — подписка одна
craftFrame.CraftButton.MouseButton1Click:Connect(function()
    -- ничего не выбрано
    if not selected then return end
    -- сервер проверит и скрафтит
    local _, text = craftFunction:InvokeServer(selected)
    -- «Создано» или «Не хватает»
    craftFrame.Message.Text = text
-- конец функции
end)
-- назад к рецептам
craftFrame.BackButton.MouseButton1Click:Connect(function()
    -- прячем рецепт
    craftFrame.Visible = false
    -- показываем список
    recipesFrame.Visible = true
-- конец функции
end)
-- закрыть
mainFrame.CloseButton.MouseButton1Click:Connect(function() mainFrame.Visible = false end)
-- верстак позвал
openEvent.OnClientEvent:Connect(function()
    -- сначала список
    craftFrame.Visible = false
    -- рецептов
    recipesFrame.Visible = true
    -- открываем
    mainFrame.Visible = true
-- конец функции
end)

CraftServer

Где: ServerScriptService · Script
Зачем: Крафт с проверкой.

-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- образцы инструментов
local SS = game:GetService("ServerStorage")
-- рецепты
local Items = require(RS:WaitForChild("Items"))
-- инвентарь
local Inventory = require(game:GetService("ServerScriptService"):WaitForChild("Inventory"))
-- запрос крафта
local craftFunction = RS:WaitForChild("CraftFunction")
-- инструменты, которые можно скрафтить
local tools = SS:WaitForChild("Tools")

-- игрок нажал «Скрафтить»
craftFunction.OnServerInvoke = function(player, item)
    -- рецепт
    local recipe = typeof(item) == "string" and Items.Recipes[item]
    -- нет
    if not recipe then return false, "Нет такого рецепта" end
    -- проверяем каждый ингредиент
    for name, amount in pairs(recipe) do
        -- не хватает
        if Inventory.Count(player, name) < amount then
            -- ответ
            return false, "Не хватает: " .. (Items.Names[name] or name)
        -- конец проверки
        end
    -- конец перебора
    end
    -- забираем ингредиенты
    for name, amount in pairs(recipe) do Inventory.Take(player, name, amount) end
    -- это инструмент?
    local tool = tools:FindFirstChild(item)
    -- да
    if tool then
        -- в рюкзак
        tool:Clone().Parent = player.Backpack
    -- блок
    else
        -- в инвентарь
        Inventory.Add(player, item, 1)
    -- конец проверки
    end
    -- ответ
    return true, "Создано: " .. (Items.Names[item] or item)
-- конец функции
end

Проверка

Запусти Play (или Run) и убедись: