Hello, I need to call a c# function from jslib file (JS code). I used unityInstance.SendMessage for this, but no runtime I recieve an error: Uncaught ReferenceError: unityInstance is not defined. This error is strange because I used the same code in another project and that's where it works. In the current project, in the same Unity version, an error is received. I tried very hard and could not solve the problem. Here is the c# code: Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Runtime.InteropServices; public class GetJSData : MonoBehaviour { [DllImport("__Internal")] public static extern void callGame(int gameNum); [DllImport("__Internal")] public static extern void checkGameLocalStorage(); [DllImport("__Internal")] public static extern void resetVaribiles(); // Start is called before the first frame update void Start() { checkGameLocalStorage(); } public void fromJS(int val){ Debug.Log("fromJS!!!!!!"); GameManager.ifGameOut = false; if(val==1){ GameProcess.ifCorrect_fromGame=true; } else { GameProcess.ifCorrect_fromGame=false; } Debug.Log(GameProcess.ifCorrect_fromGame); } } And the JS code: Code (JavaScript): mergeInto(LibraryManager.library, { resetVaribiles: function(){ localStorage.removeItem("hagim81Game_Words"); localStorage.removeItem("hagim81ToGame"); localStorage.removeItem("hagim81Return"); localStorage.removeItem("wordSearchSumWords"); //localStorage.removeItem(""); localStorage.setItem('refrashIframes',1); }, callGame: function (gameNum) { localStorage.setItem("hagim81ToGame",gameNum); }, checkGameLocalStorage: function(){ setInterval(function(){ checkGameLocalStorage_1(); },1000) function checkGameLocalStorage_1(){ if(localStorage.getItem("hagim81Game_Words")!= null && localStorage.getItem("hagim81Game_Words")>0){ console.log("checkGameLocalStorage") localStorage.removeItem("hagim81Game_Words"); localStorage.removeItem("hagim81ToGame"); localStorage.setItem("hagim81Return",1); unityInstance.SendMessage('GetJSData' , 'fromJS' , 1); } } }, }) The error is obtained from the code line: unityInstance.SendMessage('GetJSData' , 'fromJS' , 1); Note: The C# script is on GameObject called "GetJSData". The JSLIB file is in PLUGINS folder in assets.
I have .SendMessage working within a .jslib file in 2020.2 - I explicitly assign the reference to window.unityInstance in the WebGL template: Code (JavaScript): createUnityInstance(canvas, config, (progress) => { progressBarFull.style.width = 100 * progress + "%"; }).then((unityInstance) => { window.unityInstance = unityInstance; // <-- this ..and in my .jslib: Code (CSharp): window.unityInstance.SendMessage(...
Thank you. But I am using the 2019.3 version. And like I said in another project it works for me (in the same Unity version). I will still try what you have suggested.
Honestly, I did not understand where you put the first code ... if you can please explain to me. Thanks!
In the WebGL template's index.html, i.e.: [unity installation path]\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\WebGLTemplates\Default\index.html If the template html doesn't contain "unityInstance" let me know, I'll install 2019 quickly and take a closer look.
Maybe the code in 2019 is different. This is what the HTML looks like: Code (JavaScript): <!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> <link rel="shortcut icon" href="TemplateData/favicon.ico"> <link rel="stylesheet" href="TemplateData/style.css"> <script src="TemplateData/UnityProgress.js"></script> <script src="%UNITY_WEBGL_LOADER_URL%"></script> <script> var unityInstance = UnityLoader.instantiate("unityContainer", "%UNITY_WEBGL_BUILD_URL%", {onProgress: UnityProgress}); </script> </head> <body> <div class="webgl-content"> <div id="unityContainer" style="width: %UNITY_WIDTH%px; height: %UNITY_HEIGHT%px"></div> <div class="footer"> <div class="webgl-logo"></div> <div class="fullscreen" onclick="unityInstance.SetFullscreen(1)"></div> <div class="title">%UNITY_WEB_NAME%</div> </div> </div> </body> </html>
So looking at the 2019 WebGL template it has this: Code (JavaScript): var unityInstance = UnityLoader.instantiate("unityContainer", "%UNITY_WEBGL_BUILD_URL%", {onProgress: UnityProgress}); Try changing that to: Code (JavaScript): var unityInstance = window.unityInstance = UnityLoader.instantiate("unityContainer", "%UNITY_WEBGL_BUILD_URL%", {onProgress: UnityProgress}); Edit: And then using window.unityInstance.SendMessage instead of unityInstance.SendMessage
Code (JavaScript): window.unityInstance.SendMessage('GetJSData' , 'fromJS' , 1); But: Uncaught TypeError: Cannot read property 'SendMessage' of undefined
You are calling this only during builds, correct (not in the editor)? There is no unityInstance unless the application is being loaded in the web browser, which you probably already know.. I can't think of any other reason aside from scope (which is covered by using window.unityInstance) or that the javascript on the page hasn't instantiated your build. That doesn't mean there isn't another possible reason, just that we haven't thought of it yet
Thank you. Yes of course, only in build, not in the Editor. But now I think maybe the problem is that I am presenting the game within Iframe of another platform (Construct 2). I.e. Iframe within Iframe (I make transitions between different games by Iframes). Maybe there's a problem here that he can not talk to Unity properly from the iframe. I'm trying to check it out.
I don't know enough to say what complications that would have - maybe start with getting things to work as expected with the default WebGL template (without being inside of an iframe) and then make a simple page with just an iframe loading that. Then you have an environment set up where you can test things and experiment until you find a solution that works.
There might be something useful at the following link where iframes are concerned, although I'm not sure it's going to be entirely relevant - the puzzling thing is that unityInstance wouldn't be defined within the same window that instantiated the game. Hopefully someone else with experience re Unity+iframes can help. https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
Unfortunately it still does not work. The maximum I succeeded in was when I started the game outside of the internal Iframe, along with the changes that @polemical suggested, and then no error was received, but in practice the function did not work ... I mention it works for me on another project in the same version of Unity. There is something unexplained here. I have tried a lot already ... I am already quite discouraged ... I would love if there are more ideas for a solution.
I did a test with 2019.4.8f1 today, calling SendMessage from a click on a div in a parent window into an instance of Unity running within an iframe. It's working in my browsers on Windows (Chrome, Firefox, Edge). In the parent page I put a button, <div id="btnSendTestMessage">test</div> For .jslib added this function: Code (JavaScript): EnableIFrameParentWindowTest: function(buttonId) { var obj = window.parent.document.getElementById(Pointer_stringify(buttonId)); if (obj != null) { obj.style.color = "blue"; // the color css of my div is grey, so the text in it turns blue like a link when this runs obj.onclick = function() { unityInstance.SendMessage("Javascript", "FromIFrameParentWindow", "msg from iframe parent"); } } } I have an object in the scene root called "Javascript" with a class "Javascript.cs" doing this: Code (CSharp): using UnityEngine; using UnityEngine.UI; public class Javascript : MonoBehaviour { [DllImport("__Internal")] public static extern void EnableIFrameParentWindowTest(string buttonId); public Text output; // I have a Text on a Canvas assigned to this #if UNITY_WEBGL && !UNITY_EDITOR void Start() { EnableIFrameParentWindowTest("btnSendTestMessage"); } #endif public void FromIFrameParentWindow(string msg) { output.text = msg; } } After Unity loads in the iframe, Start is called in the Javascript object and the div in the parent window is assigned .onclick - clicking the div changes the Text component in Unity to say "msg from iframe parent". Note this is all the same domain and if you're trying to access parent window that's from a different domain it will probably fail / you would have to restructure things. Specifying window. before unityInstance wasn't necessary, it's working fine without it. Also, if you wanted to add an onclick handler from within .jslib to an element in the same window/iframe, you could use var obj = document.getElementById(Pointer_stringify(buttonId)); without window.parent. before document like was done in EnableIFrameParentWindowTest.
Thank you. I also tried a new project, and it worked. Maybe I found out what the problem is. I see that the project build does not create a TemplateData folder. It seems to me that this is necessary for our purposes. What could be the reason why the folder was not created? Where is it set up?
Well ... so that was the problem. There was a Minimal setting in the project player settings for webgl. As a result, no TemplateData folder was created for export, and the setting was probably missing. Now it works, after a lot of time and effort ... Thank you so much @polemical , you are great! problem solved.
You're welcome - good luck with your project(s)! For anyone else trying to figure this out I'll answer the last question even though you've solved it: Make sure in Project Settings->Player->WebGL tab->Resolution and Presentation that you have the WebGL Template selected that you want to use. The templates listed there are populated automatically based on what's in the WebGLTemplates folder of the current Unity Editor installation: [unity installation path]\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\WebGLTemplates\
Thanks polemical - this finally got some of my old code working. It looks like we can't expect unityInstance to exist as a global variable anymore, unless we explicitly set it in the webgltemplate as you have done. I have been using it in jslib functions. Downside is the custom web template makes it all-but unusable for distributing generic custom code (e.g. through assetstore).
Thank you so much for this discussion it helped me greatly I ran into this problem today with the latest 2020.2.0f1 build, just wanted to make some observations for this release. As stated you have to use the default template, minimum won't work. To get this working in ifames, create a variable in your main.html at the top e.g. Code (JavaScript): <script> var GlobalUnityInstance = null; </script> then in the generated index.html from unity add the line to the onLoad resolution Code (JavaScript): <script> parent.GlobalUnityInstance = unityInstance; </script> The entire function would look like: Code (JavaScript): script.onload = () => { createUnityInstance(canvas, config, (progress) => { progressBarFull.style.width = 100 * progress + "%"; }).then((unityInstance) => { loadingBar.style.display = "none"; parent.GlobalUnityInstance = unityInstance; //add this line fullscreenButton.onclick = () => { unityInstance.SetFullscreen(1) }; }).catch((message) => { alert(message); }); }; in your main.html create an iframe that references the generated index and you can use GlobalUnityInstance to send messages e.g. Code (JavaScript): GlobalUnityInstance.SendMesaage(...
Huge thanks @polemical, the window.unityInstance thing worked for me. Since you clearly know more about this than me: am I right in saying that some recent Unity version introduced a change to the Default WebGL template that totally breaks the standard unityInstance.SendMessage code they have here https://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html ? And people have written whole libraries based on (e.g. https://github.com/rotolonico/FirebaseWebGL/tree/master/Assets/FirebaseWebGL) If so, thats.. wow. Bad. So many jslibs dying. Should I do a bug report?
As of 2020.1 there were changes, noted here: https://forum.unity.com/threads/cha...-templates-introduced-in-unity-2020-1.817698/ If the library in question was created before 2020.1 (or only supports a version prior to that), it may require changes as well. SendMessage should still work though, as docs indicate it's still supported as of latest listed version (2020.2). Example of use.. create an element on the page like <div id="your_button_id">test</div>, add this to jslib functions: Code (Javascript): mergeInto(LibraryManager.library, { InitializeTestButton: function(buttonId) { var obj = document.getElementById(Pointer_stringify(buttonId)); obj.style.color = "blue"; // changes css text color of the element to blue just to see this function initialized it obj.onclick = function() { window.unityInstance.SendMessage("Javascript", "TestButtonClicked", "button clicked"); } } }); ..then create an GameObject named "Javascript" (or whatever, but must match first param of SendMessage call) in the root of your scene and add a component: Code (CSharp): using System.Runtime.InteropServices; using UnityEngine; public class Javascript : MonoBehaviour { [DllImport("__Internal")] public static extern void InitializeTestButton(string buttonId); #if UNITY_WEBGL && !UNITY_EDITOR void Start() { InitializeTestButton("your_button_id"); // the id="" value of the HTML element that you want to click } #endif public void TestButtonClicked(string msg) { Debug.Log(msg); // javascript console should output "button clicked" when the element is clicked } } Edit: I just realized I gave almost an exact same example as I did earlier in the thread, I should have read through this first and I'd have noticed it was actually the same thread
Ok yea I get all that, it's just wild to me that they'd make this change that breaks all the existing .jslibs people wrote. I have to go back and make them all say "window.unityInstance" instead of "unityInstance" .. I guess this is what I get for upgrading to a newer Unity version. At the very least I guess they just need to fix this website: https://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html Because that code doesn't work as-written any more.
I wanted to call SendMessage from .jspre function. In browser the standard unityInstance.SendMessage worked fine, but when I published to Unity Play (https://play.unity.com/mg/other/buildfolder-2) I would get the unityInstance not defined error. The solution I found was to simply call the directly SendMessage without unityInstance. Assume it work because the functions in .jspre are in the same unityInstance.
Code (JavaScript): function handleCharacteristicValueChanged(event) { const value = event.target.value; const parsedCscValue = parseCscValue(value); // console.log(parsedCscValue) SendMessage('JavascriptHook', 'getSensorReading', JSON.stringify(parsedCscValue)); }
Why don't you use return instead of Sendmessage? Code (JavaScript): function checkGameLocalStorage_1(){ if(localStorage.getItem("hagim81Game_Words")!= null && localStorage.getItem("hagim81Game_Words")>0){ console.log("checkGameLocalStorage") localStorage.removeItem("hagim81Game_Words"); localStorage.removeItem("hagim81ToGame"); localStorage.setItem("hagim81Return",1); return(1); } } }, That will return the parameter to the source of call.
Saving reference of instance in window worked for me (Unity 2018.4). Even though I should be able to access it via plugin, it did not work. I also tried global variable but it was undefined when I tried to access it from plugin. Thanks for solution! I wish there would be an option to send information to singleton... as a workaround I am creating a dummy object which receives a message and then forwards it to singleton. UPD: I did not pay too much attention to docs. There is a ways to return data from the function. Here is the example of string: Code (CSharp): StringReturnValueFunction: function () { var returnStr = "bla"; var bufferSize = lengthBytesUTF8(returnStr) + 1; var buffer = _malloc(bufferSize); stringToUTF8(returnStr, buffer, bufferSize); return buffer; },
In 2021.2.0bX the default WebGL-Index.html template does not have the "unityInstance"-Variable. Could you please fix that or at least update the documentation?