儲存資料

事前準備

使用 Realtime Database 前,請先完成下列事項:

  • 註冊 Unity 專案並設定使用 Firebase。

    • 如果 Unity 專案已使用 Firebase,代表已註冊並設定 Firebase。

    • 如果沒有 Unity 專案,可以下載範例應用程式。

  • 將 Firebase Unity SDK (具體來說是 FirebaseDatabase.unitypackage) 新增至 Unity 專案。

請注意,將 Firebase 新增至 Unity 專案時,您必須在 Firebase 控制台和開啟的 Unity 專案中執行工作 (例如從控制台下載 Firebase 設定檔,然後移至 Unity 專案)。

儲存資料

將資料寫入 Firebase Realtime Database 的方法有五種:

方法 常見的使用方式
SetValueAsync() 將資料寫入或取代至定義的路徑,例如 users/<user-id>/<username>。
SetRawJsonValueAsync() 使用原始 JSON 寫入或取代資料,例如 users/<user-id>/<username>。
Push() 新增至資料清單。每次呼叫 Push() 時,Firebase 都會產生專屬金鑰,可用做專屬 ID,例如 user-scores/<user-id>/<unique-score-id>。
UpdateChildrenAsync() 更新已定義路徑的部分索引鍵,而不取代所有資料。
RunTransaction() 更新可能因並行更新而損毀的複雜資料。

取得 DatabaseReference

如要將資料寫入資料庫,您需要 DatabaseReference 的執行個體:

using Firebase;
using Firebase.Database;

public class MyScript: MonoBehaviour {
  void Start() {
    // Get the root reference location of the database.
    DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference;
  }
}

在參照位置寫入、更新或刪除資料

基本寫入作業

如果是基本寫入作業,您可以使用 SetValueAsync() 將資料儲存至指定參照,取代該路徑中的任何現有資料。您可以使用這個方法傳遞與可用 JSON 類型對應的類型,如下所示:

  • string
  • long
  • double
  • bool
  • Dictionary<string, Object>
  • List<Object>

如果使用有類型的 C# 物件,可以透過內建的 JsonUtility.ToJson() 將物件轉換為原始 JSON,並呼叫 SetRawJsonValueAsync()。舉例來說,您可能有一個 User 類別,如下所示:

public class User {
    public string username;
    public string email;

    public User() {
    }

    public User(string username, string email) {
        this.username = username;
        this.email = email;
    }
}

您可以使用 SetRawJsonValueAsync() 新增使用者,如下所示:

private void writeNewUser(string userId, string name, string email) {
    User user = new User(name, email);
    string json = JsonUtility.ToJson(user);

    mDatabaseRef.Child("users").Child(userId).SetRawJsonValueAsync(json);
}

以這種方式使用 SetValueAsync() 或 SetRawJsonValueAsync(),會覆寫指定位置的資料,包括任何子節點。不過,您仍可更新子項,而不必重寫整個物件。如要允許使用者更新個人資料,可以按照下列方式更新使用者名稱:

mDatabaseRef.Child("users").Child(userId).Child("username").SetValueAsync(name);

附加至資料清單

在多使用者應用程式中,使用 Push() 方法將資料附加至清單。每當有新的子項新增至指定的 Firebase 參照時,Push() 方法就會產生專屬金鑰。為清單中的每個新元素使用這些自動生成的鍵,多個用戶端可以同時將子項新增至相同位置,而不會發生寫入衝突。Push() 產生的不重複索引鍵是以時間戳記為基礎,因此清單項目會自動依時間順序排列。

您可以使用 Push() 方法傳回的新資料參照,取得子項自動產生的索引鍵值,或為子項設定資料。對 Push() 參照呼叫 Key,會傳回自動產生的索引鍵值。

更新特定欄位

如要同時寫入節點的特定子項,而不覆寫其他子節點,請使用 UpdateChildrenAsync() 方法。

呼叫 UpdateChildrenAsync() 時,您可以指定鍵的路徑,更新較低層級的子項值。如果資料儲存在多個位置,以便提高擴充性,您可以使用資料展開更新該資料的所有執行個體。舉例來說,遊戲可能會有類似以下的 LeaderboardEntry 類別:

public class LeaderboardEntry {
    public string uid;
    public int score = 0;

    public LeaderboardEntry() {
    }

    public LeaderboardEntry(string uid, int score) {
        this.uid = uid;
        this.score = score;
    }

    public Dictionary<string, Object> ToDictionary() {
        Dictionary<string, Object> result = new Dictionary<string, Object>();
        result["uid"] = uid;
        result["score"] = score;

        return result;
    }
}

