資料儲存 是您可以用來保存和加載 持久性玩家數據 的服務,這在不同的玩家會話之間是持續的。它們儲存重要的信息,例如玩家的進度或庫存,並允許您在玩家下次加入您的遊戲時檢索這些信息。如果沒有資料儲存,玩家每次離開遊戲時都會失去所有進度。
資料儲存有兩種類型:標準資料儲存和有序資料儲存。本教程使用 標準資料儲存,它儲存像數字、字符串和不需要排名或排序的表格這樣的數據。
本教程使用 金礦資料儲存教程 - 開始 .rbxl 檔案作為起始位置,並參考 金礦資料儲存教程 - 完成 .rbxl 檔案,為您提供保存玩家進度和創建更一致的遊戲所需的基礎,包括以下指導:
- 保存和加載玩家收集的金幣數量。
- 更新用戶界面以顯示玩家的金幣庫存。
- 每 30 秒自動保存玩家的金幣庫存。
- 在玩家離開遊戲時保存其位置。
- 在玩家再次加入遊戲時恢復其位置。
在本教程結束時,您應該在 ServerScriptService 下擁有兩個包含資料儲存的腳本:
- 更新的 GoldManager 腳本,跟蹤、加載並自動保存玩家的金幣。
- 更新的 PositionManager 腳本,在玩家離開遊戲時保存其位置,並在他們回到遊戲時恢復其位置。

啟用 Studio 訪問 API 服務
資料儲存並不會保存在您的設備本地,因此您的遊戲依賴於與 Roblox 後端的伺服器到伺服器通信來使用它們。默認情況下,Studio 會限制此通信,以防止濫用或意外使用。Studio 中的 API 服務的訪問默認禁用,直到您明確啟用它,以確保只有受信任的遊戲可以讀取和寫入 Roblox 的後端伺服器。
要啟用 Studio 訪問 API 服務,以便您可以使用資料儲存:
- 在 Studio 中打開 金礦資料儲存教程 - 開始 .rbxl 檔案並製作本地副本。
- 返回 Studio,轉到 文件 ⟩ 體驗設置 ⟩ 安全。
- 開啟 啟用 Studio 訪問 API 服務。
- 保存您的更改。
創建標準資料儲存
在創建資料儲存時,您應始終從伺服器端的 Script 調用 DataStoreService。這是重要的,因為:
- Roblox 阻止客戶端訪問所有資料儲存,以確保只有伺服器有權訪問和修改持久的玩家數據。
- 伺服器端腳本在集中式環境中運行,確保讀取和寫入資料儲存的數據是準確和有效的。
- 由於客戶端腳本在玩家的設備上運行,因此任何玩家都可能利用遊戲並盜取或覆蓋其他玩家的數據,如果客戶端可以訪問您的資料儲存。
您還應為資料儲存指定一個唯一名稱,以保持數據組織,並防止在不同資料儲存之間重疊或衝突。在本教程中,保存和儲存玩家金幣庫存的資料儲存名稱為 PlayerGold。
要創建 PlayerGold 資料儲存:
打開 ServerScriptService 下的現有 GoldManager 腳本。

