保存數據

在你開始之前

在使用實時數據庫之前,您需要:

  • 註冊您的 Unity 項目並將其配置為使用 Firebase。

    • 如果您的 Unity 項目已經使用 Firebase,那麼它已經針對 Firebase 進行了註冊和配置。

    • 如果您沒有 Unity 項目,可以下載示例應用程序

  • Firebase Unity SDK (特別是FirebaseDatabase.unitypackage )添加到您的 Unity 項目。

請注意,將 Firebase 添加到您的 Unity 項目涉及Firebase 控制台和您打開的 Unity 項目中的任務(例如,您從控制台下載 Firebase 配置文件,然後將它們移動到您的 Unity 項目中)。

保存數據

有五種方法可以將數據寫入 Firebase 實時數據庫:

方法常見用途
SetValueAsync()將數據寫入或替換到定義的路徑,例如users/<user-id>/<username>
SetRawJsonValueAsync()使用原始 Json 編寫或替換數據,例如users/<user-id>/<username>
Push()添加到數據列表。每次調用Push()時,Firebase 都會生成一個唯一鍵,該鍵也可用作唯一標識符,例如user-scores/<user-id>/<unique-score-id>
UpdateChildrenAsync()在不替換所有數據的情況下更新定義路徑的一些鍵。
RunTransaction()更新可能被並發更新損壞的複雜數據。

獲取數據庫引用

要將數據寫入數據庫,您需要一個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 實時數據庫服務器,您可以添加一個延續。 SetValueAsync()UpdateChildrenAsync()都會返回一個Task ,讓您知道操作何時完成。如果調用因任何原因不成功,則 Tasks IsFaulted將為真, 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 客戶端會在“盡力”的基礎上將該數據與遠程數據庫服務器和其他客戶端同步。

因此,在將任何數據寫入服務器之前,所有對數據庫的寫入都會立即觸發本地事件。這意味著無論網絡延遲或連接如何,您的應用程序都會保持響應。

重新建立連接後,您的應用程序會收到一組適當的事件,以便客戶端與當前服務器狀態同步,而無需編寫任何自定義代碼。

下一步

,

在你開始之前

在使用實時數據庫之前,您需要:

  • 註冊您的 Unity 項目並將其配置為使用 Firebase。

    • 如果您的 Unity 項目已經使用 Firebase,那麼它已經針對 Firebase 進行了註冊和配置。

    • 如果您沒有 Unity 項目,可以下載示例應用程序

  • Firebase Unity SDK (特別是FirebaseDatabase.unitypackage )添加到您的 Unity 項目。

請注意,將 Firebase 添加到您的 Unity 項目涉及Firebase 控制台和您打開的 Unity 項目中的任務(例如,您從控制台下載 Firebase 配置文件,然後將它們移動到您的 Unity 項目中)。

保存數據

有五種方法可以將數據寫入 Firebase 實時數據庫:

方法常見用途
SetValueAsync()將數據寫入或替換到定義的路徑,例如users/<user-id>/<username>
SetRawJsonValueAsync()使用原始 Json 編寫或替換數據,例如users/<user-id>/<username>
Push()添加到數據列表。每次調用Push()時,Firebase 都會生成一個唯一鍵,該鍵也可用作唯一標識符,例如user-scores/<user-id>/<unique-score-id>
UpdateChildrenAsync()在不替換所有數據的情況下更新定義路徑的一些鍵。
RunTransaction()更新可能被並發更新損壞的複雜數據。

獲取數據庫引用

要將數據寫入數據庫,您需要一個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 實時數據庫服務器,您可以添加一個延續。 SetValueAsync()UpdateChildrenAsync()都會返回一個Task ,讓您知道操作何時完成。如果調用因任何原因不成功,則 Tasks IsFaulted將為真, 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 客戶端會在“盡力”的基礎上將該數據與遠程數據庫服務器和其他客戶端同步。

因此,在將任何數據寫入服務器之前,所有對數據庫的寫入都會立即觸發本地事件。這意味著無論網絡延遲或連接如何,您的應用程序都會保持響應。

重新建立連接後,您的應用程序會收到一組適當的事件,以便客戶端與當前服務器狀態同步,而無需編寫任何自定義代碼。

下一步