데이터 저장소는 서로 다른 플레이어 세션 간에 지속적인 플레이어 데이터를 저장하고 불러오는 데 사용할 수 있는 서비스입니다. 이들은 플레이어의 진행 상황이나 인벤토리와 같은 중요한 정보를 저장하고, 플레이어가 게임에 다시 참여할 때 이를 불러올 수 있도록 합니다. 데이터 저장소가 없으면 플레이어는 게임을 떠날 때마다 모든 진행 상황을 잃게 됩니다.
데이터 저장소에는 표준 데이터 저장소와 정렬된 데이터 저장소 두 가지가 있습니다. 이 튜토리얼은 정렬된 데이터 저장소를 사용하며, 데이터는 수치적으로 순위를 매기고 정렬할 수 있으며 저장된 수치 값을 기준으로 오름차순 또는 내림차순으로 검색할 수 있습니다.
금광산 리더보드 만들기 튜토리얼 - 시작 .rbxl 파일을 출발점으로 사용하고 금광산 리더보드 만들기 튜토리얼 - 완성 .rbxl 파일을 참조하여 이 튜토리얼에서는 다음을 포함하여 사용자 정의 플레이어 리더보드를 만드는 데 필요한 기초를 제공합니다:
- 정렬된 데이터 저장소에 금 점수를 저장하기.
- 모든 서버에서 상위 점수를 검색하고 정렬하기.
- 사용자 정의 UI의 리더보드에 상위 점수를 표시하기.
- 몇 초마다 사용자 정의 리더보드를 자동으로 새로 고치기.
이 튜토리얼이 끝나면 다음을 갖추게 됩니다:
- ServerScriptService에서:
- 플레이어의 점수를 리더보드에 대한 정렬된 데이터 저장소에 저장하는 업데이트된 GoldManager 스크립트.
- 리더보드의 상위 점수 목록을 검색하고 반환하는 새로운 LeaderboardRemoteHandler 모듈 스크립트.
- 클라이언트의 리더보드 데이터 요청에 응답하는 새로운 LeaderboardManager 스크립트.
- StarterGui에서, 플레이어를 위한 리더보드 UI를 렌더링하는 새로운 LeaderboardGuiScript 로컬 스크립트.

스튜디오에서 API 서비스 접근 허용
데이터 저장소는 여러분의 장치에 로컬로 저장되지 않으므로, 여러분의 게임은 Roblox의 백엔드와의 서버 간 통신에 의존합니다. 기본적으로 스튜디오는 남용이나 우발적인 사용을 방지하기 위해 이러한 통신을 제한합니다. API 서비스의 스튜디오 접근은 여러분이 명시적으로 활성화할 때까지 비활성화되어 있어야 하며, 그렇지 않으면 신뢰할 수 있는 게임만이 Roblox의 백엔드 서버에서 읽고 쓸 수 있습니다.
API 서비스에 대한 스튜디오 접근을 허용하여 데이터 저장소를 사용할 수 있도록 하려면 다음과 같은 단계를 따르세요:
- 스튜디오에서 금광산 리더보드 만들기 튜토리얼 - 시작 파일을 엽니다. 이 시작 파일에는 이미 플레이어 데이터 저장 튜토리얼의 코드와 플레이어 리더보드를 위한 UI 스켈레톤이 포함되어 있습니다.
- 게임을 게시합니다.
- 스튜디오에서 파일 ⟩ 경험 설정 ⟩ 보안으로 이동합니다.
- API 서비스에 대한 스튜디오 접근 허용을 켭니다.
- 변경 사항을 저장합니다.
정렬된 데이터 저장소 생성
데이터 저장소를 생성할 때는 항상 서버 측 Script에서 DataStoreService를 호출해야 합니다. 이는 다음과 같은 중요한 이유가 있습니다:
- Roblox는 클라이언트의 모든 데이터 저장소 접근을 차단하여 서버만이 지속적인 플레이어 데이터에 접근하고 수정할 수 있도록 합니다.
- 서버 측 스크립트는 중앙 집중식 환경에서 실행되어 데이터가 정확하고 유효하도록 합니다.
- 클라이언트 측 스크립트가 플레이어의 장치에서 실행되므로, 클라이언트가 데이터 저장소에 접근할 수 있다면 플레이어는 게임을 악용하여 다른 플레이어의 데이터를 훔치거나 덮어쓸 수 있습니다.
여러분은 또한 데이터 저장소의 고유한 이름을 지정하여 데이터를 정리하고 서로 다른 데이터 저장소에서 충돌하거나 겹치는 것을 방지해야 합니다. 이 튜토리얼에서 리더보드의 데이터 저장소는 GlobalLeaderboard라고 합니다.
GlobalLeaderboard 정렬된 데이터 저장소를 만들려면:
ServerScriptService 하에 있는 기존 GoldManager 스크립트를 엽니다.

