Super fast Javascript interaction on WebGL

This doc page suggests that a SendMessage call can be made at any time after index.html finishes interpreting JS (in a WebGL build):

Yet, if I wait for a WebGL Release to finish building, copy all assets to an http server, load its index.html, and then open Chrome Dev Tools console and call SendMessage – without calling it on a specific object – then I get an error that it’s undefined. There seems to be an unspoken requirement, perhaps related to the Module definition.

You’re right, that the runtime is not ready if you call SendMessage right away, but Emscripten gives you some options here:
https://kripken.github.io/emscripten-site/docs/getting_started/FAQ.html#how-can-i-tell-when-the-page-is-fully-loaded-and-it-is-safe-to-call-compiled-functions

Look at my example project:
https://github.com/atteneder/UnityJavascriptInteraction

right after the unity glue code (look at the template’s index.html), I include test.js with basically this piece of code:

Module.onRuntimeInitialized = function() {
console.log('Unity is ready now');
}

hth,
atti

2 Likes

Your project solved my problem, thanks!

Some tips for others who use it:

  • Let’s assume you want to use just one of atti’s methods, say, the one for passing json as a string from the html container to the game object. You’d call “c_vxjson” from the html container (see test.js) and this will be received by TargetVxJson in the game object (see Receiver.cs)

  • You can rename test.js (and update the filename in index.html) but inside of it do not remove any of the entries in Module.onRuntimeInitialized. I replaced everything else with a fn named “onGameReady” (containing the call to c_vxjson), and updated progress.js to call this fn instead of run_tests.

  • Renaming “c_vv3json” also gave me a problem once, so I reverted that.

  • Do not alter anything in Receiver.cs either, except the content of the fn you want to use. In the case of TargetVxJson, you may also want to rename the struct it uses for json deserialization (I made it a class with subclasses as well).

  • You’ll want to use Receiver.cs as a script for the game object you want to control from the html container. I haven’t gotten this far yet but I expect it to work. I also don’t know whether it’s possible to dispatch commands to different game objects that each have their own script; I suspect Receiver.cs would need to be adapted to control other game objects even though it’s attached to just one.

Hi, I tried your code hoping to solve javascript - webgl unity communication, but, after building from your repository code, I get this error when loading index.html:

ReferenceError: Can’t find variable: Module

I’m on Unity 5.6 beta, and Safari on Mac (same result with Chrome, tough).

Do you have an idea of what might cause the error?

Thanks!

Yes, when I tested 5.6 I noticed they renamed “Module”. Can’t remember exactly, but something like “GameInstance”.
Have a look into UnityLoader.js to see exactly or your browser’s dev console.

hth

Pardon my lack of knowledge … what should I look for, exactly? What was “Module” before?

I looked a test I did some weeks ago. Just add this at the top of tests.js:

if(!window.Module) {
    window.Module = gameInstance.Module;
}

Module is now inside another object.

it now says: “ReferenceError: Can’t find variable: gameInstance”

This is the index.html generated by your project:

<!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 | JavascriptInteraction</title>
    <script src="progress.js"></script>
    <style>
    /* a style sheet needs to be present for cursor hiding and custom cursors to work. */
    </style>
  </head>
  <body>
    <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" height="200px" width="320px"></canvas>
    %UNITY_WEBGL_LOADER_GLUE%
    <script src="tests.js"></script>
  </body>
</html>

Should’t be somewhere a ?

Edit: wait a minute, %UNITY_WEBGL_LOADER_GLUE% shouldn’t be in index.html in the WebGL build … so something went wrong when I built the WebGL

p.s. I have 5.6 beta9

I finally found time and made a branch of my project, which works with Unity 5.6 (beta 10 here).

Note: this doesn’t work with WebAssembly (yet).
hth

Edit: Just tested Unity 5.6.0b11 on Chrome Canary 59 and Firefox 52. Seems to work…Yay!

1 Like

After a quick look: I think not.
My demo is fast because it’s an alternative to SendMessage. The WebSockets plugin doesn’t use SendMessage in first place.
For example receiving (huge chunks of) data: Seems like they cache responses in WebSocket.jslib (socket.messages.push(array)) and poll them from C# via SocketRecv, which directly writes into the heap.
Now you could theoretically invoke a native cwrapped C# function when a message is received (via socket.socket.onmessage), and pass on the content as parameter. But this is probably dangerous, since you cannot know in what state Unity is within its run-loop. So you’d have to synchronize things in C# again.

