Menu
CoddyTech

שמירה בעת יציאה

שיעור 19 מתוך 20 בקורס משחק Roblox: סימולטור חיות מחמד של Coddy.

מטבעות, המדבר וכל חיית מחמד צריכים עדיין להיות שם בפעם הבאה שהשחקן ייכנס. DataStore שומר טבלה לכל שחקן, תחת UserId שלו. חיות מחמד הן מופעים, ו-DataStore לא יכול לשמור אותם, לכן שומרים טבלה רגילה עם השם של כל חיית מחמד והאם היא בחוץ:

{ Coins = 340, Desert = false, Pets = { { Name = "Bunny", Equipped = true } } }

שומרים כשהשחקן עוזב (PlayerRemoving), ואם השרת נסגר כשעדיין יש בו שחקנים, שומרים גם ב-game:BindToClose. השמירה עלולה להיכשל בצד של Roblox, לכן עוטפים אותה ב-pcall:

pcall(function()
	saveStore:SetAsync(player.UserId, data)
end)

ב-Roblox Studio, DataStores פועלים לאחר פרסום המקום והפעלת האפשרות Enable Studio Access to API Services.

challenge icon

אתגר

קשה

שמור את המשחק של כל שחקן כשהוא עוזב.

  1. קבל את DataStoreService ואת local saveStore = DataStoreService:GetDataStore("Pets").
  2. כתוב את save(player): בתוך pcall, את saveStore:SetAsync(player.UserId, data), עם Coins, Desert ו-Pets, רשימה של { Name = pet.Value, Equipped = ... }.
  3. קרא לה בעת Players.PlayerRemoving ובשביל כל שחקן בתוך game:BindToClose.

לחץ על "שחק", הרוויח מטבעות ולחץ על עצור: השמירה מתבצעת כשאתה עוזב. לאחר מכן לחץ על "בדיקה".

נסו בעצמכם

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local RunService = game:GetService("RunService")
local Settings = require(game.ReplicatedStorage.Settings)

local remotes = game.ReplicatedStorage.Remotes
local piles = workspace.Piles
local petParts = workspace.PetParts
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
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)

-- שיעור 19: שמירה בעת עזיבה מתווספת כאן

כל השיעורים ביחידה משחק Roblox: סימולטור חיות מחמד

תרגלו בעצמכם: Roblox אונליין