시작하기
중급 튜토리얼

플레이어 데이터를 표준 데이터 저장소로 저장하기

*이 콘텐츠는 AI(베타)를 사용해 번역되었으며, 오류가 있을 수 있습니다. 이 페이지를 영어로 보려면 여기를 클릭하세요.

데이터 저장소는 서로 다른 플레이어 세션 간에 지속적인 플레이어 데이터를 저장하고 로드하는 데 사용할 수 있는 서비스입니다. 이들은 플레이어의 진행 상황이나 인벤토리와 같은 중요한 정보를 저장하고, 플레이어가 게임에 다시 접속할 때 이를 검색할 수 있도록 해줍니다. 데이터 저장소가 없으면 플레이어는 게임을 떠날 때마다 모든 진행 상황을 잃게 됩니다.

데이터 저장소는 두 가지 유형이 있습니다: 표준 및 정렬된 데이터 저장소. 이 튜토리얼에서는 순위를 매기거나 정렬할 필요가 없는 숫자, 문자열 및 테이블과 같은 데이터를 저장하는 표준 데이터 저장소를 사용합니다.

금광 채굴 데이터 저장소 튜토리얼 - 시작 .rbxl 파일을 시작점으로 사용하고 금광 채굴 데이터 저장소 튜토리얼 - 완성 .rbxl 파일을 참조하여, 이 튜토리얼은 플레이어 진행 상황을 저장하고 보다 일관된 게임을 만들기 위한 기초를 제공합니다. 다음과 같은 내용을 포함합니다:

  • 플레이어가 수집한 금의 양을 저장하고 로드하는 방법.
  • 플레이어의 금 인벤토리를 표시하기 위한 UI 업데이트 방법.
  • 플레이어의 금 인벤토리를 30초마다 자동으로 저장하는 방법.
  • 플레이어가 게임을 떠날 때 그들의 위치를 저장하는 방법.
  • 플레이어가 게임에 다시 접속할 때 그들의 위치를 복원하는 방법.

이 튜토리얼이 끝날 때쯤이면, ServerScriptService 아래에 데이터 저장소를 가진 두 개의 스크립트를 갖게 될 것입니다:

  1. 플레이어의 금을 추적, 로드 및 자동 저장하는 업데이트된 GoldManager 스크립트.
  2. 플레이어가 게임을 떠날 때 그들의 위치를 저장하고, 게임에 돌아올 때 그들의 위치를 복원하는 업데이트된 PositionManager 스크립트.

스튜디오에서 API 서비스 접근 허용

데이터 저장소는 귀하의 장치에 로컬로 저장되지 않기 때문에, 게임은 이를 사용하기 위해 로블록스 백엔드와의 서버 간 통신에 의존합니다. 기본적으로 스튜디오는 남용이나 우발적인 사용을 방지하기 위해 이 통신을 제한합니다. API 서비스에 대한 스튜디오 접근은 명시적으로 활성화할 때까지 비활성화되어, 신뢰할 수 있는 게임만이 로블록스의 백엔드 서버에서 데이터를 읽고 쓸 수 있도록 합니다.

스튜디오에서 API 서비스 접근을 활성화하여 데이터 저장소를 사용할 수 있도록 하려면:

  1. 스튜디오에서 금광 채굴 데이터 저장소 튜토리얼 - 시작 .rbxl 파일을 열고, 로컬 복사본을 만듭니다.
  2. 스튜디오로 돌아가 파일경험 설정보안으로 이동합니다.
  3. API 서비스에 대한 스튜디오 접근 허용을 켭니다.
  4. 변경 사항을 저장합니다.

표준 데이터 저장소 생성

데이터 저장소를 생성할 때는 항상 서버 측 Script에서 DataStoreService를 호출해야 합니다. 이는 다음과 같은 이유로 중요합니다:

  • 로블록스는 클라이언트의 모든 데이터 저장소 접근을 차단하여 서버만 지속적인 플레이어 데이터에 접근하고 수정할 수 있도록 합니다.
  • 서버 측 스크립트는 중앙 집중식 환경에서 실행되므로, 데이터 저장소에 읽고 쓰는 데이터가 정확하고 유효한지 확인합니다.
  • 클라이언트 측 스크립트는 플레이어의 장치에서 실행되므로, 클라이언트가 데이터 저장소에 접근할 수 있다면 어떤 플레이어가 다른 플레이어의 데이터를 훔치거나 덮어쓸 수 있습니다.

또한 데이터 저장소에 고유한 이름을 부여하여 데이터를 구성하고, 서로 다른 데이터 저장소 간의 겹침이나 충돌을 방지해야 합니다. 이 튜토리얼에서는 플레이어의 금 인벤토리를 저장하고 유지하는 데이터 저장소를 PlayerGold라고 부릅니다.