在腳本頂部的其他服務下,初始化 DataStoreService,並用字符串 "PlayerGold" 調用 GetDataStore。
local DataStoreService = game:GetService("DataStoreService")local goldStore = DataStoreService:GetDataStore("PlayerGold")
保存和加載玩家數據
資料儲存由鍵組成,這些鍵定義數據,以及值,這些值用於儲存數據。
| 鍵 | 值 | |
|---|---|---|
| 描述 | 鍵是一個唯一的標識符,您可以用來訪問特定數據。 鍵讓您組織數據,便於管理和調試。 | 值是您想要保存或加載的實際數據,例如玩家的庫存或其設置。 值讓您在會話之間儲存進度,以及在玩家下次加入遊戲時檢索玩家數據。 |
| 允許的格式 | 只能是字符串。 | 可以是數字、字符串、布林值或表格。 |
| 示例 | "Player_A", "TopScore", "GameSetting" | 100, "Level_5", true, {gold = 100, level = 5} |
在本教程中,鍵是玩家的 userId,而值是他們收集的金幣數量。要使用這個鍵和值來保存和加載玩家數據:
在 GoldManager 腳本中,添加一個助手函數 saveGold 來將玩家的金幣保存到 PlayerGold 資料儲存。
local function saveGold(userId, value)local success, err = pcall(function()-- `userId` 是玩家的唯一 ID-- `value` 是要為該玩家保存的金幣數量goldStore:SetAsync(userId, value)end)end- 可選向 saveGold 添加一個警告,打印玩家的 ID 和錯誤消息。雖然不是必需的,但這一步在函數失敗時能夠更輕鬆地進行調試是很好的做法。local function saveGold(userId, value)local success, err = pcall(function()-- `userId` 是玩家的唯一 ID-- `value` 是要為該玩家保存的金幣數量goldStore:SetAsync(userId, value)end)if not success thenwarn("[SAVE ERROR] 無法保存玩家", userId, "的金幣:", err)endend
修改 onPlayerAdded 事件,以在玩家首次加入遊戲時加載他們的金幣。更新的 onPlayerAdded 函數:
- 嘗試加載 PlayerGold 資料儲存中的內容。如果玩家沒有金幣或者之前沒有玩過這個遊戲,則返回 0 作為值。
- 提供調試日誌,以幫助您更輕鬆地調試代碼。此調試日誌包括加入玩家的保存金幣,或者如果 onPlayerAdded 無法訪問資料儲存的錯誤消息。
- 更新用戶界面以顯示玩家的保存金幣在他們的屏幕上。
local function onPlayerAdded(player)local userId = player.UserIdlocal success, storedGold = pcall(function()return goldStore:GetAsync(userId)end)if success thenlocal currentGold = storedGold or 0playerGold[userId] = currentGolduiEvent:FireClient(player, {gold = currentGold,doTween = false,showAlert = not success})elseuiEvent:FireClient(player, { showAlert = true })warn("無法加載", player.Name, "的金幣")endend修改 onPlayerRemoving 事件,以調用之前創建的 saveGold 函數,並在玩家離開遊戲時保存他們的金幣。
local function onPlayerRemoving(player)local userId = player.UserIdif playerGold[userId] thensaveGold(userId, playerGold[userId])endend
自動保存玩家數據
自動保存玩家數據是良好的做法,因為它可以保護玩家不會丟失數據。如果您僅在玩家通過 onPlayerRemoving 離開遊戲時保存他們的數據,則如果他們意外斷開與伺服器的連接,可能會錯過他們的最新進度。
要自動保存玩家的數據:
創建一個新的常量來確定您希望您的遊戲多久自動保存一次玩家的數據。常量是一個在遊戲運行時不會變化的變量。您可以使用這個常量在代碼中隨處引用自動保存間隔,並輕鬆調整自動保存的頻率。
-- 此數字以秒為單位local AUTOSAVE_INTERVAL = 30在現有的 onPlayerAdded 函數中添加一個自動保存的協程,創建一個在玩家在遊戲中時運行的後台循環。協程是異步運行的函數;它可以在某些點暫停,在中斷處恢復,並且與代碼的其他部分並行運行而不阻塞它們。
在此腳本中,自動保存協程後台循環按您的 AUTOSAVE_INTERVAL 常量中的秒數等待,然後調用您之前創建的 saveGold 函數。
coroutine.wrap(function()while player.Parent dotask.wait(AUTOSAVE_INTERVAL)if playerGold[userId] thenprint("[AUTOSAVE] 自動保存", playerGold[userId], "金幣給", player.Name)saveGold(userId, playerGold[userId])endendend)()
保存和加載玩家位置
要保存玩家的位置,請處理他們的 Character,而不是 Player 對象本身。玩家的 Character 代表他們在 3D 世界中的物理模型,並包含他們當前位置和方向的坐標,而 Player 對象僅表示用戶帳戶。
因為資料儲存只能保存基本數據類型,如數字、字符串和表格,所以您不能直接存儲像 Vector3 或 CFrame 的複雜對象。要保存玩家的位置,您需要將其拆分為三個獨立的數字(X、Y 和 Z 坐標),並分別保存它們。稍後加載位置時,您可以使用保存的坐標重建 Vector3。
要保存和加載角色的位置:
打開 ServerScriptService 下的現有 PositionManager 腳本。
在腳本的頂部,初始化 DataStoreService,並用字符串 "PlayerPosition" 調用 GetDataStore。
local DataStoreService = game:GetService("DataStoreService")local positionStore = DataStoreService:GetDataStore("PlayerPosition")添加一個助手函數 savePosition,該函數接受玩家的 userId 和 Vector3 位置。由於您無法直接保存 Vector3 值,請將位置轉換為 X、Y、Z 數字的表。
local function savePosition(player, position)local userId = player.UserIdlocal success, err = pcall(function()positionStore:SetAsync(userId, {position.X, position.Y, position.Z})end)end- 可選向 savePosition 添加一個警告,打印錯誤消息和您無法保存位置的玩家的名稱。雖然不是必需的,但這步驟在函數失敗時能夠更輕鬆地進行調試是良好的做法。local function savePosition(player, position)local userId = player.UserIdlocal success, err = pcall(function()positionStore:SetAsync(userId, {position.X, position.Y, position.Z})end)if not success thenwarn("無法保存", player.Name, "的位置:", err)endend
添加另一個助手函數 loadPosition,在 PlayerPosition 資料儲存中加載玩家的保存座標。使用 PivotTo 將角色的位置恢復到世界中。由於玩家的保存位置返回為一個表,您必須重建一個 Vector3,然後將其轉換為 CFrame 以移動角色。
local function loadPosition(userId, character)local userId = player.UserIdlocal success, savedCoords = pcall(function()return positionStore:GetAsync(userId)end)if success and savedCoords thenlocal pos = Vector3.new(savedCoords[1], savedCoords[2], savedCoords[3])print("恢復", player.Name, "的位置")character:PivotTo(CFrame.new(pos))elseif not success thenwarn("無法加載", player.Name, "的位置")endend修改 onPlayerAdded 事件,以便為每個加入您遊戲的玩家設置位置邏輯。在更新的 onPlayerAdded 函數內:
- CharacterAdded 調用 loadPosition 在玩家的角色生成時恢復他們最後已知的位置。
- CharacterRemoving 調用 savePosition 在玩家的角色被移除時保存其當前位置。
- GetPivot 獲取角色被移除時的確切位置。
local function onPlayerAdded(player)local userId = player.UserIdplayer.CharacterAdded:Connect(function(character)loadPosition(player, character)end)player.CharacterRemoving:Connect(function(character)local pos = character:GetPivot().PositionsavePosition(player, pos)end)endPlayer.PlayerAdded:Connect(onPlayerAdded)
最終代碼
以下代碼片段是完整的 GoldManager 和 PositionManager 腳本。您可以直接將它們粘貼並運行於 金礦保存數據教程 - 開始 .rbxl 文件中。
GoldManager
-- 獲取服務
-- 獲取服務
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
local DataStoreService = game:GetService("DataStoreService")
-- 更新 UI 的遠程事件
local uiEvent = ReplicatedStorage:WaitForChild("UIEvent")
-- 用於保存玩家的金幣塊的資料儲存
local goldStore = DataStoreService:GetDataStore("PlayerGold")
-- 用於儲存玩家在單次會話中的金幣塊的表
local playerGold = {}
-- 常量
local GOLD_VALUE = 10 -- 每個金幣塊的值
local GOLD_REGEN_DELAY = 2 -- 一個塊再生的延遲(以秒為單位)
local AUTOSAVE_INTERVAL = 30 -- 玩家金幣塊保存的頻率(以秒為單位)
-- 處理玩家收集金幣塊的函數
local function processGoldTouch(chunk, player)
-- 玩家收集金幣時隱藏該塊
chunk.Parent = nil
-- 將該塊的金幣加到玩家的會話金幣分數中
local userId = player.UserId
playerGold[userId] = (playerGold[userId] or 0) + GOLD_VALUE
print(player.Name, "現在擁有", playerGold[userId], "金幣")
-- 更新 UI 以反映該會話的金幣新得分
uiEvent:FireClient(player, {
gold = playerGold[userId],
doTween = true,
showAlert = false
})
-- 等待一段時間,然後重生剛剛被收集的金幣塊
task.delay(GOLD_REGEN_DELAY, function()
chunk.Parent = workspace.GoldChunks
end)
end
-- 遍歷所有標記為 "Gold" 的部分並連接觸發檢測
for _, chunk in ipairs(CollectionService:GetTagged("Gold")) do
chunk.Touched:Connect(function(otherPart)
local player = Players:GetPlayerFromCharacter(otherPart.Parent)
if player then
processGoldTouch(chunk, player)
end
end)
end
-- 將玩家的金幣保存到資料儲存的函數
local function saveGold(player, value)
local userId = player.UserId
local success, err = pcall(function()
goldStore:SetAsync(userId, value)
end)
if not success then
warn("無法保存", player.Name, "的金幣:", err)
end
end
-- 處理玩家加入遊戲的函數
local function onPlayerAdded(player)
local userId = player.UserId
print(player.Name, "已加入遊戲")
-- 嘗試從資料儲存中加載玩家的金幣
local success, storedGold = pcall(function()
return goldStore:GetAsync(userId)
end)
if success then
-- 使用資料儲存值設置當前會話的金幣分數
local currentGold = storedGold or 0
playerGold[userId] = currentGold
-- 使用資料儲存值更新玩家的 UI
uiEvent:FireClient(player, {
gold = currentGold,
doTween = false,
showAlert = false
})
print("為", player.Name, "加載了", currentGold, "金幣")
else
-- 通知玩家他們的金幣無法加載
uiEvent:FireClient(player, { showAlert = true })
warn("無法加載", player.Name, "的金幣")
end
-- 開始自動保存協程,每 30 秒保存一次玩家的金幣
coroutine.wrap(function()
while player.Parent do
task.wait(AUTOSAVE_INTERVAL)
if playerGold[userId] then
saveGold(player, playerGold[userId])
print("自動保存", playerGold[userId], "金幣給", player.Name)
end
end
end)()
end
-- 處理玩家離開遊戲的函數
local function onPlayerRemoving(player)
local userId = player.UserId
print(player.Name, "已離開遊戲")
-- 檢查玩家的金幣是否被追踪
if playerGold[userId] then
-- 在他們離開前將玩家的金幣保存到資料儲存
saveGold(player, playerGold[userId])
end
end
-- 連接加入和離開玩家的事件
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
PositionManager
-- 獲取服務
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
-- 用於保存玩家位置的資料儲存
local positionStore = DataStoreService:GetDataStore("PlayerPosition")
-- 將玩家位置保存為 {X, Y, Z} 表的函數
local function savePosition(player, position)
local userId = player.UserId
local success, err = pcall(function()
positionStore:SetAsync(userId, {position.X, position.Y, position.Z})
end)
if not success then
warn("無法保存", player.Name, "的位置:", err)
end
end
-- 函數以加載玩家的最後保存位置並移動其角色
local function loadPosition(player, character)
local userId = player.UserId
local success, savedCoords = pcall(function()
return positionStore:GetAsync(userId)
end)
if success and savedCoords then
local pos = Vector3.new(savedCoords[1], savedCoords[2], savedCoords[3])
print("為", player.Name, "恢復位置")
character:PivotTo(CFrame.new(pos))
elseif not success then
warn("無法加載", player.Name, "的位置")
end
end
-- 處理玩家加入遊戲的函數
local function onPlayerAdded(player)
local userId = player.UserId
-- 嘗試在玩家角色生成時加載其位置
player.CharacterAdded:Connect(function(character)
loadPosition(player, character)
end)
-- 在他們的角色被移除時保存玩家的位置信息
player.CharacterRemoving:Connect(function(character)
local pos = character:GetPivot().Position
savePosition(player, pos)
end)
end
Players.PlayerAdded:Connect(onPlayerAdded)