Roblox · Pet Simulator · урок 5
Монеты, кристаллы и питомцы сохраняются: вышел и зашёл — всё на месте.
Синим — что добавляем на этом уроке.
ServerScriptService └─ MainScript (Script) — вызывает Load и Save └─ SaveData (ModuleScript)
Где: ServerScriptService → MainScript · ModuleScript
Зачем: Загрузка и сохранение данных игрока.
-- хранилище данных Roblox
local DSS = game:GetService("DataStoreService")
-- все игроки
local Players = game:GetService("Players")
-- наше хранилище
local dataStore = DSS:GetDataStore("PetSimulatorData")
-- какие папки игрока сохраняем (Values — нет)
local SAVE = {"leaderstats", "Pets", "Areas"}
-- таблица модуля
local saveData = {}
-- у кого данные загрузились
local loaded = {}
-- загрузить данные игрока
function saveData.Load(player)
-- запрос может не пройти
local ok, data = pcall(function()
-- данные по номеру игрока
return dataStore:GetAsync(player.UserId .. "Key")
-- конец попытки
end)
-- не загрузилось
if not ok then
-- без загрузки не сохраняем — иначе затрём
warn("Данные не загрузились (" .. tostring(data) .. ") — сохранение для " .. player.Name .. " выключено")
-- играем без сохранения
return
-- конец проверки
end
-- можно сохранять
loaded[player] = true
-- новый игрок — данных ещё нет
if not data then return end
-- каждая сохраняемая папка
for _, folderName in ipairs(SAVE) do
-- папка в игроке
local folder = player:FindFirstChild(folderName)
-- что сохранено
local saved = data[folderName]
-- есть и то и другое
if folder and saved then
-- каждое значение
for _, item in ipairs(saved) do
-- такое значение уже есть (Coins, Gems)
local existing = folder:FindFirstChild(item[1])
-- есть
if existing then
-- просто меняем число
existing.Value = item[2]
-- нет (питомцы, области)
else
-- создаём того же класса
local value = Instance.new(item[3])
-- имя
value.Name = item[1]
-- значение
value.Value = item[2]
-- в папку
value.Parent = folder
-- конец проверки
end
-- конец перебора
end
-- конец проверки
end
-- конец перебора
end
-- конец функции
end
-- сохранить данные игрока
function saveData.Save(player)
-- не загружали — не трогаем
if not loaded[player] then return end
-- второй раз не сохраняем
loaded[player] = nil
-- что запишем
local data = {}
-- каждая папка
for _, folderName in ipairs(SAVE) do
-- в игроке
local folder = player:FindFirstChild(folderName)
-- есть
if folder then
-- список значений
data[folderName] = {}
-- каждое значение
for _, value in ipairs(folder:GetChildren()) do
-- имя, значение, класс
table.insert(data[folderName], {value.Name, value.Value, value.ClassName})
-- конец перебора
end
-- конец проверки
end
-- конец перебора
end
-- запись может не пройти
local ok, err = pcall(function()
-- записываем
dataStore:SetAsync(player.UserId .. "Key", data)
-- конец попытки
end)
-- результат в Output
if ok then print("Сохранено: " .. player.Name) else warn(err) end
-- конец функции
end
-- сохранить всех и дождаться (выключение сервера)
function saveData.SaveAll()
-- сколько сохранений ещё идёт
local left = 0
-- каждый игрок
for _, player in ipairs(Players:GetPlayers()) do
-- ещё одно
left += 1
-- параллельно
task.spawn(function()
-- сохраняем
saveData.Save(player)
-- готово
left -= 1
-- конец потока
end)
-- конец перебора
end
-- ждём все сохранения
while left > 0 do task.wait() end
-- конец функции
end
-- отдаём таблицу модуля
return saveDataГде: ServerScriptService · Script
Зачем: + загрузка при входе, сохранение при выходе и выключении.
-- все игроки
local Players = game:GetService("Players")
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- модуль питомцев
local pet = require(script.Pet)
-- модуль целей
local drop = require(script.Pet.Drop)
-- сохранение
local saveData = require(script.SaveData)
-- события
local events = RS:WaitForChild("Events")
-- надеть / снять питомца
local equipFunction = events:WaitForChild("EquipPetFunction")
-- образцы питомцев
local pets = RS:WaitForChild("Pets")
-- питомцы игроков на карте
local playerPets = workspace:WaitForChild("PlayerPets")
-- цели
local drops = workspace:WaitForChild("Drops")
-- сколько питомцев можно взять с собой
local MAX_EQUIPPED = 3
-- игрок зашёл
Players.PlayerAdded:Connect(function(player)
-- папка для его питомцев
local folder = Instance.new("Folder")
-- имя = имя игрока
folder.Name = player.Name
-- в PlayerPets
folder.Parent = playerPets
-- папки из PlayerInfo
for _, info in ipairs(script.PlayerInfo:GetChildren()) do
-- копия — в игрока
info:Clone().Parent = player
-- конец перебора
end
-- загружаем монеты, питомцев, области
saveData.Load(player)
-- конец обработчика
end)
-- игрок вышел
Players.PlayerRemoving:Connect(function(player)
-- сохраняем
saveData.Save(player)
-- его папка
local folder = playerPets:FindFirstChild(player.Name)
-- убираем питомцев
if folder then folder:Destroy() end
-- конец обработчика
end)
-- сервер выключается — сохраняем всех и ждём
game:BindToClose(saveData.SaveAll)
-- подготовить цель
local function SetupDrop(dropObject)
-- полоска здоровья
drop.UpdateGui(dropObject)
-- клик по цели — подключаем ОДИН раз
dropObject.ClickDetector.MouseClick:Connect(function(player)
-- питомцы атакуют
pet.Attack(player, dropObject)
-- конец обработчика
end)
-- конец функции
end
-- цели, что уже стоят
for _, obj in ipairs(drops:GetDescendants()) do
-- это цель
if obj:IsA("Model") and obj:FindFirstChild("ClickDetector") then SetupDrop(obj) end
-- конец перебора
end
-- цель появилась снова
drops.DescendantAdded:Connect(function(obj)
-- подключаем и её
if obj:IsA("Model") and obj:FindFirstChild("ClickDetector") then SetupDrop(obj) end
-- конец обработчика
end)
-- отдельный поток
task.spawn(function()
-- каждую секунду
while task.wait(1) do
-- питомцы наносят урон
pet.Tick()
-- конец цикла
end
-- конец потока
end)
-- игрок нажал на иконку питомца
equipFunction.OnServerInvoke = function(player, petName, equip)
-- прислали не текст
if typeof(petName) ~= "string" then return false end
-- его питомцы на карте
local folder = playerPets:FindFirstChild(player.Name)
-- образец
local template = pets:FindFirstChild(petName)
-- нет папки или такого питомца
if not folder or not template then return false end
-- снять
if not equip then
-- этот питомец на карте
local active = folder:FindFirstChild(petName)
-- убираем
if active then active:Destroy() end
-- готово
return true
-- конец проверки
end
-- сколько таких есть и сколько уже надето
local owned, active = 0, 0
-- купленные питомцы
for _, value in ipairs(player.Pets:GetChildren()) do
-- считаем таких
if value.Name == petName then owned += 1 end
-- конец перебора
end
-- надетые
for _, petModel in ipairs(folder:GetChildren()) do
-- считаем таких
if petModel.Name == petName then active += 1 end
-- конец перебора
end
-- не свой или уже максимум
if active >= owned or #folder:GetChildren() >= MAX_EQUIPPED then return false end
-- питомец
local clone = template:Clone()
-- появляется у хозяина
if player.Character then clone:PivotTo(player.Character:GetPivot()) end
-- сразу к текущей цели
clone.Attack.Value = player.Values.Target.Value
-- на карту
clone.Parent = folder
-- надет
return true
-- конец функции
endЗапусти Play (или Run) и убедись: