保存数据

准备工作

您必须先创建一个 Firebase 项目,并将 Firebase Unity SDK 软件包添加到您的 Unity 项目中,然后才能使用 Firebase 实时数据库

设置:

前提条件

Android

  • Unity 5.0 或更高版本
  • Android NDK 版本 10d 或更高版本

iOS

  • Unity 5.0 或更高版本
  • Xcode 8.0 或更高版本

如果您还没有 Unity 项目,可以下载一个快速入门示例,试用一下特定的 Firebase 功能。如果使用快速入门示例,请不要忘记从项目设置中获取软件包标识符,下一步中会用到该标识符。

在 Firebase 控制台中设置您的应用

要将 Firebase 添加到您的应用,您需要有一个 Firebase 项目以及适用于您的应用的 Firebase 配置文件。

要创建 Firebase 项目,请执行以下操作:

  1. 如果您还没有 Firebase 项目,请在 Firebase 控制台中创建一个。为此,请点击添加项目。如果您已经有与自己的移动应用相关联的现有 Google 项目,请从项目名称下拉菜单中选择该项目。否则,请输入项目名称以创建新项目。
  2. 可选:修改您的项目 ID。系统会自动为您的项目指定唯一 ID,此 ID 将用在公开显示的 Firebase 功能(例如数据库网址)以及您的 Firebase 托管子域名中。如果您想使用特定子域名,现在就可以更改此 ID。
  3. 执行其余的设置步骤,然后点击创建项目(如果您使用现有的项目,则点击添加 Firebase),开始为您的项目配置资源。这通常需要几分钟的时间。此过程完成后,您就会看到项目概览。

Android

  1. 点击将 Firebase 添加到您的 Android 应用,然后按设置步骤操作。如果您是导入现有 Google 项目,系统可能会自动执行这些操作,您只需下载配置文件即可。
  2. 看到提示时,输入应用的软件包名称。请务必输入应用在使用的软件包名称;只有在将应用添加到 Firebase 项目时您才能进行此设置。
  3. 在此过程中,您要下载一个 google-services.json 文件。您可以随时重新下载此文件
  4. 添加初始化代码后,运行您的应用以便向 Firebase 控制台发送验证信息,证明您已成功安装 Firebase。

iOS

  1. 点击将 Firebase 添加到您的 iOS 应用,然后按设置步骤操作。如果您是导入现有 Google 项目,系统可能会自动执行这些操作,您只需下载配置文件即可。
  2. 看到提示时,输入应用的软件包 ID。请务必输入应用在使用的软件包 ID;只有在将应用添加到 Firebase 项目时您才能进行此设置。
  3. 在此过程中,您要下载一个 GoogleService-Info.plist 文件。您可以随时重新下载此文件
  4. 添加初始化代码后,运行您的应用以便向 Firebase 控制台发送验证信息,证明您已成功安装 Firebase。
  5. 将从 Firebase 控制台下载的 GoogleService-Info.plist 拖动到 Unity 项目中的任意文件夹内。

将 Firebase Unity SDK 添加到您的应用

  1. 下载 Firebase Unity SDK
  2. 依次选择 Assets > Import Package > Custom Package 菜单项。
  3. 从之前下载的 Firebase Unity SDK 导入 FirebaseDatabase.unitypackage 软件包。
  4. 看到 Import Unity Package 窗口时,点击 Import 按钮。

初始化 SDK

Android 版 Firebase Unity SDK 需要使用 Google Play 服务。您必须先将 Google Play 服务更新为最新版本,然后才能使用该 SDK。您应在应用开头处添加以下代码,以便检查 Google Play 服务版本是否符合 Firebase Unity SDK 的要求,并视情况将其更新为相应版本,然后再调用 SDK 中的任何其他方法。

Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
  var dependencyStatus = task.Result;
  if (dependencyStatus == Firebase.DependencyStatus.Available) {
    // Create and hold a reference to your FirebaseApp, i.e.
    //   app = Firebase.FirebaseApp.DefaultInstance;
    // where app is a Firebase.FirebaseApp property of your application class.

    // Set a flag here indicating that Firebase is ready to use by your
    // application.
  } else {
    UnityEngine.Debug.LogError(System.String.Format(
      "Could not resolve all Firebase dependencies: {0}", dependencyStatus));
    // Firebase Unity SDK is not safe to use here.
  }
});