PlayerGold 데이터 저장소를 생성하려면:

  1. ServerScriptService 아래에 있는 기존 GoldManager 스크립트를 엽니다.

  2. 스크립트 상단의 다른 서비스 아래에 DataStoreService를 초기화하고 **GetDataStore**를 문자열 **"PlayerGold"**와 함께 호출합니다.


    local DataStoreService = game:GetService("DataStoreService")
    local goldStore = DataStoreService:GetDataStore("PlayerGold")

플레이어 데이터 저장 및 로드

데이터 저장소는 데이터를 식별하는 키와 데이터를 저장하는 값을 구성합니다.

설명키는 특정 데이터 조각에 접근하는 데 사용할 수 있는 고유 식별자입니다.

키를 통해 데이터를 구성할 수 있어, 이를 쉽게 관리하고 디버깅할 수 있습니다.
값은 저장하거나 로드하려는 실제 데이터입니다. 예를 들어, 플레이어의 인벤토리나 설정과 같은 데이터가 있습니다.

값을 통해 세션 간의 진행 상황을 저장하고, 플레이어가 다음에 게임에 접속할 때 데이터를 검색할 수 있습니다.
허용되는 형식문자열만 가능합니다.숫자, 문자열, 불리언 또는 테이블이 될 수 있습니다.
예시"Player_A", "TopScore", "GameSetting"100, "Level_5", true, {gold = 100, level = 5}

이 튜토리얼에서는 키가 플레이어의 userId이고 값이 그들이 수집한 금의 양입니다. 이 키와 값을 사용하여 플레이어 데이터를 저장하고 로드하려면:

  1. GoldManager 스크립트에 플레이어의 금을 PlayerGold 데이터 저장소에 저장하는 헬퍼 함수 saveGold를 추가합니다.


    local function saveGold(userId, value)
    local success, err = pcall(function()
    -- `userId`는 플레이어의 고유 ID입니다.
    -- `value`는 해당 플레이어를 위한 저장할 금의 양입니다.
    goldStore:SetAsync(userId, value)
    end)
    end
  2. OPTIONAL
    saveGold에 플레이어의 ID와 오류 메시지를 인쇄하는 경고를 추가합니다. 필수는 아니지만, 이 단계는 함수가 실패할 경우 디버깅을 쉽게 하기 위해 좋은 관행입니다.


    local function saveGold(userId, value)
    local success, err = pcall(function()
    -- `userId`는 플레이어의 고유 ID입니다.
    -- `value`는 해당 플레이어를 위한 저장할 금의 양입니다.
    goldStore:SetAsync(userId, value)
    end)
    if not success then
    warn("[SAVE ERROR] Could not save gold for", userId, ":", err)
    end
    end
  3. onPlayerAdded 이벤트를 수정하여 플레이어가 처음 게임에 가입할 때 플레이어의 금을 로드합니다. 업데이트된 onPlayerAdded 함수는:

    • PlayerGold 데이터 저장소 내의 콘텐츠를 로드하려고 시도합니다. 플레이어의 저장된 금을 반환하거나, 플레이어가 금이 없거나 게임을 한 번도 하지 않았다면 0 값을 반환합니다.
    • 코드 디버깅을 더 쉽게 도와주는 디버그 로그를 제공합니다. 이 디버그 로그에는 가입하는 플레이어의 저장된 금이 포함되거나, onPlayerAdded가 데이터 저장소에 접근할 수 없을 경우 오류 메시지가 포함됩니다.
    • UI를 업데이트하여 플레이어의 저장된 금을 그들의 화면에 표시합니다.

    local function onPlayerAdded(player)
    local userId = player.UserId
    local success, storedGold = pcall(function()
    return goldStore:GetAsync(userId)
    end)
    if success then
    local currentGold = storedGold or 0
    playerGold[userId] = currentGold
    uiEvent:FireClient(player, {
    gold = currentGold,
    doTween = false,
    showAlert = not success
    })
    else
    uiEvent:FireClient(player, { showAlert = true })
    warn("Could not load gold for", player.Name)
    end
    end
  4. onPlayerRemoving 이벤트를 수정하여 플레이어가 게임을 떠날 때 이전에 생성한 saveGold 함수를 호출하고 플레이어의 금을 저장합니다.


    local function onPlayerRemoving(player)
    local userId = player.UserId
    if playerGold[userId] then
    saveGold(userId, playerGold[userId])
    end
    end

플레이어 데이터 자동 저장

플레이어의 데이터를 자동으로 저장하는 것은 플레이어가 데이터를 잃는 것을 방지하기 때문에 좋은 관행입니다. 플레이어의 데이터를 onPlayerRemoving에서 게임을 떠날 때만 저장하면, 예상치 않게 서버에서 연결이 끊어질 경우 최신 진행 상황을 잃을 수 있습니다.

