본문으로 바로가기

Unity3D) Firebase 연동

category Unity3D/기능 2021. 2. 18. 01:42

Unity3D에서 Firebase 연동에 대해서 설명합니다.

Firebase에 대한 전체적인 설명은 다음과 같습니다 : (현재 작성중)

 

이곳에서 Firebase에 대한 예제와 설명을 볼 수 있습니다.

firebase.google.com/docs/unity/setup?hl=ko

 

1. 구글 파이어베이스에 접속하여, 시작하기를 누릅니다.

그럼 Firebase 프로젝트 화면이 나타납니다. 프로젝트 추가를 누릅니다.

 

구글 파이어베이스에 접속하여 시작하기를 누르면 프로젝트 추가라는 버튼이 보입니다.

 

2. (1) 프로젝트 이름

   (2) 약관 동의

   (3) 애널리틱스 사용 설정 ( 이왕이면 사용 )

   (3-1) 애널리틱스 구성

을 하여 Firebase를 추가합니다.

 

3. 약간의 시간이 걸린 후, 프로젝트가 준비됐다는 말과 함께 '계속' 버튼이 나옵니다.

누르면 내가 생성한 Firebase가 생성됩니다.

우리는 Unity를 연동할 것 이므로, 프로젝트 개요 화면에서 Unity 를 눌러서 시작합니다.

 

 

자신이 Firebase와 연동할 플랫폼 ( Android, iOS ) 을 선택합니다.

iOS 번들 ID, Android 패키 지 이름 등 자신의 유니티 앱이 설정할 아이디를 작성합니다.

나중에 Unity에서 플랫폼에 맞춰 빌드를 할 때, 아이디가 동일하지 않으면 Firebase와의 연동이 되지 않습니다.

만약 자신이 둘 다가 아닌, 하나의 플랫폼만 사용한다면 하나의 앱만 등록하면 됩니다.

앱 닉네임은 선택사항이며, Firebase내에서 어떻게 보일 것인지 설정하는 것입니다.

 

필자는 둘 다 연동할 것이기 때문에 둘 다 체크

 

 

google-services.json ( Android ), GoogleService-info.plist ( iOS ) 를 다운로드 받아, Assets 폴더 내에 추가합니다.

만약 Android, iOS 둘 다가 아닌, 하나의 플랫폼만 선택했다면 하나의 파일만 다운로드 할 수 있습니다.

다음은 Firebase SDK 입니다.

Firebase Unity SDK(Zip)을 다운로드 하여, 사용할 파일만 Unity 내에서 Import 합니다.

Assets -> Import Package -> Custom Package 를 클릭하여 Import 합니다.

필자는 Auth, Analytics, Database만을 추가했습니다.

 

모든 패키지를 Import 할 필요는 없습니다.

 

이제 기초적인 세팅은 끝났습니다. 

 

이제 Firebase에 대한 연동 Script를 작성해야 합니다.

필자같은 경우는 Firebase를 게임 시작 전 ( Read Ranking ), 게임 끝난 후 ( Write Ranking ) 계속 사용하기 때문에,

Manager로 관리하였습니다.

 

Awake에서 다음과 같이 작성합니다.

private void Awake()
{

  Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
    var dependencyStatus = task.Result;
    if (dependencyStatus == Firebase.DependencyStatus.Available) {
      // Create and hold a reference to your FirebaseApp,
      // where app is a Firebase.FirebaseApp property of your application class.
         app = Firebase.FirebaseApp.DefaultInstance;

      // Set a flag here to indicate whether Firebase is ready to use by your app.
    } else {
      UnityEngine.Debug.LogError(System.String.Format(
        "Could not resolve all Firebase dependencies: {0}", dependencyStatus));
      // Firebase Unity SDK is not safe to use here.
    }
  });
}

CheeckAndFixDependenciesAsync를 통해 Google Play 서비스를 확인하고 업데이트 내용을 확인해야 합니다.

만약 하지 않으면, 접근할 수 없습니다.

 

