Roblox · Тайкун · урок 5

Сохранение прогресса

Деньги в кошельке, деньги в кассе и всё купленное сохраняются в DataStore и возвращаются при следующем входе.

Структура

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

ServerScriptService
└─ Stats (Script) — переписан
Workspace
└─ IndustryModel (Model)
   └─ Buttons (Folder)
      ├─ BuyButtons (Folder)
      │  └─ CandyDropper (Part) — бывший CandyDropperButton
      └─ GainButtons (Folder)
         └─ … CollectGainScript (Script) — обновлён
Player (во время игры) — создаёт Stats
├─ leaderstats → Money — кошелёк
├─ GainIndustry (NumberValue) — копия кассы
└─ ItemsFolder (Folder) — список покупок

Шаги

  1. Игру нужно опубликовать: File → Publish to Roblox.
  2. Game Settings → Security → Enable Studio Access to API Services → Save. Без этого в Studio сохранение не работает.
  3. DataStore работает только в серверных скриптах. Вызовы — через pcall: сеть может подвести.
  4. Кнопки покупки переименуй как покупаемые предметы: CandyDropperButton → CandyDropper.
  5. Улучшения из UpgradeButtons этот скрипт не сохраняет — только кошелёк, кассу и купленное.

Скрипты

Stats

Где: ServerScriptService · Script
Зачем: Создаёт кошелёк и список покупок; при входе загружает сохранение, при выходе и остановке сервера — записывает.

-- сервис сохранений
local DSS = game:GetService("DataStoreService")
-- наше хранилище
local store = DSS:GetDataStore("TycoonData")
-- серверное хранилище
local SS = game:GetService("ServerStorage")
-- производство
local tycoon = workspace.IndustryModel
-- купленное оборудование
local ownedItems = tycoon.Owned_Items
-- кнопки покупки
local buttons = tycoon.Buttons.BuyButtons

-- сохраняет данные игрока
local function Save(player)
    -- данные не загрузились — не затираем сохранение нулями
    if not player:GetAttribute("DataLoaded") then return end
    -- список купленного
    local items = {}
    -- всё, что игрок купил
    for _, item in pairs(player.ItemsFolder:GetChildren()) do
        -- запоминаем имя
        table.insert(items, item.Name)
    -- конец перебора
    end
    -- кошелёк, касса, покупки
    local data = {Money = player.leaderstats.Money.Value, Gain = player.GainIndustry.Value, Items = items}
    -- защищённый вызов: сеть может подвести
    local success, err = pcall(function()
        -- записываем по ID игрока
        store:SetAsync(player.UserId, data)
    -- конец защищённого вызова
    end)
    -- не получилось — пишем причину в Output
    if not success then warn(err) end
-- конец функции
end

-- игрок зашёл
game.Players.PlayerAdded:Connect(function(player)
    -- папка для таблицы лидеров
    local leaderstats = Instance.new("Folder", player)
    -- имя, которое понимает Roblox
    leaderstats.Name = "leaderstats"
    -- кошелёк
    local money = Instance.new("NumberValue", leaderstats)
    -- называем Money
    money.Name = "Money"
    -- копия кассы — чтобы сохранить её при выходе
    local gainIndustry = Instance.new("NumberValue", player)
    -- называем GainIndustry
    gainIndustry.Name = "GainIndustry"
    -- список покупок игрока
    local itemsFolder = Instance.new("Folder", player)
    -- называем ItemsFolder
    itemsFolder.Name = "ItemsFolder"
    -- что-то куплено
    ownedItems.ChildAdded:Connect(function(child)
        -- записываем в список покупок
        local item = Instance.new("StringValue", itemsFolder)
        -- по имени предмета
        item.Name = child.Name
    -- конец обработчика
    end)

    -- защищённый вызов
    local success, data = pcall(function()
        -- читаем сохранение
        return store:GetAsync(player.UserId)
    -- конец защищённого вызова
    end)
    -- прочитали — при выходе можно сохранять
    if success then player:SetAttribute("DataLoaded", true) end
    -- прочитали, и сохранение есть (у нового игрока его нет)
    if success and data then
        -- кошелёк
        money.Value = data.Money or 0
        -- касса
        gainIndustry.Value = data.Gain or 0
        -- возвращаем деньги в кассу производства
        tycoon.Values.Gain.Value = gainIndustry.Value
        -- все покупки
        for _, name in pairs(data.Items or {}) do
            -- образец в ServerStorage
            local sample = SS.Conveyors:FindFirstChild(name)
            -- образец есть и ещё не стоит
            if sample and not ownedItems:FindFirstChild(name) then
                -- копия
                local item = sample:Clone()
                -- ставим в производство
                item.Parent = ownedItems
                -- кнопка с тем же именем
                local button = buttons:FindFirstChild(name)
                -- уже куплено — кнопку убираем
                if button then button:Destroy() end
            -- конец проверки
            end
        -- конец перебора
        end
    -- сервис недоступен
    elseif not success then
        -- причина — в Output
        warn(data)
    -- конец проверки
    end
-- конец обработчика
end)

-- игрок вышел — сохраняем
game.Players.PlayerRemoving:Connect(Save)
-- сервер выключается (и Stop в Studio)
game:BindToClose(function()
    -- всех, кто ещё в игре
    for _, player in pairs(game.Players:GetPlayers()) do
        -- сохраняем
        Save(player)
    -- конец перебора
    end
-- конец функции
end)

CollectGainScript

Где: … → GainButtons → CollectGainButton → GainButton → ClickDetector · Script
Зачем: Дополнительно держит копию кассы у владельца — её сохраняет Stats.

-- ClickDetector кнопки кассы
local clicker = script.Parent
-- экран: ClickDetector → GainButton → CollectGainButton
local screen = clicker.Parent.Parent.GainScreen
-- Values: CollectGainButton → GainButtons → Buttons → IndustryModel
local valuesFolder = screen.Parent.Parent.Parent.Parent.Values

-- нажали на кнопку кассы
clicker.MouseClick:Connect(function(player)
    -- нажал владелец
    if valuesFolder.Owner.Value == player then
        -- сколько в кассе
        local gain = valuesFolder.Gain.Value
        -- сначала обнуляем — двойной клик не удвоит деньги
        valuesFolder.Gain.Value = 0
        -- переводим деньги в кошелёк
        player.leaderstats.Money.Value += gain
    -- конец проверки
    end
-- конец обработчика нажатия
end)

-- сумма в кассе изменилась
valuesFolder.Gain.Changed:Connect(function(newGain)
    -- показываем её на экране
    screen.GainScreenGui.GainText.Text = newGain .. " $"
    -- владелец
    local owner = valuesFolder.Owner.Value
    -- владелец есть и у него есть копия кассы
    if owner and owner:FindFirstChild("GainIndustry") then
        -- обновляем копию — её сохранит Stats
        owner.GainIndustry.Value = newGain
    -- конец проверки
    end
-- конец обработчика
end)

Проверка

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