LoginSignup
12

More than 3 years have passed since last update.

【Unity×Cloud Firestore(Firebase)】UnityにおけるFirestoreへの書き込み&読み込み

Last updated at Posted at 2020-06-16

はじめに

Cloud FirestoreはFirebaseの機能の1つです。今までUnityにはSdkが配布されておらず、RealtimeDatabaseの方(こちらにはSdkが配布されていたため)を利用するケースが多かったと思われます。しかし、知らぬ間にFirestoreのSdkも配布を開始していました。
とはいえ、だいぶ新しい(2020年に出たっぽい?)こともありUnity(C#)に関しては情報が少ないため、Firebaseなんもわからんな身ながら使い方を備忘録として残しておこうと思います。

ちなみにUnityに限らなければ他の記事でも十分紹介されていると思うので、Firestore自体の構造の説明などは基本省いています(書くのがめんどくさいわけじゃないですよ…?)。

情報の書き込み

まずは情報の書き込み方法です。一番簡易的なものは以下の通り。

// id未指定(自動で生成されます)
Task writeTask = dataBase.Collection(path).Document().SetAsync(data);

// id指定
Task writeTask = dataBase.Collection(path).Document(id).SetAsync(data);

dataDictionary<string, object>のもの。ちゃんと終了したのかなどをしっかり確認する場合は上記のようにTaskとして変数に保存して、task.IsCompletedtrueなるまでコルーチンで待機させたり、~.SetAsync(data).ContinueWith()(task.ContinueWith()でも可)などで対応してあげましょう。

以下は書き込む内容の例。書き込むデータはDictionary<string, object>です。Keyの方はそのままデータベース上のキーになります。大文字、小文字でも異なるので注意しましょう。

Dictionary<string, object> data = new Dictionary<string, object>()
{
   {"Name", "Hoge"},
   {"Point", 1},
   {"IsCompleted", true},
};

情報の読み込み

全データ取得

以下のコードはidと等しいIdのドキュメントにおけるPointフィールドの値を取得してログ出力しているものです。

AllDataSample.cs
        // dataBase = FirebaseFirestore.DefaultInstance;
        public async void ReadPointAsync(string id)
        {
            int point = 0;
            Query query = dataBase.Collection(userDataPath);
            QuerySnapshot querySnapshot = await query.GetSnapshotAsync();

            foreach (DocumentSnapshot document in querySnapshot.Documents)
            {
                if (document.Id.Equals(id))
                {
                    Debug.Log($"Hit user data.(Id:{document.Id})");
                    Dictionary<string, object> docDictionary = document.ToDictionary();
                    if (docDictionary.ContainsKey("point"))
                    {
                        Debug.Log($"Point:{docDictionary["point"]}");
                    }

                    break;
                }
            }
        }

image.png
上のスクリーンショットのようなドキュメントがあるとしたら、(Idは等しかったとして)Point:0というログ出力が得られるはずです。

dataBaseFirebaseFirestore.DefaultInstanceです。
userDataPathはスペルミス防止かつ何度も文字列打つのだるいので、あらかじめ記載コード外の部分で宣言してあるコレクションのパス(今回の例で言うと値はUserData)です。なんなら"point"とかも全部似たような感じであらかじめ宣言してあるんですが、あんまり事前にメソッド外で宣言した変数ばっかになると備忘録の際はわかりにくいかなと思ったので、今回は直に記述し直しました。

結果を返すメソッドにする

先ほどのはvoidなので、取得したデータを返したりはしていませんでした。もちろん、先ほどのメソッド内でデータセットを行うこともできますが、一応他のクラスから使用する場合なども考えて、結果を返すメソッドにしてみます。

AllDataReturnSample.cs.cs
        public async Task<int> ReadPointAsync(string id)
        {
            int point = 0;
            Query query = dataBase.Collection(userDataPath);
            QuerySnapshot querySnapshot = await query.GetSnapshotAsync();

            foreach (DocumentSnapshot document in querySnapshot.Documents)
            {
                if (document.Id.Equals(id))
                {
                    Debug.Log($"Hit user data.(Id:{document.Id})");
                    Dictionary<string, object> docDictionary = document.ToDictionary();
                    if (docDictionary.ContainsKey("point"))
                    {
                        Debug.Log($"Point:{docDictionary["point"]}");
                        point = (int) Convert.ChangeType(docDictionary["point"], typeof(int));
                    }

                    break;
                }
            }
            return point;
        }

まあ、ほとんど変わらないんですけど…、使用例は以下の通り。

example0.cs
            IEnumerator loadingData()
            {
                Task<List<int>> task = firebaseController.ReadPointAsync();
                while (!task.IsCompleted)
                {
                    yield return null;
                }
                point = task.Result;
            }

            StartCoroutine(loadingData());

もしくは、以下のようにContinueWith()を使うこともできます。

example1.cs
firebaseController.ReadPointAsync().ContinueWith(task => { point = task.Result; });

得た情報のCast

stringなら、name = dictionary["path"].ToString();などで問題ありませんが、そのほかの型の場合はダウンキャストになり色々めんどくさいです。SystemのConvert.ChangeType()を用いるのが楽です。

CastExample.cs
// need "using System;"
Dictionary<string, object> dictionary = (Dictionary<string, object>) Convert.ChangeType(
                            docDictionary["path"],
                            typeof(Dictionary<string, object>));

上記はFirestore側でmapとして保存してあるデータを変換する場合。

一応そのほか全種掲載しておきます。

フィールドタイプ string

すでに記述済みではありますが、改めて。

docDictionary["path"].ToString();

フィールドタイプ number

number(数字)intなどに変換できます。floatとかも同じ感じでいけます。

(int) Convert.ChangeType(docDictionary["path"], typeof(int));

フィールドタイプ boolean

boolean(ブール値)boolに変換できます。

(bool) Convert.ChangeType(docDictionary["path"], typeof(bool));

フィールドタイプ map

map(マップ)Dictionary<string, object>に変換できます。Valueをいきなりintなどにしての変換はできないので、まずはDictionary<string, object>に変換してから扱うようにしましょう。

(Dictionary<string, object>) Convert.ChangeType(docDictionary["path"], typeof(Dictionary<string, object>));

フィールドタイプ array

array(配列)は配列とは名ばかりにList<object>に変換できます。もし、本当に配列として扱いたいのであれば、いったんリストで読み込んだ後に配列に変換してあげる必要があります。

(List<object>) Convert.ChangeType(docDictionary["path"], typeof(List<object>));

フィールドタイプ null

値がnullの際はこれに該当します。例えばint[]で定義した配列を何も初期化せずに渡した場合、サーバー側でnullタイプとして保管されます。nullを取りうるarrayやreferenceはnullチェックをすべきでしょう。

フィールドタイプ timestamp

timestamp(タイムスタンプ)Timestampに変換できます。いきなりDateTimeDateTimeOffsetには変換できません。キャストエラーになります。これらにしたい際は、Timestampに変換後に、.ToDateTime()などを用いてあげましょう。

(Timestamp) Convert.ChangeType(docDictionary["path"], typeof(Timestamp));

フィールドタイプ geopoint

geopoint(地理位置情報)GeoPointに変換できます。

(GeoPoint) Convert.ChangeType(docDictionary["path"], typeof(GeoPoint));

フィールドタイプ reference

  • referenceのみ調査中です。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12