이를 통해 Google 서비스 버전 요구사항을 확인할 수 있었습니다.

실제로 Database를 접근하기 위해선 조금 더 추가할 내용이 있습니다.

 

4. 실제로 Database를 생성하여야 합니다. Firebase는 NoSQL이기 때문에 제약사항을 작성할 필요는 없습니다.

Google Firebase 의 자신의 프로젝트 에서 Realtime Database를 클릭합니다.

그리고 데이터 베이스 만들기를 클릭합니다.

 

 

4-1) 실시간 데이터베이스 위치는 기본적인 '미국' 으로 합니다.

4-2) 보안 규칙이 필요합니다. 나중에 모드를 변경할 수 있으니, 우리는 일단 연동을 위해 '테스트 모드'를 선택합니다.

30일 내로 모드를 수정하여야 합니다.

 

 

생성하면 다음과 같은 데이터베이스가 생성됩니다.

 

 

필자는 '이름' 과 '랭킹' 을 가지고 데이터베이스를 생성할 것입니다.

데이터베이스를 어떻게 구성하는지는 사용자 마음이므로 저는 제 식대로 생성합니다.

 

 

각각의 Child들을 'key' 라고 합니다. ( example.... , rank,  0 )

 

 

 

이제 데이터베이스도 생성했으니, 데이터베이스에 연결해봅니다.

 

private void Awake()
{
  // ...
  
  AppOptions options = new AppOptions { DatabaseUrl = new Uri("https://example-5476c-default-rtdb.firebaseio.com/")};
  FirebaseApp app = FirebaseApp.Create(options);
  _reference = FirebaseDatabase.DefaultInstance.GetReference("rank");
}

 

DatabaseUrl 은 자신의 데이터베이스 위에 보면 https://example...firebaseio.com/ 같이 데이터베이스 주소가 있습니다.

이를 클릭하면 복사할 수 있는데, 이 것을 DatabseUrl 로 사용하면 됩니다.

 

_reference는 DatabaseReference 타입입니다.

이를 통해 처음 Key ( rank ) 에 미리 접근하여 놓는 것입니다.

그럼 데이터베이스를 계속 파고들 필요 없이, rank 컬럼 내에서 삽입 & 삭제 & 읽기 등을 할 수 있습니다.

 

데이터를 실제로 가져와 보겠습니다. _reference에 접근하면 됩니다.

현재 _reference는 rank 에 접근해 있습니다. 

우리는 데이터가 추가될 때 rank 밑에서 하나의 컬럼을 새로 생성하여 데이터를 Push 할 것 이기 때문에

rank 밑의 모든 값들을 읽어 오면 됩니다.

 

저는 데이터를 유연(?) 하게 사용하기 위해, Generic 타입으로 정보를 얻어올 수 있도록 하였습니다.

또한 Firebase는 데이터들을 기본적으로 '비동기' 로 읽어오기 때문에 ( 즉, 서브 스레드 ) 

데이터를 다 읽어옴과 동시에 callback 처리를 할 수 있도록 Action 타입을 받습니다.

 