My gut tells me it probably cannot get much faster, but feel free to prove me wrong.

1 Like

Thanks atti, I have a multiplayer webgl game and some users have experiencing a major delay with what they should be seeing, not sure where the problem is stemming from exactly.

Does anyone know if there a unity version/patch which included the send message change? Or do i have to still add this to my template?

Module.onRuntimeInitialized = function() {
        SendMessage = function(gameObject, func, param) {
          if (param === undefined) {
            if (typeof this.SendMessage_vss != 'function')
              this.SendMessage_vss = Module.cwrap('SendMessage', 'void', ['string', 'string']);
            this.SendMessage_vss(gameObject, func);
          } else if (typeof param === "string") {
            if (typeof this.SendMessage_vsss != 'function')
              this.SendMessage_vsss = Module.cwrap('SendMessageString', 'void', ['string', 'string', 'string']);
            this.SendMessage_vsss(gameObject, func, param);
          } else if (typeof param === "number") {
            if (typeof this.SendMessage_vssn != 'function')
              this.SendMessage_vssn = Module.cwrap('SendMessageFloat', 'void', ['string', 'string', 'number']);
            this.SendMessage_vssn(gameObject, func, param);
          } else
              throw "" + param + " is does not have a type which is supported by SendMessage.";
        }
      };

Hi folks,

I am searching a way to return value from sendMessage. Do anyone able to do this? cause it will allow me to work in asynchronous pattern otherwise i have to implement another function in order to get value.??

Hi Folks,
I am facing issue with UnityJavaScriptInteraction. Please help me out if you any idea.
exception thrown: TypeError: Module.setValue is not a function
got error from tests.js file. using unity 2019.4.1 and running @tteneder GitHub - atteneder/UnityJavascriptInteraction at unity56

or is there any other way to call c# method from javascript file?

I was looking at this too recently. I got this working by adding some additional emscrption arguments:

https://emscripten.org/docs/getting_started/FAQ.html#why-do-i-get-typeerror-module-something-is-not-a-function

In EditorTools.cs,
Replace line.

PlayerSettings.SetPropertyString(“emscriptenArgs”,“–profiling -s ASSERTIONS=2”~~~

To

PlayerSettings.WebGL.emscriptenArgs = “–profiling -s ASSERTIONS=2 -s EXTRA_EXPORTED_RUNTIME_METHODS=[‘addRunDependency’,‘removeRunDependency’,‘ccall’,‘cwrap’,‘stackTrace’,‘setValue’,‘FS_createPath’,‘FS_createDataFile’]”;

Hi all,

I finally invested the time to update this little demo to 2021.2.

Observations after the update:

  • Original SendMessage is now actually faster than the wrapped, “optimized” one. Maybe a sign of Unity optimizing it.
  • JSON wrapped native callbacks are now actually slower than SendMessage versions. Maybe the JS runtime detects and optimizes the repeated variable init, or maybe SendMessage really is that good now.
  • The rest is still pretty darn fast

hth

2 Likes

All these process seem like it could be automated in compile time. I mean we could have some way we can add attribute to a static function that will mark that function for unity compiler to pick up and expose it in the unity instance

In fact, given that SendMessage need to give the object name and make a lookup and also need to keep that object to exist in the scene just for listen to message. static function is more convenient and should be the default mode of js interop

class MyClass
{
    [DllExportToJS("MyClass","MyAdd")] // objectName,functionName
    static int MyAdd(int a,int b)
    {
        return a + b;
    }
}
var result = unityInstance.Module.MyClass.MyAdd(1,2); // 3

Would you pleased to make these kind of functionality in to unity? Maybe make it be a package in the meantime and make it official integration of framework later

4 Likes

@Thaina totally agree that would be extremely valuable and I think totally doable on the Unity side. Would be nice if some Typescript .d.ts description files were generated as well.

1 Like

Hi, i have an error and i dont know why.
im using unity 2019.4.19 with your project, but when i make the build and put it on servez this is what it say:

server started on ::8080 for path: /Users/*****/Downloads/Isobar/Exporter-web-unity/Exporter-web
available on:
   http://localhost:8080
   http://127.*.*.1:8080
   http://192.168.***.**:8080
GET /
GET /Build/%7B%7B%7B%20LOADER_FILENAME%20%7D%7D%7D
GET /tests.js
ERROR: GET /Build/%7B%7B%7B%20LOADER_FILENAME%20%7D%7D%7D [404: does not exist]

how can i fix it?