플레이어 데이터를 자동 저장하려면:

  1. 게임이 플레이어의 데이터를 자동으로 얼마나 자주 저장할지를 결정하기 위한 새로운 상수를 생성합니다. 상수는 게임이 실행되는 동안 그 값이 변하지 않는 변수입니다. 이 상수를 사용하여 스크립트 전반에 걸쳐 자동 저장 간격을 참조하고 자동 저장 빈도를 쉽게 조정할 수 있습니다.


    -- 이 숫자는 초 단위입니다
    local AUTOSAVE_INTERVAL = 30
  2. 기존 onPlayerAdded 함수에 자동 저장 코루틴을 추가하여 플레이어가 게임에 있는 동안 실행되는 백그라운드 루프를 생성합니다. 코루틴은 비동기적으로 실행되는 함수로, 특정 지점에서 일시 중지되고, 중단된 지점에서 다시 시작되며, 스크립트의 다른 부분과 병렬로 실행될 수 있습니다.

    이 스크립트에서 자동 저장 코루틴 백그라운드 루프는 AUTOSAVE_INTERVAL 상수의 초 수만큼 기다린 후, 앞서 생성한 saveGold 함수를 호출합니다.


    coroutine.wrap(function()
    while player.Parent do
    task.wait(AUTOSAVE_INTERVAL)
    if playerGold[userId] then
    print("[AUTOSAVE] Saving", playerGold[userId], "gold for", player.Name)
    saveGold(userId, playerGold[userId])
    end
    end
    end)()

플레이어 위치 저장 및 로드

플레이어의 위치를 저장하려면 Character에서 작업해야 하며, Player 객체 자체가 아닌 그 객체를 사용해야 합니다. 플레이어의 Character는 3D 세계에서 그들의 물리적 모델을 나타내며, 현재 위치 및 방향에 대한 좌표를 가지고 있지만, Player 객체는 사용자 계정만 나타냅니다.

데이터 저장소는 숫자, 문자열 및 테이블과 같은 기본 데이터 유형만 저장할 수 있으므로, Vector3 또는 CFrame 값과 같은 복잡한 객체를 직접 저장할 수 없습니다. 플레이어의 위치를 저장하려면 이를 세 개의 개별 숫자(X, Y 및 Z 좌표)로 분할하고 각각 저장해야 합니다. 나중에 위치를 로드할 때 저장된 좌표를 사용하여 Vector3를 재구성할 수 있습니다.

