Загрузка при входе
Урок 20 из 20 курса Игра в Roblox: симулятор питомцев на Coddy.
Теперь считай сохранённые данные, когда игрок присоединяется. GetAsync возвращает то, что SetAsync сохранил под ключом, или nil, если игрок ещё никогда ничего не сохранял. Вызов тоже может завершиться ошибкой, поэтому вызывай его в pcall, который возвращает, сработал ли вызов, а затем значение:
local ok, saved = pcall(function()
return saveStore:GetAsync(player.UserId)
end)
if ok and saved then
print(saved.Coins)
endЗагружай данные после создания папок, чтобы у питомцев была папка Pets, в которую их можно поместить. Пропускай сохранённые имена, которых нет в petInfo: питомец, удалённый из игры, не должен мешать присоединению игрока.
Задание
СложноВерни игрокам то, что они сохранили.
- В
PlayerAdded, после создания папки Pets:local ok, saved = pcall(function() return saveStore:GetAsync(player.UserId) end). - Если
ok and saved: установи значение Coins вsaved.Coins, а атрибутDesert— вsaved.Desert. - Для каждого сохранённого питомца в
petInfoвызовиgivePet(player, pet.Name, pet.Equipped == true).
Нажми «Играть», вылупи питомца, нажми «Остановить», затем снова «Играть»: твои монеты и питомцы вернутся. Затем нажми «Проверить».
Попробуйте сами
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local RunService = game:GetService("RunService")
local DataStoreService = game:GetService("DataStoreService")
local Settings = require(game.ReplicatedStorage.Settings)
local remotes = game.ReplicatedStorage.Remotes
local piles = workspace.Piles
local petParts = workspace.PetParts
local saveStore = DataStoreService:GetDataStore("Pets")
local pileSpot = {} -- каждая куча -> место, на котором она находится
local lastHit = {} -- каждый игрок -> время его последнего удара
print("A Meadow pile gives " .. Settings.PileValue .. " coins")
-- Settings.Pets по имени
local petInfo = {}
for _, info in Settings.Pets do
petInfo[info.Name] = info
end
-- Питомец: StringValue в папке Pets игрока, содержащее его имя, а
-- его редкость и сила хранятся в атрибутах
local function givePet(player, name, equipped)
local info = petInfo[name]
local pet = Instance.new("StringValue")
pet.Name = "Pet"
pet.Value = name
pet:SetAttribute("Rarity", info.Rarity)
pet:SetAttribute("Power", info.Power)
pet:SetAttribute("Equipped", equipped)
pet.Parent = player.Pets
return pet
end
-- сколько питомцев игрок выпустил
local function equippedCount(player)
local count = 0
for _, pet in player.Pets:GetChildren() do
if pet:GetAttribute("Equipped") then
count += 1
end
end
return count
end
-- сколько силы питомцы игрока добавляют к каждому удару
local function petPower(player)
local power = 0
for _, pet in player.Pets:GetChildren() do
if pet:GetAttribute("Equipped") then
power += pet:GetAttribute("Power")
end
end
return power
end
-- Каждый игрок получает Coins в таблице лидеров
Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local coins = Instance.new("IntValue")
coins.Name = "Coins"
coins.Parent = leaderstats
-- питомцы, которыми они владеют
local pets = Instance.new("Folder")
pets.Name = "Pets"
pets.Parent = player
-- Урок 20: здесь будет загрузка при входе
end)
-- Куча монет на месте: копия ServerStorage.CoinPile, у которой запас прочности
-- и стоимость хранятся в атрибутах
local function spawnPile(spot)
local desert = spot.Parent.Parent.Name == "Desert"
local health = desert and Settings.DesertHealth or Settings.PileHealth
local pile = ServerStorage.CoinPile:Clone()
pile.Position = spot.Position
pile:SetAttribute("MaxHealth", health)
pile:SetAttribute("Health", health)
pile:SetAttribute("Value", desert and Settings.DesertValue or Settings.PileValue)
pile:SetAttribute("Zone", spot.Parent.Parent.Name)
pile.HealthGui.Label.Text = pile:GetAttribute("Health") .. "/" .. pile:GetAttribute("MaxHealth")
pileSpot[pile] = spot
pile.Parent = piles
return pile
end
-- Удар отнимает у кучи 1 единицу здоровья плюс силу питомцев игрока;
-- когда здоровье падает до 0, куча разрушается и приносит монеты по своей стоимости
local function hitPile(player, pile)
local health = pile:GetAttribute("Health") - (1 + petPower(player))
if health > 0 then
pile:SetAttribute("Health", health)
pile.HealthGui.Label.Text = health .. "/" .. pile:GetAttribute("MaxHealth")
return
end
player.leaderstats.Coins.Value += pile:GetAttribute("Value")
local spot = pileSpot[pile]
pileSpot[pile] = nil
pile:Destroy()
-- через несколько секунд на том же месте появляется новая куча
task.delay(Settings.RespawnTime, spawnPile, spot)
end
-- По куче на каждом месте в каждой зоне
for _, zone in workspace.Zones:GetChildren() do
for _, spot in zone.Spawns:GetChildren() do
spawnPile(spot)
end
end
-- Клиент запрашивает удар по куче. Никогда не доверяйте клиенту: кто угодно может вызвать
-- это удалённое событие с любыми данными, откуда угодно и с любой частотой.
remotes.Hit.OnServerEvent:Connect(function(player, pile)
-- настоящая куча
if typeof(pile) ~= "Instance" or pile.Parent ~= piles then
return
end
-- в пределах досягаемости
local character = player.Character
local root = character and character:FindFirstChild("HumanoidRootPart")
if not root or (root.Position - pile.Position).Magnitude > Settings.Reach then
return
end
-- не слишком часто
local now = time()
if lastHit[player] and now - lastHit[player] < Settings.HitCooldown then
return
end
lastHit[player] = now
-- в зоне, которую игрок открыл
if pile:GetAttribute("Zone") == "Desert" and not player:GetAttribute("Desert") then
return
end
hitPile(player, pile)
end)
-- Взвешенный выбор: Chance каждого питомца (из 100) — его доля в общем диапазоне
local function rollPet()
local roll = math.random() * 100
for _, info in Settings.Pets do
roll -= info.Chance
if roll <= 0 then
return info.Name
end
end
return Settings.Pets[1].Name
end
-- Вылупление: взаимодействие с яйцом за монеты
workspace.Egg.ProximityPrompt.Triggered:Connect(function(player)
local coins = player.leaderstats.Coins
if coins.Value < Settings.EggPrice then
return
end
coins.Value -= Settings.EggPrice
local name = rollPet()
-- Сразу выпускаем, если есть место
local pet = givePet(player, name, equippedCount(player) < Settings.MaxEquipped)
print(player.Name .. " hatched a " .. name)
-- Все узнают о самых редких питомцах
if pet:GetAttribute("Rarity") == "Legendary" then
remotes.Announce:FireAllClients(player.Name, name)
end
end)
-- Выпущенные питомцы следуют за хозяином: у каждого есть объект в игровом мире, который
-- в каждом кадре располагается в ряду позади игрока
local petModels = {} -- каждый питомец -> его объект в игровом мире
RunService.Heartbeat:Connect(function()
for _, player in Players:GetPlayers() do
local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
local pets = player:FindFirstChild("Pets")
local slot = 0
for _, pet in pets and pets:GetChildren() or {} do
local model = petModels[pet]
if pet:GetAttribute("Equipped") and root then
slot += 1
if not model then
model = ServerStorage.PetModels[pet.Value]:Clone()
model.Parent = petParts
petModels[pet] = model
end
model.CFrame = root.CFrame * CFrame.new((slot - 2) * 3, -1.5, 4)
elseif model then
-- убираем питомца: его объект исчезает
model:Destroy()
petModels[pet] = nil
end
end
end
end)
-- Клиент просит выпустить или убрать питомца. Сервер проверяет, что питомец
-- принадлежит игроку и что выпущено не больше MaxEquipped питомцев.
remotes.Equip.OnServerEvent:Connect(function(player, pet)
if typeof(pet) ~= "Instance" or pet.Parent ~= player.Pets then
return
end
if pet:GetAttribute("Equipped") then
pet:SetAttribute("Equipped", false)
elseif equippedCount(player) < Settings.MaxEquipped then
pet:SetAttribute("Equipped", true)
end
end)
-- Ворота в Desert: заплатив один раз, игрок открывает их только для себя
workspace.Gate.ProximityPrompt.Triggered:Connect(function(player)
local coins = player.leaderstats.Coins
if player:GetAttribute("Desert") or coins.Value < Settings.GatePrice then
return
end
coins.Value -= Settings.GatePrice
player:SetAttribute("Desert", true)
end)
-- Сохранение: Coins, Desert и всех питомцев
local function save(player)
local pets = {}
for _, pet in player.Pets:GetChildren() do
table.insert(pets, { Name = pet.Value, Equipped = pet:GetAttribute("Equipped") == true })
end
local data = {
Coins = player.leaderstats.Coins.Value,
Desert = player:GetAttribute("Desert") == true,
Pets = pets,
}
pcall(function()
saveStore:SetAsync(player.UserId, data)
end)
end
Players.PlayerRemoving:Connect(save)
game:BindToClose(function()
for _, player in Players:GetPlayers() do
save(player)
end
end)
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Игра в Roblox: симулятор питомцев
Потренируйтесь самостоятельно: Песочница Roblox