기존 PlayerGold 데이터 저장소 아래에서 GetOrderedDataStore를 문자열 **"GlobalLeaderboard"**와 함께 호출합니다. 이는 플레이어가 금을 얻을 때 점수를 저장하기 위한 정렬된 데이터 저장소를 생성합니다.
local leaderboardStore = DataStoreService:GetOrderedDataStore("GlobalLeaderboard")
데이터 저장소를 만든 후에는 여러 스크립트에서 이 저장소를 참조할 수 있습니다. 예를 들어, GoldManager 스크립트와 이 튜토리얼의 이후에 만들 LeaderboardManager 모듈 스크립트에서 GetOrderedDataStore를 호출할 수 있습니다. 같은 데이터 저장소를 두 곳에서 참조한다고 해서 데이터를 중복하는 것은 아닙니다; 두 스크립트가 같은 중앙 집중식 스토리지에 접근하고 있는 것이며, 마치 두 팀원이 정보를 공유하기 위해 하나의 폴더를 사용하는 것과 같습니다.
플레이어 데이터를 리더보드에 보내기
데이터 저장소는 데이터를 식별하는 키와 데이터를 저장하는 값으로 구성됩니다. 표준 데이터 저장소와 달리, 정렬된 데이터 저장소는 값으로 데이터를 정렬할 수 있으므로 리더보드와 같은 것을 만드는 데 이상적입니다.
정렬된 데이터 저장소의 각 항목은 수치적으로 순위를 매기고 정렬할 수 있는 고유한 키 아래에 저장되며, 저장된 수치 값을 기준으로 오름차순 또는 내림차순으로 검색할 수 있습니다. 이는 정렬된 데이터 저장소를 사용하여 예를 들어 플레이어가 금을 더 많이 모을 때 실시간으로 업데이트되는 상위 3 목록을 보여줄 수 있음을 의미합니다.
| 키 | 값 | |
|---|---|---|
| 설명 | 키는 특정 데이터에 접근하는 데 사용할 수 있는 고유한 문자열 식별자로, 플레이어의 사용자 ID와 같은 것입니다. 키는 데이터를 조직해서 쉽게 관리하고 디버깅할 수 있도록 합니다. | 값은 저장하거나 불러오고자 하는 실제 수치 데이터입니다. 이는 플레이어의 점수와 같은 것입니다. 값은 세션 간의 진행 상황을 저장하고, 플레이어가 게임에 다시 참여할 때 데이터를 검색할 수 있도록 합니다. |
| 허용 형식 | 문자열만 사용할 수 있습니다. 사용자 ID와 같은 숫자를 사용하는 경우 tostring()을 사용하여 문자열로 변환해야 합니다. | 숫자만 사용할 수 있습니다. |
| 예제 | "Player_A", "TopScore", "123456" (문자열로서의 userId) | 100, 3.14, 0, -25 |
이제 GlobalLeaderboard 정렬된 데이터 저장소를 만들었으므로, GoldManager 스크립트의 기존 saveGold 함수를 업데이트하여 SetAsync로 플레이어의 금 점수를 사용자 정의 리더보드에 보내세요:
local function saveGold(player, value)
local userId = player.UserId
local success, err = pcall(function()
goldStore:SetAsync(userId, value)
leaderboardStore:SetAsync(tostring(userId), value)
end)
if not success then
warn("Could not save gold for", player.Name, ":", err)
end
end
정렬된 데이터 저장소 GlobalLeaderboard를 공유 폴더로 생각할 수 있습니다: SetAsync는 폴더에 내용을 추가하고, GetSortedAsync (나중에 LeaderboardManager 모듈 스크립트에서 생성할 것입니다)는 폴더의 내용을 읽고 점수에 따라 정렬합니다.
리더보드 점수 가져오기
리더보드 점수를 가져오기 위해서는 ModuleScript를 사용해야 합니다. 모듈 스크립트는 여러 스크립트에서 코드를 조직하고 재사용할 수 있도록 합니다. 동일한 논리를 여러 곳에 복사하는 대신, 모듈 스크립트에서 한 번 코드를 작성한 후 필요할 때 어디에서나 호출할 수 있습니다.
리더보드 논리를 모듈에 저장하면 UI 핸들러와 서버 스크립트 모두가 같은 함수를 사용하여 상위 플레이어 점수를 가져올 수 있습니다. 예를 들어 사용자 이름을 가져오는 방식이나 점수를 정렬하는 방식을 업데이트해야 하는 경우 코드의 한 곳만 수정하면 됩니다. 모듈을 업데이트하면 이를 사용하는 모든 스크립트도 자동으로 업데이트된 동작을 받게 됩니다.
사용자 정의 리더보드는 다음 정렬된 데이터 저장소 메서드를 사용하여 서로 다른 서버 간에 데이터를 공유합니다:
- 플레이어 점수를 GlobalLeaderboard 정렬된 데이터 저장소로 보내기 위해 ( GoldManager 스크립트에서) 사용되는 SetAsync.
- 모든 서버에서 동일한 데이터 저장소의 상위 점수를 가져오기 위해 (모듈 스크립트에서) 사용되는 GetSortedAsync. GetSortedAsync 메서드는 정렬된 데이터 저장소에만 독점적이며 특정 정렬 순서, 페이지 크기, 최소 및 최대 값을 기준으로 여러 정렬된 키를 검색할 수 있습니다.
리더보드 점수를 가져오려면:
ServerScriptService에서 LeaderboardManager라는 새로운 ModuleScript를 만드세요.
모듈 스크립트의 상단에서 GetOrderedDataStore를 문자열 **"GlobalLeaderboard"**와 함께 호출하세요. GoldManager 스크립트에서 만든 동일한 정렬된 데이터 저장소를 참조하면 나중에 리더보드 UI가 상위 플레이어 점수를 검색하고 정렬할 수 있습니다.
local DataStoreService = game:GetService("DataStoreService")local Players = game:GetService("Players")local leaderboardStore = DataStoreService:GetOrderedDataStore("GlobalLeaderboard")다른 스크립트에 노출하려는 모든 함수와 데이터를 보관할 모듈 테이블을 준비합니다.
local leaderboardManager = {}상위 플레이어 점수를 가져오기 위한 GetTopScores라는 함수를 만드세요. 이 함수는:
- GetSortedAsync를 호출하여 내림차순으로 limit 개수의 상위 점수를 가져옵니다. 호출이 실패하면 나머지 코드의 충돌을 방지하기 위해 빈 목록이 반환됩니다.
- 리더보드를 통해 GetCurrentPage로 엔트리 테이블을 가져오고 GetNameFromUserIdAsync를 사용하여 플레이어 사용자 이름을 가져옵니다.
- 각 결과를 최종 목록에 추가하고 플레이어 사용자 이름과 점수가 포함된 리더보드 테이블을 만듭니다.
- 최종 플레이어 목록을 반환하고 이 데이터를 leaderboardManager.GetTopScores 함수를 호출하는 모든 스크립트에 보내 리더보드 데이터를 전달합니다.
function leaderboardManager.GetTopScores(limit)local success, pages = pcall(function()return leaderboardStore:GetSortedAsync(false, limit)end)if not success or not pages thenreturn {}endlocal results = {}for _, entry in ipairs(pages:GetCurrentPage()) dolocal userId = tonumber(entry.key)local score = entry.valuelocal username = "Unknown"local ok, name = pcall(function()return Players:GetNameFromUserIdAsync(userId)end)if ok thenusername = nameendtable.insert(results, {Username = username,Score = score})endreturn resultsend이전에 생성한 모듈 테이블을 반환하여 모듈을 사용할 수 있도록 만듭니다. 모듈 스크립트를 생성할 때마다, 다른 스크립트가 액세스할 수 있도록 모듈 테이블을 반환해야 합니다.
return leaderboardManager
서버에서 리더보드 데이터 요청
클라이언트 UI가 리더보드 점수를 표시하려면, 클라이언트가 데이터를 요청할 때 서버가 응답해야 합니다. RemoteFunction은 클라이언트가 서버에서 정보를 요청하고 응답을 기다릴 수 있게 합니다.
서버에서 리더보드 데이터를 요청하려면:
- ReplicatedStorage 아래에 LeaderboardRemote라는 RemoteFunction을 추가합니다. 이 원격 함수는 클라이언트의 UI 스크립트와 서버의 리더보드 논리 간의 다리 역할을 합니다.
- ServerScriptService 아래에 LeaderboardRemoteHandler라는 스크립트를 만듭니다. 이 스크립트에서는:
ReplicatedStorage (클라이언트와 서버 간의 공유 공간), ServerScriptService (리더보드 모듈을 보관하는 곳), 그리고 클라이언트가 리더보드 데이터를 요청하기 위해 호출할 leaderboardRemote를 호출합니다.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local ServerScriptService = game:GetService("ServerScriptService")local leaderboardRemote = ReplicatedStorage:WaitForChild("LeaderboardRemote")이전에 생성한 LeaderboardManager 모듈을 불러옵니다. 이 모듈에는 정렬된 데이터 저장소에서 상위 점수를 가져오는 기능이 포함되어 있습니다.
local leaderboardManager = require(ServerScriptService:WaitForChild("LeaderboardManager"))클라이언트가 InvokeServer를 호출할 때 동작을 설정합니다. 서버는 상위 3개 점수를 가져오고 결과를 클라이언트에 다시 보냅니다.
leaderboardRemote.OnServerInvoke = function(player)return leaderboardManager.GetTopScores(3)end
사용자 정의 리더보드 UI에 데이터 표시
서버 측 스크립트를 설정한 후, 매 3초마다 상위 3명의 플레이어 데이터를 가져오고 게임 내의 글로벌 UI 리더보드에 결과를 표시하는 로컬 스크립트를 만듭니다.
자주 사용하는 리더보드에 데이터를 표시하려면:
StarterGui ⟩ LeaderboardGui 아래에 LeaderboardGuiScript라는 로컬 스크립트를 만듭니다.
스크립트의 상단에서 원격 함수에 연결합니다. ReplicatedStorage를 호출하여 서버와 클라이언트 간의 공유 객체에 접근한 후, leaderboardRemote를 호출하여 클라이언트가 서버에서 리더보드 데이터를 요청할 수 있도록 합니다.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local leaderboardRemote = ReplicatedStorage:WaitForChild("LeaderboardRemote")LeaderboardGui 아래의 기존 리더보드 UI 요소들을 참조합니다.
local gui = script.Parentlocal leaderboardFrame = gui:WaitForChild("LeaderboardFrame")local entriesFrame = leaderboardFrame:WaitForChild("EntriesFrame")local template = entriesFrame:WaitForChild("LeaderboardEntry")이전 리더보드 항목을 지우기 위한 clearEntries라는 함수를 만듭니다. 이 함수는 리더보드 행이 포함된 entriesFrame의 모든 자식을 반복하여 확인하고, 새 데이터를 표시하기 전에 리더보드가 완전히 새로 고쳐지도록 합니다.
local function clearEntries()for _, child in ipairs(entriesFrame:GetChildren()) doif child:IsA("TextLabel") and child.Name:match("^Row%d+$") thenchild:Destroy()endendend플레이어 데이터를 리더보드 행에 삽입하고 표시하기 위한 renderLeaderboard라는 함수를 만듭니다. data 객체는 서버에서 반환된 테이블로, 플레이어 사용자 이름과 점수가 포함된 항목을 포함합니다.
local function renderLeaderboard(data)clearEntries()for i, entry in ipairs(data) dolocal row = template:Clone()row.Name = "Row" .. irow.Visible = truerow.LayoutOrder = irow.Text = string.format("%d%s place: %s - %d",i, i == 1 and "st" or i == 2 and "nd" or i == 3 and "rd" or "th",entry.Username, entry.Score)row.Parent = entriesFrameendend주기적으로 리더보드 데이터를 가져오기 위한 루프를 만듭니다. 매 3초마다 루프가 실행되고, 이전에 LeaderboardRemoteHandler 스크립트에서 생성한 InvokeServer 함수를 사용하여 서버에서 호출됩니다. 호출이 성공적이면 UI가 업데이트된 점수로 새로 고쳐집니다.
while true dolocal success, data = pcall(function()return leaderboardRemote:InvokeServer()end)if success and data thenrenderLeaderboard(data)endtask.wait(3)end
최종 코드
다음 코드 조각은 GoldManager, LeaderboardManager, LeaderboardRemoteHandler, 그리고 LeaderboardGuiScript 스크립트에 대한 완전한 내용을 보여줍니다. 이 코드를 금광산 리더보드 만들기 튜토리얼 - 시작 .rbxl 파일에 직접 붙여넣고 실행할 수 있습니다.
GoldManager
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 leaderboardStore = DataStoreService:GetOrderedDataStore("GlobalLeaderboard")
-- 금 조각의 위치를 무작위로 결정하기 위한 난수 생성기 생성
local random = Random.new()
-- 단일 세션에 대한 플레이어의 금 조각을 저장할 테이블
local playerGold = {}
-- 상수
local GOLD_VALUE = 10 -- 각 금 조각의 가치
local GOLD_REGENERATION_BOUNDS = { -- 금 조각을 무작위로 재위치시키는 데 사용되는 영역 경계
minX = -72,
maxX = 72,
minZ = -72,
maxZ = 72
}
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, "now has", playerGold[userId], "gold")
-- 이 세션의 새로운 금 점수를 반영하도록 UI 점수 바를 업데이트합니다.
uiEvent:FireClient(player, {
gold = playerGold[userId],
doTween = true,
showAlert = false
})
-- 잠시 기다렸다가 수집된 금 조각을 무작위 위치에 다시 생성합니다.
task.delay(GOLD_REGEN_DELAY, function()
-- 새로운 무작위 위치에서 금 조각을 다시 생성합니다.
local nextPositionX = random:NextInteger(GOLD_REGENERATION_BOUNDS.minX, GOLD_REGENERATION_BOUNDS.maxX)
local nextPositionZ = random:NextInteger(GOLD_REGENERATION_BOUNDS.minZ, GOLD_REGENERATION_BOUNDS.maxZ)
chunk.CFrame = CFrame.new(nextPositionX, 50, nextPositionZ) -- 새로운 위치로 청크 이동
chunk:SetAttribute("DropCollided", false) -- 충돌 플래그 초기화
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)
leaderboardStore:SetAsync(tostring(userId), value)
end)
if not success then
warn("Could not save gold for", player.Name, ":", err)
end
end
-- 플레이어가 게임에 참여할 때 처리하는 함수
local function onPlayerAdded(player)
local userId = player.UserId
print(player.Name, "has joined the game")
-- 데이터 저장소에서 플레이어의 금을 불러오려고 시도합니다.
local success, storedGold = pcall(function()
return goldStore:GetAsync(userId)
end)
-- 플레이어에 대한 데이터 저장소 값을 사용하여 현재 세션 금 점수를 설정합니다.
local currentGold = if success and storedGold then storedGold else 0
playerGold[userId] = currentGold
-- 플레이어에 대한 데이터 저장소 값을 UI에 업데이트합니다.
uiEvent:FireClient(player, {
gold = currentGold,
doTween = false,
showAlert = not success
})
if success then
print("Loaded", currentGold, "gold for", player.Name)
else
-- 플레이어에게 금액을 불러올 수 없음을 알립니다.
uiEvent:FireClient(player, { showAlert = true })
warn("Could not load gold for", player.Name)
end
-- 플레이어의 금을 매 30초마다 저장하는 자동 저장 코루틴을 시작합니다.
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
-- 플레이어가 게임에서 나갈 때 처리하는 함수
local function onPlayerRemoving(player)
local userId = player.UserId
print(player.Name, "has left the game")
-- 플레이어의 금이 추적되었는지 확인합니다.
if playerGold[userId] then
-- 플레이어가 떠나기 전에 데이터 저장소에 플레이어의 금을 저장합니다.
saveGold(player, playerGold[userId])
end
end
-- 플레이어 참여 및 이탈 이벤트에 연결
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
LeaderboardManager
LeaderboardManager 모듈 스크립트:
- GlobalLeaderboard 정렬된 데이터 저장소에서 GetSortedAsync()를 호출합니다.
- 사용자 ID를 사용하여 GetNameFromUserIdAsync를 사용하여 사용자 이름으로 변환합니다.
- 상위 순위 플레이어의 { Username, Score } 테이블 목록을 반환합니다.
- 클라이언트가 리더보드 데이터를 요청할 때마다 LeaderboardRemoteHandler에서 호출됩니다.
-- 서비스 가져오기
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
-- 리더보드 점수를 저장하기 위한 정렬된 데이터 저장소
local leaderboardStore = DataStoreService:GetOrderedDataStore("GlobalLeaderboard")
-- 리더보드 점수를 저장할 테이블
local leaderboardManager = {}
-- 상위 점수를 반환하는 함수
function leaderboardManager.GetTopScores(limit)
-- 데이터 저장소에서 리더보드 항목의 정렬된 페이지를 검색하려고 시도합니다.
local success, pages = pcall(function()
-- false = 내림차순 (상위 점수 먼저)
return leaderboardStore:GetSortedAsync(false, limit)
end)
-- 호출이 실패하거나 아무것도 반환되지 않을 경우 경고를 기록하고 빈 테이블을 반환합니다.
if not success or not pages then
warn("Failed to get leaderboard data")
return {}
end
-- 상위 플레이어 데이터를 저장할 테이블을 생성합니다.
local topPlayers = {}
-- 리더보드 결과의 현재 페이지마다 각 항목을 반복합니다.
for _, entry in ipairs(pages:GetCurrentPage()) do
local userId = tonumber(entry.key) -- 엔트리 키를 숫자로 변환 (userId는 문자열로 저장됨)
local score = entry.value -- 값은 플레이어의 점수 (금액)
local username = "Unknown" -- 조회 실패 시 기본값
-- userId에서 플레이어의 사용자 이름을 가져오려고 시도합니다.
local ok, name = pcall(function()
return Players:GetNameFromUserIdAsync(userId)
end)
-- 조회가 성공하면 실제 사용자 이름을 저장합니다.
if ok then
username = name
end
-- 이 플레이어와 그들의 점수를 목록에 추가합니다.
table.insert(topPlayers, {
Username = username,
Score = score
})
end
-- 사용자 이름과 점수를 가진 상위 플레이어의 전체 목록을 반환합니다.
return topPlayers
end
-- 다른 스크립트가 사용할 수 있도록 LeaderboardManager 모듈을 반환합니다.
return leaderboardManager
LeaderboardRemoteHandler
LeaderboardRemoteHandler 스크립트는 호출하는 클라이언트에게 상위 3개의 점수를 가져오고 반환합니다.
-- 서비스 가져오기
local ServerScriptService = game:GetService("ServerScriptService") -- 서버 측 모듈 접근
local ReplicatedStorage = game:GetService("ReplicatedStorage") -- 클라이언트-서버 간 통신 객체
-- 데이터 저장소에서 리더보드 데이터를 요청하는 데 사용하는 RemoteFunction을 기다립니다.
local leaderboardRemote = ReplicatedStorage:WaitForChild("LeaderboardRemote")
-- 상위 점수를 가져오는 기능이 포함된 LeaderboardManager 모듈을 불러옵니다.
local leaderboardManager = require(ServerScriptService:WaitForChild("LeaderboardManager"))
-- 클라이언트가 리더보드 원격 함수를 호출할 때 설정합니다.
leaderboardRemote.OnServerInvoke = function(player)
-- 리더보드 모듈을 호출하여 상위 3개의 점수를 가져오고 클라이언트에 반환합니다.
return leaderboardManager.GetTopScores(3)
end
LeaderboardGuiScript
LeaderboardGuiScript 로컬 스크립트:
- Class.RemoteHandler.InvokeServer|InvokeServer를 사용하여 매 3초마다 서버에서 리더보드 데이터를 요청합니다.
- 각각의 리더보드 행을 표시하기 위해 숨겨진 LeaderboardEntry 템플릿을 사용합니다.
- 각 행을 "1st place: Username - Score" 형식으로 포맷합니다.
- UIListLayout을 사용하여 리더보드 행의 생성, 표시 및 레이아웃을 관리합니다.
-- RemoteFunction 접근을 위한 공유 스토리지 가져오기
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 서버에서 리더보드 데이터를 요청하는 데 사용되는 RemoteFunction을 가져옵니다.
local leaderboardRemote = ReplicatedStorage:WaitForChild("LeaderboardRemote")
-- UI 참조
local gui = script.Parent -- ScreenGui 아래의 LocalScript
local leaderboardFrame = gui:WaitForChild("LeaderboardFrame") -- 리더보드의 주요 컨테이너
local entriesFrame = leaderboardFrame:WaitForChild("EntriesFrame") -- 리더보드 항목이 표시되는 프레임
local template = entriesFrame:WaitForChild("LeaderboardEntry") -- 행 생성을 위해 사용되는 숨겨진 템플릿 TextLabel
-- 오래된 리더보드 행을 제거하는 함수 (템플릿 제외)
local function clearEntries()
for _, child in ipairs(entriesFrame:GetChildren()) do
-- 이름이 "Row1", "Row2" 등인 라벨만 파괴합니다 (템플릿은 남겨둡니다).
if child:IsA("TextLabel") and child.Name:match("^Row%d+$") then
child:Destroy()
end
end
end
-- 화면에 리더보드 데이터를 표시하는 함수
local function renderLeaderboard(data)
-- 우선 오래된 행 제거
clearEntries()
for i, entry in ipairs(data) do
local row = template:Clone() -- 숨겨진 템플릿 복제
row.Name = "Row" .. i -- 고유한 이름 부여
row.Visible = true -- 표시
row.LayoutOrder = i -- UIListLayout이 올바르게 정렬하도록 설정
-- 리더보드 포맷: "1st place: Username - 123"
row.Text = string.format("%d%s place: %s - %d",
i,
i == 1 and "st" or i == 2 and "nd" or i == 3 and "rd" or "th",
entry.Username,
entry.Score
)
-- UI에 데이터 추가
row.Parent = entriesFrame
end
end
-- 주요 루프: 3초마다 리더보드를 업데이트합니다.
while true do
-- 리더보드 데이터에 대해 서버에 요청합니다.
local success, data = pcall(function()
return leaderboardRemote:InvokeServer()
end)
if success and data then
-- 리더보드에 결과 표시
renderLeaderboard(data)
else
warn("Failed to fetch leaderboard data")
end
-- 업데이트하기 전에 3초 대기
task.wait(3)
end