构建您的应用

Android

  1. 依次选择 File > Build Settings 菜单选项。
  2. 选择 Platform 列表中的 Android
  3. 点击 Switch Platform,选择 Android 作为目标平台。
  4. 等待 Unity 状态栏右下角的旋转进度条(正在构建)图标停止旋转。
  5. 点击 Build and Run

iOS

  1. 依次选择 File > Build Settings 菜单选项。
  2. 选择 Platform 列表中的 iOS
  3. 点击 Switch Platform,选择 iOS 作为目标平台。
  4. 等待 Unity 状态栏右下角的旋转进度条(正在构建)图标停止旋转。
  5. 点击 Build and Run

保存数据

有五种方法可以将数据写入 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

要将数据写入到数据库中,您需要一个 DatabaseReference 实例:

using Firebase;
using Firebase.Database;
using Firebase.Unity.Editor;

public class MyScript: MonoBehaviour {
  void Start() {
    // Set up the Editor before calling into the realtime database.
    FirebaseApp.DefaultInstance.SetEditorDatabaseUrl("https://YOUR-FIREBASE-APP.firebaseio.com/");

    // 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()。例如,您可能有一个如下所示的用户类:

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&ltstring, Object&gt ToDictionary() {
        Dictionary&ltstring, Object&gt result = new Dictionary&ltstring, Object&gt();
        result["uid"] = uid;
        result["score"] = score;

        return result;
    }
}

要创建 LeaderboardEntry 并同时将其更新到最近的分数 Feed 和用户自己的分数列表中,游戏会使用如下代码:

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&ltstring, Object&gt entryValues = entry.ToDictionary();

    Dictionary&ltstring, Object&gt childUpdates = new Dictionary&ltstring, Object&gt();
    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,让您了解操作何时完成。如果调用因为某种原因没有成功,Task 的 IsFaulted 将为 true,并使用 Exception 属性指出失败的原因。

将数据另存为事务

处理可能因并发修改而损坏的数据(例如,增量计数器)时,您可以使用事务操作。您需要为此操作提供一个 (Func) 函数。此更新函数 (Func) 会将数据的当前状态视为参数,并返回您要写入的新目标状态。如果另一个客户端在您成功写入新值前向该位置写入数据,则系统会使用新的当前值再次调用更新函数,然后重新尝试执行写入操作。

例如,在一个游戏中,您可以允许用户更新排行榜中的前五个最高得分:

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

    leaderBoardRef.RunTransaction(mutableData =&gt {
      List&ltobject&gt leaders = mutableData.Value as List&ltobject>

      if (leaders == null) {
        leaders = new List&ltobject&gt();
      } else if (mutableData.ChildrenCount &gt= MaxScores) {
        long minScore = long.MaxValue;
        object minVal = null;
        foreach (var child in leaders) {
          if (!(child is Dictionary&ltstring, object&gt)) continue;
          long childScore = (long)
                      ((Dictionary&ltstring, object&gt)child)["score"];
          if (childScore &lt minScore) {
            minScore = childScore;
            minVal = child;
          }
        }
        if (minScore &gt 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&ltstring, object&gt newScoreMap =
                       new Dictionary&ltstring, object&gt();
      newScoreMap["score"] = score;
      newScoreMap["email"] = email;
      leaders.Add(newScoreMap);
      mutableData.Value = leaders;
      return TransactionResult.Success(mutableData);
    });
}

如果多个用户同时记录得分或客户端存在过时数据,使用事务可防止排行榜出错。如果事务遭拒绝,则服务器会将当前值返回到客户端,然后客户端会使用更新后的值再次运行事务。此过程将反复进行,直到事务被接受或尝试次数达到限制为止。

离线写入数据

如果客户端的网络连接中断,您的应用将继续正常运行。

对于所有有效数据,连接到 Firebase 数据库的每个客户端均维护着各自的内部版本。数据在写入时,首先会写入这类本地版本。然后,Firebase 客户端会尽可能将这些数据与远程数据库服务器以及其他客户端同步。

因此,对数据库执行的所有写入操作会立即触发本地事件,然后数据才会写入服务器。这意味着应用仍将保持随时响应的状态,无论网络延迟或连接状况如何。

连接重新建立之后,您的应用将收到一系列相应的事件,以便客户端与当前服务器状态进行同步,而不必编写任何自定义代码。

后续步骤

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面