Coding Feature.

[Unity 3D] 3D 퐁 만들기 #9 Unity Gaming Services 의 Leaderboard 기능을 활용한 온라인 리더 보드 구현. 본문

Toy Project/MICRO-PONG [Unity3D]

[Unity 3D] 3D 퐁 만들기 #9 Unity Gaming Services 의 Leaderboard 기능을 활용한 온라인 리더 보드 구현.

codingfeature 2024. 1. 22. 16:24

이전에 Firebase를 통해 리더보드 데이터베이스를 직접 구축해서 만들어보자 라고 생각을 했었는데 구글에 좀 더 찾던 중에 유니티에서 자체적으로 리더보드를 제공해주는게 있어서 그걸 사용하기로 했습니다.

 

우선 유니티의 리더보드 documentation을 읽어보았습니다.

Leaderboards (unity.com)

 

Leaderboards

Attention: The Digital Services Act (DSA) requires Unity to notify our customers’ end users if Unity takes an action which impacts those end users under the DSA. To comply with this requirement, if you use Unity Gaming Services (UGS) products that rely o

docs.unity.com

 

유니티의 리더보드 기능을 사용하기 위해 해야할 일은 크게 다음과 같았습니다.

 

1. 유니티 클라우드 사이트에서 리더보드 설정 및 구동하기.

2. 유니티 SDK 설치 후 리더보드 연동하기

3. 코드 작성.

4. UI 구현

 

1. 유니티 클라우드 사이트에서 리더보드 설정 및 구동하기.

cloud.unity.com

 

Unity Cloud

 

cloud.unity.com

 

위 사이트의 리더보드 설정 창에 들어가 리더보드 기능을 설정하고 만들었습니다.

위 사진처럼 여러 설정을 한 뒤에,

 

리더보드를 새로 생성했습니다.

 

 

 

2. 유니티 SDK 설치 후 리더보드 연동하기

 

유니티 프로젝트의 패키지 매니저에 들어가서 Leaderboards라는 패키지를 다운받았습니다.

 

추가로 리더보드 기능을 사용하기 위해 Authentication이라는 패키지도 필요하지만 위 리더보드 패키지를 다운받으면 자동으로 다운받아진다고 합니다.

 

 

다운로드를 받으면 현재 유니티 프로젝트를 Unity Game Services 프로젝트 ID와 Linking 하라는 창이 뜹니다.

출처 - 유니티 Documentation

 

아래 Project Settings 버튼을 클릭해서 Linking을 하면 끝납니다.

 

3. 코드 작성.

 

이제 리더보드 기능 구현을 위한 코드를 작성해보겠습니다.

 

참고로 리더보드 기능을 구현하기 위해 도움이 되는 샘플 코드를 Documentation에서 찾을 수 있습니다! 관심 있으시면 참고 바랍니다!

SDK sample (unity.com)

 

SDK sample

SDK sample#This sample (with additional methods) is found as part of the Leaderboards SDK package by checking the box to include Samples when installing from the package manager.The following example presumes the existence of a leaderboard with the ID my-f

docs.unity.com

 

우선 리더보드를 관리하기 위한 매니저를 구현했습니다.

using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Unity.Services.Authentication;
using Unity.Services.Core;
using Unity.Services.Leaderboards;
using UnityEngine;

public class LeaderboardsManager : MonoBehaviour
{
    // Create a leaderboard with this ID in the Unity Cloud Dashboard
    const string LeaderboardId = "micro-pong-leaderboard";

    private static LeaderboardsManager instance;
    public class Score // 리더보드 element
    {
        public string playerId;
        public string playerName;
        public int rank;
        public double score;
    };

    public List<Score> scores = new List<Score>(); // 상위 플레이어 Score 리스트
    public Score playerScore = new Score(); // 현재 플레이어 Score.
    
    public static LeaderboardsManager Instance
    {
        get
        {
            return instance;
        }
    }

    async void Awake()
    {
        if (instance)
        {
            Destroy(instance);
            return;
        }

        instance = this;
        DontDestroyOnLoad(this.gameObject);

        await UnityServices.InitializeAsync();

        await SignInAnonymously(); // 익명으로 서비스에 플레이어 등록
    }

    async Task SignInAnonymously()
    {
        AuthenticationService.Instance.SignedIn += () =>
        {
            Debug.Log("Signed in as: " + AuthenticationService.Instance.PlayerId);
        };
        AuthenticationService.Instance.SignInFailed += s =>
        {
            // Take some action here...
            Debug.Log(s);
        };

        await AuthenticationService.Instance.SignInAnonymouslyAsync();
    }


