Roblox · Дополнительное

Магазин

Прилавок: E — окно со списком товаров и ценами. «Купить» — монеты списываются, предмет в рюкзаке. Товары с пометкой KeepOnDeath остаются и после смерти.

Структура

Синим — что нужно создать.

Workspace
└─ Shop (Model) — прилавок
   └─ Counter (Part)
      └─ ShopPrompt (ProximityPrompt) — имя важно
ReplicatedStorage
├─ ShopItems (Folder)
│  └─ Torch, Sword, … (Tool) — атрибуты Price и KeepOnDeath
└─ BuyItem (RemoteFunction)
ServerScriptService
├─ Coins (ModuleScript) — из раздела «Системы»
└─ ShopServer (Script)
StarterGui
└─ ShopGui (ScreenGui) — ResetOnSpawn выкл.
   └─ Frame (Frame) — Visible выкл.: Title, List (ScrollingFrame + UIListLayout), Status, Close
StarterPlayer → StarterPlayerScripts
└─ ShopLocal (LocalScript)

Шаги

  1. Нужна система монет (раздел «Системы» → «Монеты»). Сборщик поставит её сам.
  2. Товары — готовые Tool в ReplicatedStorage → Folder «ShopItems». Цена — атрибут Price (number), KeepOnDeath (boolean) — товар навсегда: кладётся ещё и в StarterGear.
  3. Прилавок: деталь с ProximityPrompt, имя надписи — «ShopPrompt» (по нему скрипт узнаёт магазин).
  4. ReplicatedStorage → RemoteFunction «BuyItem», ServerScriptService → Script «ShopServer».
  5. StarterGui → ScreenGui «ShopGui» → Frame (Visible выкл.): TextLabel «Title», ScrollingFrame «List» (UIListLayout, AutomaticCanvasSize = Y), TextLabel «Status», TextButton «Close». Строки товаров ShopLocal строит сам по папке ShopItems — новый товар появится в списке без правки скриптов.
  6. Деньги списывает только сервер (Coins.Spend) — купить бесплатно, подделав запрос, нельзя.

Скрипты

ShopServer

Где: ServerScriptService · Script
Зачем: Проверяет монеты и выдаёт товар.

-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- модуль монет (раздел «Системы»)
local Coins = require(game:GetService("ServerScriptService"):WaitForChild("Coins"))
-- товары: Tool с атрибутом Price
local items = RS:WaitForChild("ShopItems")
-- игрок просит купить
local buyItem = RS:WaitForChild("BuyItem")

-- покупка
buyItem.OnServerInvoke = function(player, itemName)
    -- прислали не текст
    if typeof(itemName) ~= "string" then return false, "Ошибка" end
    -- товар
    local item = items:FindFirstChild(itemName)
    -- нет
    if not item then return false, "Нет такого товара" end
    -- остаётся ли после смерти
    local keep = item:GetAttribute("KeepOnDeath")
    -- навсегда купленное второй раз не продаём
    if keep and player.StarterGear:FindFirstChild(itemName) then return false, "Уже куплено" end
    -- списываем, если хватает
    if not Coins.Spend(player, item:GetAttribute("Price") or 0) then return false, "Не хватает монет" end
    -- товар — в рюкзак
    item:Clone().Parent = player.Backpack
    -- и в StarterGear — вернётся после смерти
    if keep then item:Clone().Parent = player.StarterGear end
    -- ответ
    return true, "Куплено: " .. itemName
-- конец функции
end

ShopLocal

Где: StarterPlayer → StarterPlayerScripts · LocalScript
Зачем: Окно магазина и кнопки «Купить».

-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- все надписи ProximityPrompt
local PPS = game:GetService("ProximityPromptService")
-- товары
local items = RS:WaitForChild("ShopItems")
-- купить
local buyItem = RS:WaitForChild("BuyItem")
-- этот игрок
local player = game.Players.LocalPlayer
-- окно магазина
local gui = player:WaitForChild("PlayerGui"):WaitForChild("ShopGui")
-- окно
local frame = gui:WaitForChild("Frame")
-- список товаров
local list = frame:WaitForChild("List")
-- строка ответа
local status = frame:WaitForChild("Status")
-- крестик
local closeButton = frame:WaitForChild("Close")

-- надпись в строке товара
local function Label(parent, text, x, width)
    -- надпись
    local label = Instance.new("TextLabel")
    -- ширина
    label.Size = UDim2.fromScale(width, 1)
    -- место
    label.Position = UDim2.fromScale(x, 0)
    -- без фона
    label.BackgroundTransparency = 1
    -- по размеру
    label.TextScaled = true
    -- белый
    label.TextColor3 = Color3.new(1, 1, 1)
    -- жирный
    label.Font = Enum.Font.GothamBold
    -- текст
    label.Text = text
    -- в строку
    label.Parent = parent
-- конец функции
end

-- собрать список товаров
local function Build()
    -- старые строки
    for _, child in ipairs(list:GetChildren()) do
        -- убираем
        if child:IsA("Frame") then child:Destroy() end
    -- конец перебора
    end
    -- каждый товар
    for i, item in ipairs(items:GetChildren()) do
        -- строка
        local row = Instance.new("Frame")
        -- во всю ширину
        row.Size = UDim2.new(1, -12, 0, 48)
        -- фон
        row.BackgroundColor3 = Color3.fromRGB(50, 50, 65)
        -- порядок
        row.LayoutOrder = i
        -- в список
        row.Parent = list
        -- название
        Label(row, item.Name, 0.02, 0.45)
        -- цена
        Label(row, (item:GetAttribute("Price") or 0) .. " монет", 0.48, 0.25)
        -- кнопка
        local buy = Instance.new("TextButton")
        -- размер
        buy.Size = UDim2.fromScale(0.22, 0.8)
        -- справа
        buy.Position = UDim2.fromScale(0.76, 0.1)
        -- зелёная
        buy.BackgroundColor3 = Color3.fromRGB(70, 170, 90)
        -- белый текст
        buy.TextColor3 = Color3.new(1, 1, 1)
        -- по размеру
        buy.TextScaled = true
        -- жирный
        buy.Font = Enum.Font.GothamBold
        -- надпись
        buy.Text = "Купить"
        -- в строку
        buy.Parent = row
        -- нажали «Купить»
        buy.MouseButton1Click:Connect(function()
            -- просим сервер
            local ok, message = buyItem:InvokeServer(item.Name)
            -- ответ
            status.Text = message
            -- зелёный или красный
            status.TextColor3 = ok and Color3.fromRGB(90, 230, 120) or Color3.fromRGB(240, 80, 80)
        -- конец обработчика
        end)
    -- конец перебора
    end
-- конец функции
end

-- нажал E у надписи
PPS.PromptTriggered:Connect(function(prompt)
    -- это не магазин
    if prompt.Name ~= "ShopPrompt" then return end
    -- свежий список
    Build()
    -- ответа пока нет
    status.Text = ""
    -- открываем
    frame.Visible = true
-- конец обработчика
end)
-- крестик
closeButton.MouseButton1Click:Connect(function()
    -- закрываем
    frame.Visible = false
-- конец обработчика
end)

Проверка

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

Собрать магазин одной командой