Unity에 Firebase Realtime Database를 적용해보기 위해 간단한 프로젝트를 만들었다.
횡스크롤 방치형 게임이고, 서버 DB에는 어떤 유저가 몇 스테이지까지 갔는지를 기록하고 자동으로 Save/Load 되는 기능을 만들어봤다.
Firebase 콘솔 페이지에서 프로젝트를 생성하고, 프로젝트 개요 화면에서 유니티 앱을 추가한다.

그러면 이 화면으로 넘어오는데, 여기서 설정하는 패키지 이름은 이후에 유니티 프로젝트에서의 패키지 이름과 같게 설정해줘야한다.
앱을 추가하고 나면, google-services.json이라는 파일을 다운받는다. 이 파일 안에는 Firebase 앱의 여러 정보와 DB의 URL등이 들어있다.
다운로드 받은 json파일을 유니티 프로젝트의 Assets 폴더 내에 넣어주면 된다.

Unity 전용 Firebase SDK를 받으면, 압축파일 안에 여러 개의 패키지 파일이 있다. 나는 실시간 DB를 사용할 것이기 때문에 FirebaseDatabase 패키지를 유니티 프로젝트에 임포트했다.
임포트하고 나면, 유니티 콘솔창에 에러가 발생할 수 있는데, 나 같은 경우엔 IOS 전용 XCode가 설치되지 않아서 발생했었다. 지금은 필요없기 때문에 Firebase의 IOS 전용 어셈블리 파일이 자동으로 종속성을 찾는 옵션을 꺼서 해결했다.
Firebase는 모바일 플랫폼이 주 타게팅이라서 Desktop 플랫폼에서는 제대로 동작하지 않을 수 있다고 한다.
아직 베타 기능이지만, 게임의 개발 도중에는 에디터도 PC에서 돌아가고, 디버깅도 PC로 하는게 편하니까 데스크탑 환경에서도 동작하도록 하는 방법이 있다고 한다.

에디터가 google-services.json 파일을 찾을 수 있게 StreamingAssets 폴더 안에 넣어주면 된다.
간단하게 UserID를 key로 해서 몇 스테이지까지 도달했는지를 저장하기로 했다.
로그인/로그아웃 기능 대신, 게임을 처음 실행하면 고유한 UserID를 생성하고, 이 UserID를 이용해 DB에 접근하기로 했다.
UserID는 고유한 값을 사용하기 위해 GUID를 이용했다.
public class UserGUIDManager
{
private readonly string _path = $"{Application.persistentDataPath}/UserID.data";
private Guid _userID;
private bool _isReadYet = false;
public Guid GetUserID()
{
if (!_isReadYet)
{
if (File.Exists(_path))
_userID = ReadDataFile();
else
{
_userID = Guid.NewGuid();
CreateDataFile();
}
Debug.Log($"User GUID: {_userID}");
_isReadYet = true;
}
return _userID;
}
private void CreateDataFile()
{
using FileStream fs = new(_path, FileMode.Create, FileAccess.Write);
{
byte[] data = _userID.ToByteArray();
fs.Write(data, 0, data.Length);
}
}
private Guid ReadDataFile()
{
Guid res;
using FileStream fs = new(_path, FileMode.Open, FileAccess.Read);
{
byte[] data = new byte[fs.Length];
int remain = (int)fs.Length;
int offset = 0;
while (remain > 0)
{
int n = fs.Read(data, offset, remain);
if (n == 0)
break;
offset += n;
remain -= n;
}
res = new Guid(data);
}
return res;
}
}
GUID 요청 시, 로컬에 저장된 GUID가 있는지 확인하고, 없다면 새로 생성해서 로컬에 저장한다.
저장된 GUID가 있다면, 파일을 읽어서 GUID로 변환한 후 반환해준다.
public class FirebaseDBManager
{
private DatabaseReference _dbRef;
private bool _isConnected = false;
public void Initialize()
{
_dbRef = FirebaseDatabase.DefaultInstance.RootReference;
}
public void TryRead(Action<int> completed = null, Action faulted = null)
{
GameManager.Instance.StartCoroutine(CheckConnect(completed, faulted));
}
private IEnumerator CheckConnect(Action<int> completed = null, Action faulted = null)
{
float timeOut = 10f;
float startTime = Time.time;
Ping ping = new("8.8.8.8");
while (true)
{
if (ping.isDone)
{
Debug.Log("Network Connect : TRUE");
_isConnected = true;
Read(completed, faulted);
yield break;
}
if (Time.time > startTime + timeOut)
{
Debug.Log("Network Connect : FALSE");
faulted?.Invoke();
yield break;
}
yield return null;
}
}
private void Read(Action<int> completed = null, Action faulted = null)
{
if (!_isConnected)
return;
string id = GameManager.DataManager.UserID.ToString();
_dbRef.Child(id).GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted || task.IsCanceled)
{
Debug.LogWarning(task.Exception.ToString());
faulted?.Invoke();
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
if (snapshot.Value == null || !int.TryParse(snapshot.Value.ToString(), out int res))
{
faulted?.Invoke();
return;
}
completed?.Invoke(res);
}
});
}
public void Write()
{
if (!_isConnected)
return;
string id = GameManager.DataManager.UserID.ToString();
_dbRef.Child(id).SetValueAsync(GameManager.StageManager.StageCnt);
}
}
주요 기능으로는 DB 읽기를 시도하는 기능과 DB에 쓰는 기능, 인터넷 연결을 확인하는 기능이 있다.
인터넷 연결을 확인하는 기능은 처음엔 Application.internetReachability를 사용해봤는데, 인터넷을 끊어도 인터넷이 연결되어있다고 뜨는 경우가 있었고, 유니티 문서에서도 사용을 추천하지 않는다고 해서 다른 방법을 찾았다.
새로 찾은 방법은 구글이 제공해주는 "8.8.8.8" 주소에 Ping을 보내는 것이다.
이 방법으로 인터넷 연결 체크도 할 수 있고, 응답속도도 알아낼 수 있다고 한다.