如要建立 LeaderboardEntry,並同時更新至近期分數動態消息和使用者自己的分數清單,遊戲會使用類似下列的程式碼:

private void WriteNewScore(string userId, int score) {
    // Create new entry at /user-scores/$userid/$scoreid and at
    // /leaderboard/$scoreid simultaneously
    string key = mDatabase.Child("scores").Push().Key;
    LeaderBoardEntry entry = new LeaderBoardEntry(userId, score);
    Dictionary<string, Object> entryValues = entry.ToDictionary();

    Dictionary<string, Object> childUpdates = new Dictionary<string, Object>();
    childUpdates["/scores/" + key] = entryValues;
    childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

    mDatabase.UpdateChildrenAsync(childUpdates);
}

這個範例使用 Push() 在節點中建立項目,其中包含 /scores/$key 所有使用者的項目,並同時使用 Key 擷取金鑰。接著,您可以使用該鍵在 /user-scores/$userid/$key 的使用者分數中建立第二個項目。

使用這些路徑,您只要呼叫 UpdateChildrenAsync() 一次,就能同時更新 JSON 樹狀結構中的多個位置,例如本範例在兩個位置建立新項目。以這種方式同時進行的更新是不可分割的:要不全部更新成功,要不全部更新失敗。

刪除資料

如要刪除資料,最簡單的方法是在資料位置的參照上呼叫 RemoveValue()。

您也可以指定 null 做為其他寫入作業 (例如 SetValueAsync() 或 UpdateChildrenAsync()) 的值,藉此刪除資料。您可以搭配使用這項技術和 UpdateChildrenAsync(),在單一 API 呼叫中刪除多個子項。

掌握資料提交時間。

如要瞭解資料何時提交至 Firebase Realtime Database 伺服器,可以新增接續作業。SetValueAsync() 和 UpdateChildrenAsync() 都會傳回 Task,讓您知道作業何時完成。如果呼叫因任何原因失敗,Tasks IsFaulted 會是 true,且 Exception 屬性會指出失敗原因。

將資料儲存為交易

處理可能因並行修改而損毀的資料 (例如遞增計數器) 時,可以使用交易作業。您為這項作業提供 Func。這項更新 Func 會將資料的目前狀態做為引數,並傳回您要寫入的新狀態。如果另一個用戶端在您成功寫入新值之前寫入該位置,系統會再次呼叫更新函式,並使用新的目前值,然後重試寫入。

舉例來說,在遊戲中,您可以允許使用者更新排行榜,顯示前五名最高分:

private void AddScoreToLeaders(string email, 
                               long score,
                               DatabaseReference leaderBoardRef) {

    leaderBoardRef.RunTransaction(mutableData => {
      List<object> leaders = mutableData.Value as List<object>

      if (leaders == null) {
        leaders = new List<object>();
      } else if (mutableData.ChildrenCount >= MaxScores) {
        long minScore = long.MaxValue;
        object minVal = null;
        foreach (var child in leaders) {
          if (!(child is Dictionary<string, object>)) continue;
          long childScore = (long)
                      ((Dictionary<string, object>)child)["score"];
          if (childScore < minScore) {
            minScore = childScore;
            minVal = child;
          }
        }
        if (minScore > score) {
          // The new score is lower than the existing 5 scores, abort.
          return TransactionResult.Abort();
        }

        // Remove the lowest score.
        leaders.Remove(minVal);
      }

      // Add the new high score.
      Dictionary<string, object> newScoreMap =
                       new Dictionary<string, object>();
      newScoreMap["score"] = score;
      newScoreMap["email"] = email;
      leaders.Add(newScoreMap);
      mutableData.Value = leaders;
      return TransactionResult.Success(mutableData);
    });
}

使用交易可避免多位使用者同時記錄分數,或用戶端資料過時,導致排行榜不正確。如果交易遭拒,伺服器會將目前的值傳回用戶端,用戶端則會使用更新後的值再次執行交易。這個程序會重複執行,直到交易獲得接受或嘗試次數過多為止。

離線寫入資料

如果用戶端失去網路連線,應用程式仍會正常運作。

每個連結至 Firebase 資料庫的用戶端,都會維護自己的內部版本,寫入資料時,會先寫入這個本機版本。接著,Firebase 用戶端會盡可能將該資料與遠端資料庫伺服器和其他用戶端同步。

因此,在任何資料寫入伺服器之前,所有寫入資料庫的作業都會立即觸發本機事件。這表示無論網路延遲或連線狀況如何,應用程式都能持續回應。

恢復連線後,應用程式會收到適當的事件組合,讓用戶端與目前的伺服器狀態保持同步,不必編寫任何自訂程式碼。

後續步驟