使用 Firebase JS SDK 将 Unity WebGL 表单发布到 Cloud Firestore

Unity WebGL form posts to Cloud Firestore with Firebase JS SDK

我的目标是在 Unity 中构建一个具有特定表单的 WebGL 游戏,并在提交时将表单将数据发布到其中一个 Firebase 存储解决方案中。看完this article, it's clear to me I need to use Cloud Firestore instead of Realtime Database. Good news is that as of of March 2020, one of the team members wrote,

we released Firebase Unity SDK 6.12.0 which includes an alpha release of Firestore.

事实是,Firebase 的 Unity SDK 不适用于 WebGL 构建,对于经历过该思考过程的人来说,可以使用 (spam alert). From looking at the release notes,可以看到 Firebase JS SDK 支持 Firestore,因此它具有所有具备快速解决的条件。

所以,我转到 Firebase 控制台,创建了一个项目,一个使用 Firebase JS SDK 的 Web 应用程序,这个过程输出了以下代码

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.17.1/firebase-app.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/7.17.1/firebase-analytics.js"></script>

<script>
  // Your web app's Firebase configuration
  var firebaseConfig = {
    apiKey: "",
    authDomain: "",
    databaseURL: "",
    projectId: "",
    storageBucket: "",
    messagingSenderId: "",
    appId: "",
    measurementId: ""
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
  firebase.analytics();
</script>

一条消息说

Copy and paste these scripts into the bottom of your tag, but before you use any Firebase services

除此之外,这是 how to Call JavaScript functions from Unity scripts and this is a Cloud Firestore JS Sample App

根据这些信息,如何创建表单?

假设您想要一个接收输入的表单

  • 字符串
  • 数量
  • 来自用户的文本

在您的 Firestore 控制台中,创建一个集合并为其命名(例如 formDataTree),提供一个自动 ID 并添加字段

  • strVal
  • 整数值
  • webVal

然后,我会将这些脚本放在您 WebGL template 中 head 标签的底部。因此,在 Assets 中创建一个名为 WebGLTemplates 的文件夹和一个名为 New Template(或任何您喜欢的名称)的文件夹,然后添加一个 index.html 那里

根据文档,这个index.html应该类似于

<!DOCTYPE html>
<html lang="en-us">

  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Unity WebGL Player | %UNITY_WEB_NAME%</title>
    <script src="%UNITY_WEBGL_LOADER_URL%"></script>
    <script>
    var unityInstance = UnityLoader.instantiate("unityContainer", "%UNITY_WEBGL_BUILD_URL%");
    </script>
  </head>
  
  <body>
    <div id="unityContainer" style="width: %UNITY_WIDTH%px; height: %UNITY_HEIGHT%px; margin: auto"></div>
  </body>
  
</html>

所以,根据这些新信息,它会是这样的

<!DOCTYPE html>
<html lang="en-us">

  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Unity WebGL Player | %UNITY_WEB_NAME%</title>
    <script src="%UNITY_WEBGL_LOADER_URL%"></script>
    <script>
        var unityInstance = UnityLoader.instantiate("unityContainer", "%UNITY_WEBGL_BUILD_URL%");
    </script>
    <!-- The core Firebase JS SDK is always required and must be listed first -->
    <script src="https://www.gstatic.com/firebasejs/7.17.1/firebase-app.js"></script>

    <!-- TODO: Add SDKs for Firebase products that you want to use
         https://firebase.google.com/docs/web/setup#available-libraries -->
    <script src="https://www.gstatic.com/firebasejs/7.17.1/firebase-analytics.js"></script>

    <script>
      // Your web app's Firebase configuration
      var firebaseConfig = {
        apiKey: "",
        authDomain: "",
        databaseURL: "",
        projectId: "",
        storageBucket: "",
        messagingSenderId: "",
        appId: "",
        measurementId: ""
      };
      // Initialize Firebase
      firebase.initializeApp(firebaseConfig);
      firebase.analytics();
    </script>
  </head>
  
  <body>
    <div id="unityContainer" style="width: %UNITY_WIDTH%px; height: %UNITY_HEIGHT%px; margin: auto"></div>
  </body>
  
</html>

然后,在播放器设置下,select 该模板。

然后在模板正文中,有一个隐藏可见性的表单。包括游戏中的内容以及您想要在浏览器中填写的任何输入:

<form id="webForm" style="visibility:hidden;">
    <input type="hidden" id="stringInput" name="stringInput">
    <input type="hidden" id="intInput" name="intInput">
    <label for="webInput">web input</label><input type="text" id="webInput" name="webInput">
    <button type="submit">Submit</button>
</form>

然后在 Firebase 脚本和表单下方,为页面上的表单添加一个提交侦听器,它将提交到 Firestore(基于 this answer):

myForm.addEventListener('submit', function(evt) {
  evt.preventDefault(); //Prevent the default form submit action

  var strVal = myForm.stringInput.value;
  var intVal = myForm.intInput.value;
  var webVal = intInput.webInput.value;

  var formData = {
    "strVal" : strVal,
    "intVal" : intVal,
    "webVal" : webVal
  };

  firebase.database().ref('/formDataTree').push( formData ); // Adds the new form data to the list under formDataTree node
});

总而言之,index.html应该是这样的

<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Change Mapping | %UNITY_WEB_NAME%</title>
    <script src="%UNITY_WEBGL_LOADER_URL%"></script>
    <script>
        var unityInstance = UnityLoader.instantiate("unityContainer", "%UNITY_WEBGL_BUILD_URL%");
    </script>
    
    <!-- The core Firebase JS SDK is always required and must be listed first -->
    <script src="https://www.gstatic.com/firebasejs/7.17.1/firebase-app.js"></script>

    <!-- TODO: Add SDKs for Firebase products that you want to use
         https://firebase.google.com/docs/web/setup#available-libraries -->
    <script src="https://www.gstatic.com/firebasejs/7.17.1/firebase-analytics.js"></script>

    <script>
      // Your web app's Firebase configuration
      var firebaseConfig = {
        apiKey: "",
        authDomain: "",
        databaseURL: "",
        projectId: "",
        storageBucket: "",
        messagingSenderId: "",
        appId: "",
        measurementId: ""
      };
      // Initialize Firebase
      firebase.initializeApp(firebaseConfig);
      firebase.analytics();
    </script>
  </head>
  <body>
    <div style="height:20px; width: %UNITY_WIDTH%px; background: green;" onclick="unityInstance.SetFullscreen(1)"><b>Click here to make it full screen.</b></div>
    <div id="unityContainer" style="width: %UNITY_WIDTH%px; height: %UNITY_HEIGHT%px; margin: auto"></div>
    <form id="webForm" style="visibility:hidden;">
        <input type="hidden" id="stringInput" name="stringInput">
        <input type="hidden" id="intInput" name="intInput">
        <label for="webInput">web input</label><input type="text" id="webInput" name="webInput">
        <button type="submit">Submit</button>
    </form>
    <script>
        var myForm = document.getElementById("webForm");
        myForm.addEventListener('submit', function(evt) {
          evt.preventDefault(); //Prevent the default form submit action

          var strVal = myForm.stringInput.value;
          var intVal = myForm.intInput.value;
          var webVal = intInput.webInput.value;

          var formData = {
            "strVal" : strVal,
            "intVal" : intVal,
            "webVal" : webVal
          };

          firebase.database().ref('/formDataTree').push( formData ); // Adds the new form data to the list under formDataTree node
        });
    </script>
  </body>
</html>

在模板中使用 apiKey、authDomain 等意味着它会在检查页面或查看页面源代码时显示为。然而,作为 ,可以分享这些信息。

然后,在您的 Assets 文件夹中,创建一个 Plugins 文件夹并向其中添加一个 .jslib 文件,例如名为 [=97= 的实例],具有显示表单的功能,并将游戏数据放入表单的隐藏输入中。

mergeInto(LibraryManager.library, {
  ShowWebForm: function (importantString, importantInt) {
    var myForm = document.getElementById("webForm");

    myForm.stringInput.value = Pointer_stringify(importantString);
    myForm.intInput.value = importantInt;

    myForm.style.visibility="visible"
   
  },
});

完成到此为止的步骤后,如果您进入控制台 net::ERR_BLOCKED_BY_CLIENT,只需停用该网站的 AdBlocker that's why you get that error

最后,在 Unity 中,声明该函数并在适当的时候调用它。因此,考虑到您有一个新创建的场景(因此它只有主相机和平行光),您可以在新场景中调用的一些代码中调用该方法。这是一个静态方法,因此只要您能找到参数所需的数据,就可以从任何地方调用它。

[DllImport("__Internal")]
private static extern void ShowWebForm(string importantString, int importantInt);

public void Start()
{
    // Suppose we want to send the version of unity the app is running on 
    // and the unix timestamp at start
    string unityVersion = Application.unityVersion;

    System.DateTime epochStart = new System.DateTime(1970, 1, 1, 0, 0, 0,  
            System.DateTimeKind.Utc);
    int cur_time = (int)(System.DateTime.UtcNow - epochStart).TotalSeconds;

    ShowWebForm(unityVersion, cur_time);
}

或者,如果您不想让用户在浏览器中填写内容,您可以让表单不可见,而不是将其设置为可见,而是发送一个提交事件:

mergeInto(LibraryManager.library, {
  ShowWebForm: function (importantString, importantInt) {
    var myForm = document.getElementById("webForm");

    myForm.stringInput.value = Pointer_stringify(importantString);
    myForm.intInput.value = importantInt;
    myForm.webInput.value = "some other value from the game could go here";

    myForm.dispatchEvent(new Event('submit'));
  },
});

目前无法测试,因此请注意拼写错误或其他语法错误。