public void GetInformation<T>(Action<List<T>> callback) where T : new ()
{
    var list = new List<T>();
    
    _reference.GetValueAsync().ContinueWith(task =>
    {
    	if(task.IsCompleted)
        {
            var snapshot = task.Result; // 0, ?, ? 같이 우리가 추가한 컬럼이 이곳에 해당합니다.
            foreach(var data in snapshot.Children)
            {
            	var info = (IDictionary) data.Value; // name, score 같은 컬럼이 이곳에 해당합니다.
                foreach(IdctionaryEntry elem in info)
                {
               	    if(elem.Value is T value)
                        list.Add(value);
                }
            }
            callback(list);
        }
    }
}

_reference는 rank 입니다. 이곳에서 snapshot에 접근하면 0, 1, 2 등의 컬럼 (키)이 등장합니다.

이 모든 컬럼들에 대해서 다시 접근을 하면, 각 0, 1, 2 등의 컬럼의 자식들인 name, score 같은 컬럼이 등장합니다.

그 컬럼 중에서 내가 원하는 타입 <T> 타입인 원소만 list에 추가하여 callback 함수에 넘겨줍니다.

 

이제 이 함수를 실제로 사용하는 방법을 봅시다.

필자는 FirebaseManager라는 Singleton 으로 사용하기 때문에 다음과 같이 작성합니다.

 

private void Start()
{
    FirebaseManager.Instance.GetInformation<long>(SetRanking);
}

 

SetRanking 함수는 다음과 같습니다.

 

private void SetRanking(List<long> list)
{
    list.Sort((lhs, rhs) =>
    {
        if(lhs > rhs) return -1;
        if(lhs == rhs) return 0;
        if(lhs < rhs) return 1;
        return lhs.CompareTo(rhs);
    });
}

필자는 list를 내림차순으로 정렬하여, 순위를 매기기 때문에 정렬시켰습니다.

 

다음은 데이터를 추가하는 방법을 설명합니다.

 

어떻게 추가하든 상관은 없지만, Json으로 만들어야 한다는 규약은 있습니다.

랭킹에 등록할 class를 만들고 이를 JsonUtility.ToJson으로 Json으로 변경하여 rank 내에 추가할 예정입니다.

 

Rank class는 다음과 같습니다.

 

class Rank
{
    public string name;
    public long score;
    
    public Rank(string name, long score)
    {
        this.name = name;
        this.score = score;
    }
}

 

다음은 Firebase에 Push하는 함수입니다.

 

public void AddInformation(string name, long score)
{
    Rank user = new Rank(name, score);
    string json = JsonUtility.ToJson(user);
    string key = _reference.Push().Key;
    
    _reference.Child(key).SetRawJsonValueAsync(json);
}

Rank 클래스 객체를 Json으로 변경한 후,

_reference( 현재 'rank' 컬럼 ) 에서 Push 가능한 Key를 생성해냅니다. ( key 객체 )

그리고 key객체에 새로운 값들을 추가하는 것입니다.

 

Push 가능한 Key를 생성해낸다. 즉 우리가 직접 정해준 것이 아니기 때문에 랜덤으로 생성되며,

값이 추가되면 다음과 같이 추가됩니다.

 

즉 랜덤으로 만들어진 'Key' 를 Child로 설정하여 그 'Key'에 값을 집어넣는 행위입니다.

우리는 rank, 0, 1, 등등 모든 것을 Key라고 설명하였습니다.

그렇기에, Key를 생성한다는 것은 이러한 컬럼을 생성하는것이라고 생각하시면 됩니다.

 

예제는

github.com/kimduuukbae/Today-I-Learned/tree/master/Unity3D

이곳에서 확인할 수 있습니다. ( 아직 추가 안됨 )

 

 

 

주의점

모든 Async가 붙은 함수들은 기본적으로 '비동기' 처리입니다.

즉 '서브 스레드'에서 작동되는 것 -> '서브 스레드' 에서는 Unity의 객체의 접근이 '불가능' 합니다.

callback 함수내에서 Unity 객체의 접근을 하면 안됩니다. 따로 flag 변수를 두어서

Update 또는 다른 함수내에서 처리하여야 합니다.

 

간혹가다가 데이터를 읽어오지 못하고 0으로 출력되는 경우가 있습니다.

이는 CheckAndDependencies()-> 후의 모든 내용들이 다 '비동기' 처리이기 때문에,

사용자가 데이터를 요청할 때, '비동기' 처리되어야할 모든 전처리들이 처리되지 않았으므로

0이라는 값을 출력하는 것입니다.

 

즉, 모든 데이터는 동기화시켜야할 부분이 있다면 사용자가 '직접' 동기화 처리 하여야 합니다.

'Unity3D > 기능' 카테고리의 다른 글

Unity3D) Input System  (0) 2021.02.17
Unity3D) Navigation - 2  (0) 2021.01.31
Unity3D) Navigation - 1  (0) 2021.01.31