Roblox · Сбор-а-тон · урок 2

Предметы и сбор

Прячем предметы и учим игру засчитывать находки: клик по предмету — сообщение «Найдено!», и находка сохраняется.

Структура

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

Workspace
└─ Items (Folder)
   └─ Красный кристалл (Part) — имя = название предмета
      ├─ Decal (Decal) — картинка для журнала
      ├─ Difficulty (StringValue) — Easy, Medium или Hard
      ├─ Description (StringValue) — описание
      └─ BadgeID (NumberValue) — 0 или ID нашивки
ReplicatedStorage
└─ Events (Folder)
   └─ CollectEvent (RemoteEvent)
ServerScriptService
└─ CollectServer (Script)
StarterGui
└─ CollectGui (ScreenGui) — ResetOnSpawn выкл.
   ├─ Message (TextLabel) — Visible выкл.
   ├─ Found (Sound)
   └─ CollectLocal (LocalScript)

Шаги

  1. Workspace → Folder «Items». Каждый предмет — одна деталь (Anchored вкл.), имя — название по-русски: «Старый ключ».
  2. Внутри: Decal (картинка для журнала), StringValue «Difficulty» = Easy / Medium / Hard, StringValue «Description», NumberValue «BadgeID».
  3. Нашивки (Badges) из методички — по желанию: создаются на create.roblox.com у опубликованной игры, в Studio не проверяются. Нет нашивки — оставь BadgeID = 0, находка всё равно засчитается.
  4. Находки хранятся в папке Found у игрока (видна и ему самому) и в DataStore. Для сохранения в Studio — Game Settings → Security → Enable Studio Access to API Services.
  5. ClickDetector скрипт ставит сам, один раз на предмет (в методичке — заново на каждого вошедшего игрока).
  6. CollectGui: TextLabel «Message» вверху по центру, Sound «Found», LocalScript «CollectLocal».

Скрипты

CollectServer

Где: ServerScriptService · Script
Зачем: Клики, находки, сохранение.

-- игроки
local Players = game:GetService("Players")
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- сохранение
local DSS = game:GetService("DataStoreService")
-- нашивки (по желанию)
local BadgeService = game:GetService("BadgeService")
-- все предметы
local items = workspace:WaitForChild("Items")
-- события
local events = RS:WaitForChild("Events")
-- «нашёл предмет»
local collectEvent = events:WaitForChild("CollectEvent")
-- хранилище находок
local store = DSS:GetDataStore("CollectData")
-- у кого находки загрузились
local loaded = {}

-- сохранить находки
local function Save(player)
    -- не загрузились — не затираем
    if not loaded[player] then return end
    -- имена найденных
    local list = {}
    -- каждая находка
    for _, mark in ipairs(player.Found:GetChildren()) do
        -- в список
        table.insert(list, mark.Name)
    -- конец перебора
    end
    -- сохраняем
    pcall(function() store:SetAsync("found_" .. player.UserId, list) end)
-- конец функции
end

-- игрок нашёл предмет
local function Collect(player, item)
    -- его находки
    local found = player:FindFirstChild("Found")
    -- уже нашёл
    if not found or found:FindFirstChild(item.Name) then return end
    -- отметка
    local mark = Instance.new("BoolValue")
    -- имя = предмет
    mark.Name = item.Name
    -- в папку игрока
    mark.Parent = found
    -- сообщение на экране
    collectEvent:FireClient(player, item.Name)
    -- ID нашивки
    local badge = item:FindFirstChild("BadgeID")
    -- нашивка настроена
    if badge and badge.Value > 0 then
        -- не задерживаем остальное
        task.spawn(function()
            -- выдаём; в Studio может не сработать
            pcall(function() BadgeService:AwardBadge(player.UserId, badge.Value) end)
        -- конец функции
        end)
    -- конец проверки
    end
-- конец функции
end

-- игрок зашёл
local function Setup(player)
    -- его находки
    local found = Instance.new("Folder")
    -- имя
    found.Name = "Found"
    -- загружаем
    local ok, list = pcall(function() return store:GetAsync("found_" .. player.UserId) end)
    -- загрузка удалась
    if ok then
        -- каждое сохранённое имя
        for _, name in ipairs(list or {}) do
            -- такой предмет ещё есть
            if items:FindFirstChild(name) then
                -- отметка
                local mark = Instance.new("BoolValue")
                -- имя
                mark.Name = name
                -- в папку
                mark.Parent = found
            -- конец проверки
            end
        -- конец перебора
        end
        -- можно сохранять
        loaded[player] = true
    -- конец проверки
    end
    -- в игрока — клиент видит
    found.Parent = player
-- конец функции
end

-- новые игроки
Players.PlayerAdded:Connect(Setup)
-- кто зашёл раньше скрипта
for _, player in ipairs(Players:GetPlayers()) do task.spawn(Setup, player) end
-- игрок вышел
Players.PlayerRemoving:Connect(function(player)
    -- сохраняем
    Save(player)
    -- забываем
    loaded[player] = nil
-- конец функции
end)
-- сервер закрывается
game:BindToClose(function()
    -- сохраняем всех
    for _, player in ipairs(Players:GetPlayers()) do Save(player) end
-- конец функции
end)

-- каждый предмет — один раз
for _, item in ipairs(items:GetChildren()) do
    -- клик
    local clicker = item:FindFirstChildOfClass("ClickDetector") or Instance.new("ClickDetector")
    -- кликнуть можно только рядом
    clicker.MaxActivationDistance = 20
    -- в предмет
    clicker.Parent = item
    -- кто-то кликнул
    clicker.MouseClick:Connect(function(player)
        -- засчитываем
        Collect(player, item)
    -- конец функции
    end)
-- конец перебора
end

CollectLocal

Где: StarterGui → CollectGui · LocalScript
Зачем: Сообщение о находке.

-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- «нашёл предмет»
local collectEvent = RS:WaitForChild("Events"):WaitForChild("CollectEvent")
-- CollectGui
local gui = script.Parent
-- надпись
local message = gui:WaitForChild("Message")
-- звук находки
local sound = gui:WaitForChild("Found")
-- номер последнего сообщения
local shown = 0

-- сервер засчитал находку
collectEvent.OnClientEvent:Connect(function(name)
    -- новое сообщение
    shown += 1
    -- его номер
    local my = shown
    -- текст
    message.Text = "Найдено: " .. name .. "!"
    -- показываем
    message.Visible = true
    -- звук
    sound:Play()
    -- ждём
    task.wait(2.5)
    -- прячем, если не пришло новое
    if shown == my then message.Visible = false end
-- конец функции
end)

Проверка

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