캐릭터의 위치를 저장하고 로드하려면:

  1. ServerScriptService 아래에 있는 기존 PositionManager 스크립트를 엽니다.

  2. 스크립트 상단에 DataStoreService를 초기화하고 GetDataStore를 문자열 **"PlayerPosition"**과 함께 호출합니다.


    local DataStoreService = game:GetService("DataStoreService")
    local positionStore = DataStoreService:GetDataStore("PlayerPosition")
  3. 플레이어의 userIdVector3 위치를 인수로 받는 헬퍼 함수 savePosition을 추가합니다. Vector3 값을 직접 저장할 수 없기 때문에, 위치를 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)
    end
  4. OPTIONAL
    savePosition에 오류 메시지와 위치를 저장하지 못한 플레이어의 이름을 인쇄하는 경고를 추가합니다. 필수는 아니지만, 이 단계는 함수가 실패할 경우 디버깅을 용이하게 하기 위해 좋은 관행입니다.


    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("Could not save position for", player.Name, ":", err)
    end
    end
  5. 플레이어를 위해 PlayerPosition 데이터 저장소에서 저장된 좌표를 로드하는 헬퍼 함수 loadPosition을 추가합니다. **PivotTo**를 사용하여 캐릭터의 위치를 세계에 복원합니다. 플레이어의 저장된 위치는 테이블로 반환되므로, Vector3를 재구성한 후 이를 CFrame으로 변환하여 캐릭터를 이동시켜야 합니다.


    local function loadPosition(userId, 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("Restored position for", player.Name)
    character:PivotTo(CFrame.new(pos))
    elseif not success then
    warn("Could not load position for", player.Name)
    end
    end
  6. onPlayerAdded 이벤트를 수정하여 게임에 가입하는 각 플레이어에 대한 위치 논리를 설정합니다. 업데이트된 onPlayerAdded 함수 내에서:

    • CharacterAdded는 플레이어의 캐릭터가 생성될 때 loadPosition을 호출하여 마지막 알려진 위치를 복원합니다.
    • CharacterRemoving은 플레이어의 캐릭터가 게임에서 제거될 때 현재 위치를 저장하기 위해 savePosition을 호출합니다.
    • **GetPivot**는 제거될 때 캐릭터의 정확한 위치를 세계에서 가져옵니다.

    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
    Player.PlayerAdded:Connect(onPlayerAdded)

최종 코드

다음 코드 스니펫은 완전한 GoldManagerPositionManager 스크립트를 보여줍니다. 이 코드를 금광 채굴 데이터 저장소 튜토리얼 - 시작 .rbxl 파일의 스튜디오에 직접 붙여넣고 실행할 수 있습니다.

GoldManager


-- Get services
-- Get services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
local DataStoreService = game:GetService("DataStoreService")
-- Remote event to update the UI
local uiEvent = ReplicatedStorage:WaitForChild("UIEvent")
-- Data store for saving the player's gold chunks
local goldStore = DataStoreService:GetDataStore("PlayerGold")
-- Table to store the player's gold chunks for a single session
local playerGold = {}
-- Constants
local GOLD_VALUE = 10 -- How much each gold chunk is worth
local GOLD_REGEN_DELAY = 2 -- The delay before a chunk respawns (in seconds)
local AUTOSAVE_INTERVAL = 30 -- How often the player's chunks are saved (in seconds)
-- Function to handle the player collecting a gold chunk
local function processGoldTouch(chunk, player)
-- Hide the chunk when the player collects it
chunk.Parent = nil
-- Add the chunk to the player's session gold score
local userId = player.UserId
playerGold[userId] = (playerGold[userId] or 0) + GOLD_VALUE
print(player.Name, "now has", playerGold[userId], "gold")
-- Update the UI to reflect the new gold score for this session
uiEvent:FireClient(player, {
gold = playerGold[userId],
doTween = true,
showAlert = false
})
-- Wait a bit and then respawn the gold chunk that was just collected
task.delay(GOLD_REGEN_DELAY, function()
chunk.Parent = workspace.GoldChunks
end)
end
-- Loop through all parts tagged with "Gold" and connect them to touch detection
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
-- Function to save the player's gold to the data store
local function saveGold(player, value)
local userId = player.UserId
local success, err = pcall(function()
goldStore:SetAsync(userId, value)
end)
if not success then
warn("Could not save gold for", player.Name, ":", err)
end
end
-- Function to handle the player joining the game
local function onPlayerAdded(player)
local userId = player.UserId
print(player.Name, "has joined the game")
-- Try to load the player's gold from the data store
local success, storedGold = pcall(function()
return goldStore:GetAsync(userId)
end)
if success then
-- Set the current session gold score using the data store value for the player
local currentGold = storedGold or 0
playerGold[userId] = currentGold
-- Update the UI with the data store value for the player
uiEvent:FireClient(player, {
gold = currentGold,
doTween = false,
showAlert = false
})
print("Loaded", currentGold, "gold for", player.Name)
else
-- Notify the player their gold couldn't be loaded
uiEvent:FireClient(player, { showAlert = true })
warn("Could not load gold for", player.Name)
end
-- Start the autosave coroutine to save the player's gold every 30 seconds
coroutine.wrap(function()
while player.Parent do
task.wait(AUTOSAVE_INTERVAL)
if playerGold[userId] then
saveGold(player, playerGold[userId])
print("Autosaving", playerGold[userId], "gold for", player.Name)
end
end
end)()
end
-- Function to handle the player leaving the game
local function onPlayerRemoving(player)
local userId = player.UserId
print(player.Name, "has left the game")
-- Check if the player's gold was tracked
if playerGold[userId] then
-- Save the player's gold to the data store before they leave
saveGold(player, playerGold[userId])
end
end
-- Connect the joining and leaving player events
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)

PositionManager


-- Get services
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
-- Data store for saving the player's position
local positionStore = DataStoreService:GetDataStore("PlayerPosition")
-- Function to save the player's position as a table of {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("Could not save position for", player.Name, ":", err)
end
end
-- Function to load the player's last saved position and move their character
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("Restored position for", player.Name)
character:PivotTo(CFrame.new(pos))
elseif not success then
warn("Could not load position for", player.Name)
end
end
-- Function to handle the player joining the game
local function onPlayerAdded(player)
local userId = player.UserId
-- Try to load the player's position when their character spawns
player.CharacterAdded:Connect(function(character)
loadPosition(player, character)
end)
-- Save the player's position when their character is removed
player.CharacterRemoving:Connect(function(character)
local pos = character:GetPivot().Position
savePosition(player, pos)
end)
end
Players.PlayerAdded:Connect(onPlayerAdded)
©2026 Roblox Corporation. Roblox 및 Roblox 로고, 'Powering Imagination'은 미국 및 기타 국가 내 당사의 등록 및 미등록 상표입니다.