Unity - 네트워크 팀프로젝트 6

땡구의 개발일지·2025년 8월 7일

Unity마스터

목록 보기
72/78

승 수가 가장 높은 10명을 출력하는 리더보드를 만들어보자

값 불러오기

데이터베이스에서 값을 불러 올 때, 정렬과 필터링을 사용해서 가져온다
1. OrderByChild("win") : int형인 승리 수를 기준으로 정렬한다.
2. LimitToLast(10) : 정렬된 결과 값에서 가장 마지막에 있는 10개의 결과값을 가져온다.

정렬은 늘 오름차순으로 되기 때문에, LimitToLast를 하게 되면 스냅샷의 값은 순서대로 1등~ 10등이 된다. 즉, 맨 마지막 결과값부터 가져오게 된다.

dbRef.Child("users").OrderByChild("win").LimitToFirst(10).GetValueAsync().ContinueWithOnMainThread(task =>
{
    if (task.IsCanceled)
    {
        Debug.LogWarning("값 불러오기 취소");
        return;
    }

    if (task.IsFaulted)
    {
        string error =  task.Exception?.InnerException?.Message;
        
        Debug.LogWarning($"값 불러오기 실패{error}");
        return;
    }

    DataSnapshot snapshot = task.Result; 
    
    // 이후는 가져온 결과값으로 처리하기
}

리더보드 만들기

데이터베이스에서 가져온 결과값을 저장하고 리더보드를 만든다

LeaderBoardEntry

각 유저의 정보를 저장할 클래스를 만든다. 이름, 승 수, 승률을 가지고 있다.

public class LeaderBoardEntry
{
    public string nickName;
    public int win;
    public float winRate;

    public LeaderBoardEntry(string nickName, int win, float winRate)
    {
        this.nickName = nickName;
        this.win = win;
        this.winRate = winRate;
    }
}

LeaderBoard

LeaderBoardEntry를 리스트로 가지는 클래스. 1위부터 10위까지의 정보를 모두 들고 있게 된다.

public class LeaderBoard
{
	// 1위부터 10위까지의 정보를 리스트로 가지고 있다.
    public List<LeaderBoardEntry> ranker;
    
    // 초기화 작업
    public void Init()
    {
        ranker = new List<LeaderBoardEntry>();
    }
    // 리스트에 유저를 추가한다.
    public void ListUp(string nickName, int win, float winRate)
    {
        LeaderBoardEntry entry = new LeaderBoardEntry(nickName,win,winRate);
        ranker.Add(entry);
    }
}

보안 규칙 및 색인


".indexOn" 색인을 통해서 더욱 빠르게 데이터를 가져올 수 있다. 데이터 베이스에 저장되는 값들이 색인을 통해 정리되면서 저장된다.
값을 가져오는 것 또한 정리된 채로 들여오게 된다.

문제 발생

데이터가 전부 넘어오지 않는 문제가 있다. 이유를 알 수 없음.UID들의 정보들이 단일화 된 자료구조가 아니라서 못 가져오는 것이라 판단된다.

public static void GetLeaderboard()
{
    dbRef.Child("users").OrderByChild("wins").LimitToLast(10).GetValueAsync().ContinueWithOnMainThread(task =>
    {
        if (task.IsCanceled)
        {
            Debug.LogWarning("값 불러오기 취소");
            return;
        }

        if (task.IsFaulted)
        {
            string error =  task.Exception?.InnerException?.Message;
            
            Debug.LogWarning($"값 불러오기 실패{error}");
            return;
        }

        DataSnapshot snapshot = task.Result;
        LeaderBoard.ranker =  new List<LeaderBoardEntry>();
        LeaderBoard.ranker.Clear();
        Debug.Log("여기는 들어옴"); //여기 들어오는것 확인 됨
        int index = 0;
        foreach (var entry in snapshot.Children)
        {
            string nickname;
            if (entry.Child("nickname").Exists)
            {
                nickname = entry.Child("nickname").Value.ToString();
            }
            else
            {
                nickname = "Unknown";
            }

            int win;
            if (entry.Child("wins").Exists)
            {
                win = (int)(long)entry.Child("wins").Value;
            }
            else
            {
                win = 0;
            }

            float winRate = 0f;
            if (entry.Child("winRate").Exists)
            {
                winRate = (float)(double)entry.Child("winRate").Value;
            }
            else
            {
                winRate = 0;
            }
            LeaderBoard.ListUp(nickname,win,winRate);
            Debug.Log($"{LeaderBoard.ranker[index].nickName}{LeaderBoard.ranker[index].win}"); // 이건 실행 안됨
            index++;
        }

    });
}

챗지피티, 강사님, 같은 수강 동료들 등 이유를 물어물어봤으나 답이 안나왔는데, 이전에 공부하면서 작성한 코드를 보니 정답이보였다.

FirebaseDatabase db

void Awake()
{
	db = FirebaseDatabase.DefaultInstance;
    db.GoOnline();
}

파이어베이스의 Realtime Database를 쓸 때는 GoOnline을 해줘야 한다.
플레이어의 정보를 저장할 때는 UserData 클래스 객체를 Json으로 저장하고, 불러올 때도 Json으로 불러온 다음, UserData로 변환해서 사용한다.

UserData

유저들의 정보를 클래스 객체로 저장한다

[System.Serializable]
public class UserData
{
    public string userName;
    public int wins;
    public int losses;
    public float winRate;

    public UserData(string userName, int wins, int losses, float winRate)
    {
        this.userName = userName;
        this.wins = wins;
        this.losses = losses;
        this.winRate = winRate;
    }
}

LeaderBoardEntry

리더보드에 등록되는 유저의 정보를 담는 클래스다.

[System.Serializable]
public class LeaderBoardEntry
{
    public string nickName;
    public int win;
    public float winRate;

    public LeaderBoardEntry(string nickName, int win, float winRate)
    {
        this.nickName = nickName;
        this.win = win;
        this.winRate = winRate;
    }
}

LeaderBoard

LeaderBoardEntry를 리스트로 가지고 있다. 리더보드에서 1위부터 10위까지 걸리게 되는 유저 데이터를 가진다.

public class LeaderBoard : MonoBehaviour
{

    [SerializeField] public List<LeaderBoardEntry> ranker = new List<LeaderBoardEntry>();

    [SerializeField] public List<UserData> userData = new List<UserData>();


    public void SetLeaderBoard()
    {
        Manager.FB.GetLeaderBoard();
    }

    public void UserDataToLeaderBoard(List<UserData> userData)
    {
        ranker.Clear();
        this.userData = userData;
        userData.Sort((a, b) => b.wins.CompareTo(a.wins));
        for (int i = 0; i < 10; i++)
        {
            ListUp(userData[i].userName,userData[i].wins,userData[i].winRate);
        }
    }
    
    private void ListUp(string nickName, int win, float winRate)
    {
        LeaderBoardEntry entry = new LeaderBoardEntry(nickName,win,winRate);
        
        ranker.Add(entry);
    }
}

리스트를 한 번 내림차 순으로 정렬해서 순서대로 Leaderboard의 리스트에 집어넣는다. 그러면 순서대로 1위 ~ 10위가 된다.

저장하기

파이어 베이스에 UserData 클래스 객체를 json으로 변환한 후 현재 유저의 uid 하위로 저장한다.

public void TestSave()
{
    UserData userData = new(testName, testWin, testLose, testWinRate);
    string json = JsonUtility.ToJson(userData);

    userRef.SetRawJsonValueAsync(json).ContinueWithOnMainThread(task => {
        if (task.IsCompleted)
        {
            Debug.Log("닉네임 업로드 성공");
        }
        else
        {
            Debug.LogError("닉네임 업로드 실패: " + task.Exception);
        }
    });
}

불러오기

파이어베이스에서 현재 유저의 uid를 기준으로 json으로 데이터를 불러오고, UserData 클래스 객체로 변환한다.

public void GetLeaderBoard()
{
    user = Auth.CurrentUser;
    uid = user.UserId;
    userRef = dbRef.Child("users").Child(uid);  // 사용자 데이터 경로 지정
    
    List<UserData> userDatas =  new List<UserData>(); // 결과를 저장할 리스트
    
    Debug.Log("여기는 들어옴"); //여기 들어오는것 확인 됨    dbRef.Child("users").OrderByChild("wins").LimitToLast(10).GetValueAsync().ContinueWithOnMainThread(task =>
    {
        if (task.IsCanceled)
        {
            Debug.LogWarning("리더보드 불러오기 취소");
        }

        if (task.IsFaulted)
        {
            Debug.LogWarning($"리더보드 불러오기 실패{task.Exception?.InnerException?.Message}");
        }

        DataSnapshot snapshot = task.Result;
        foreach (var entry in snapshot.Children)
        {
            string json = entry.GetRawJsonValue();
            UserData fromJson = JsonUtility.FromJson<UserData>(json);
            userDatas.Add(fromJson);
        }

        LeaderBoard.GetUserData(userDatas); //리더보드 객체에 결과 전달
    });
}

테스트


저장된 값이 순서대로 잘 불러와진다.

profile
개발 박살내자

0개의 댓글