儲存資料

事前準備

如要使用 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 用戶端會以「盡力而為」的方式,將這些資料與遠端資料庫伺服器和其他用戶端同步。

因此,所有寫入資料庫的作業都會立即觸發本機事件,然後才將任何資料寫入伺服器。也就是說,無論網路延遲或連線狀況為何,應用程式都能持續回應。

重新建立連線後,應用程式會收到適當的事件組合,讓用戶端與目前的伺服器狀態保持同步,而無需撰寫任何自訂程式碼。

後續步驟