WebGL build callback on tab close or browser quit.

Hello! We’re porting a game from android/iOS to WebGL and the problem is that we save player data when appliaction loses focus or pauses via OnAppliacitonPause/Focus. This function gets called in WebGL builds when you switch to another tab or move focus to another part of the page. However if user just closes the tab, there is no callback for us to save his data.

The question is - can the callback on tab close or browser quit can be implemented for WebGL? Im trying to make JS function “onbeforunload” work but no success so far. Any other way to achieve this?

123259-possible.gif
Note: The yellow console warnings are coming from the Unity scene.

It may not work with the native OnApplicationQuit, but it is possible. You can do this using a custom WebGL Template.
Before you start reading, it might be a good idea to glance over the Unity Documentation on Interacting with Browser Scripting.

Setting up your WebGL Template

First, you’ll want to create a WebGL template to use for your project. Read through that guide to learn how to create one. Once that’s done, you’ll have something in a <script> tag that looks like this:

gameInstance = UnityLoader.instantiate("gameContainer", "%UNITY_WEBGL_BUILD_URL%", {});

You’ll use this object to communicate with your Unity Scene, as it can use the well-known SendMessage function. For now, paste this below that UnityLoader.instantiate line in your template:

// Callback for the Unity OnClose event
window.onbeforeunload = function(e) {
    
    // These are the messages you're seeing in the gif
    console.log("Calling OnClose from Browser!");
    gameInstance.SendMessage("OnCloseObject", "OnClose");

    // This never shows up correctly for me, but it does prompt
    // the player to close their window with a dialogue box
    //
    var dialogText = "You game has been saved!  Would you like to continue unloading the page?";
    e.returnValue = dialogText;
    return dialogText;
};

As of right now, your Unity’s browser will try to call functions in Unity that don’t exist yet, so let’s set those up now.

Setting up your Scene

In my example, I made a simple object named OnCloseObject with a single script given below:

using UnityEngine;
using UnityEngine.UI;

public class OnCloseListener : MonoBehaviour
{
    public Image backgroundImage;

    /// <summary>
    /// Called when the Browser is closed.
    /// </summary>
    public void OnClose()
    {
        // Uncomment this if you set up Interop
        //BrowserJS.Warn("This warning was called from Unity!");

        // Randomize the background image color
        this.backgroundImage.color = new Color(Random.value, Random.value, Random.value);
    }
}

If you don’t want to set up the Interop stuff for Unity to speak to the browser, skip the next part.

(Optional) Setting up Interop

Create a folder called Plugins in your project and add the following as plugins.jslib:

mergeInto(LibraryManager.library, {

  Alert: function (str) {
    window.alert(Pointer_stringify(str));
  },

  Console: function (str) {
    console.log(Pointer_stringify(str));
  },

  Warn: function (str) {
    console.warn(Pointer_stringify(str));
  },

  Error: function (str) {
    console.error(Pointer_stringify(str));
  }
});

Then, create a new script and paste this:

using System.Runtime.InteropServices;

public class BrowserJS
{    
    [DllImport("__Internal")]
    public static extern void Alert(string str);

    [DllImport("__Internal")]
    public static extern void Log(string str);

    [DllImport("__Internal")]
    public static extern void Warn(string str);

    [DllImport("__Internal")]
    public static extern void Error(string str);
}

Once this is in place and your WebGL build is using your modified Template, you should be ready to go.

There might be some race conditions if your save process is a bit lengthy and the user closes out too quickly? I am sure there are some nuance-y things I am missing here, but this should be enough to get you started.