    // 플레이어 최고 점수 등록 -> 리더보드 및 플레이어 점수 가져오기.
    public async void HandleLeaderboard(int highscore)
    {
        var addScoreResponse = await LeaderboardsService.Instance.AddPlayerScoreAsync(LeaderboardId, highscore); // 플레이어 최고 점수 입력

        var leaderboardResponse = await LeaderboardsService.Instance.GetScoresAsync(LeaderboardId); // 리더보드 가져오기.

        scores.Clear();

        for (int i = 0; i < leaderboardResponse.Total; i++)
        {
            Score newScore = new Score();

            newScore.playerId = leaderboardResponse.Results[i].PlayerId;
            newScore.playerName = leaderboardResponse.Results[i].PlayerName;
            newScore.rank = leaderboardResponse.Results[i].Rank;
            newScore.score = leaderboardResponse.Results[i].Score;

            scores.Add(newScore);
        }

        UIManager.Instance.AddLeaderboardScoreText(); // 플레이어의 리더보드 정보 가져오기.

        var playerScoreResponse = await LeaderboardsService.Instance.GetPlayerScoreAsync(LeaderboardId);

        playerScore.playerId = playerScoreResponse.PlayerId;
        playerScore.playerName = playerScoreResponse.PlayerName;
        playerScore.rank = playerScoreResponse.Rank;
        playerScore.score = playerScoreResponse.Score;

        UIManager.Instance.SetPlayerLeaderboardText();
    }
}

 

먼저 내부에 Score 클래스를 정의해서 플레이어의 ID, 이름, 랭크, 점수를 저장할 수 있도록 합니다.

 

그리고 scores 리스트는 가장 높은 점수를 가진 10명의 Score 객체를 저장하게 됩니다.

playerScore 객체는 현재 플레이어의 Score 데이터를 저장하게 됩니다.

 

HandleLeaderboard 함수는 AddPlayerScoreAsync 함수를 통해 리더보드에 현재 플레이어의 최고 점수를 저장하게 되고, GetScoresAsync 함수를 통해 상위 10명의 점수 데이터를 가져오게 됩니다.

그리고 GetPlayerScoreAsync 함수를 통해 현재 플레이어가 리더보드 내에서의 점수 관련 데이터를 가져오게 됩니다.

 

위 HandleLeaderboard 함수는 게임오버가 되었을때 게임 매니저에서 호출하게 됩니다.

    public void HandleGameOver()
    {
        if (score > highScore)
        {
            highScore = score;
            LeaderboardsManager.Instance.HandleLeaderboard(score);
        }

..
    }

 

UI 매니저에서는 다음 함수들을 구현해서 리더보드에 정보를 표시하도록 했습니다.

    public void AddLeaderboardScoreText()
    {
        // 리더보드 내 모든 텍스트 제거.
        foreach(Transform child in leaderboardcontent.transform)
        {
            GameObject.Destroy(child.gameObject);
        }

        for (int i = 0; i < LeaderboardsManager.Instance.scores.Count; i++)
        {
            int score, rank;
            string playerName;
            score = (int)LeaderboardsManager.Instance.scores[i].score;
            rank = LeaderboardsManager.Instance.scores[i].rank + 1;
            playerName = LeaderboardsManager.Instance.scores[i].playerName;

            GameObject newText = Instantiate(leaderboardScoreText);
            newText.GetComponent<Text>().text = rank + ". " + playerName + " - " + score;
            newText.transform.SetParent(leaderboardcontent.transform);
            newText.transform.localScale = new Vector3(1, 1, 1);
        }
    }

    public void SetPlayerLeaderboardText()
    {
        int score, rank;
        string playerName;
        score = (int)LeaderboardsManager.Instance.playerScore.score;
        rank = LeaderboardsManager.Instance.playerScore.rank + 1;
        playerName = LeaderboardsManager.Instance.playerScore.playerName;
        playerScoreboard.text = rank + ". " + playerName + " - " + score;
    }

 

 

4. UI 구현

 

리더보드 구현을 위해 유니티의 UI 요소 중 Scrollview 를 사용했습니다.

 

Scrollview/Viewport/Content 게임 오브젝트에 다음과 같이 컴포넌트를 추가해서 content 아래 child에 어떤 text를 입력해도 일정한 간격으로 정렬, 시각화되도록 했습니다.

 

위 내용은 아래 영상을 참고했습니다!

https://youtu.be/oOox4JNQp_M?si=nuEs4yOghHXMzh4J