Search Unity

unityInstance is not defined

Discussion in 'Scripting' started by MeirKids, Aug 12, 2020.

  1. MeirKids

    MeirKids

    Joined:
    Feb 28, 2019
    Posts:
    10
    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):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.Runtime.InteropServices;
    5.  
    6. public class GetJSData : MonoBehaviour
    7. {
    8.     [DllImport("__Internal")]
    9.     public static extern void callGame(int gameNum);
    10.  
    11.     [DllImport("__Internal")]
    12.     public static extern void checkGameLocalStorage();
    13.  
    14.  
    15.     [DllImport("__Internal")]
    16.     public static extern void resetVaribiles();
    17.  
    18.  
    19.     // Start is called before the first frame update
    20.     void Start()
    21.     {
    22.         checkGameLocalStorage();
    23.      
    24.     }
    25.     public void fromJS(int val){
    26.         Debug.Log("fromJS!!!!!!");
    27.  
    28.         GameManager.ifGameOut = false;
    29.  
    30.         if(val==1){
    31.         GameProcess.ifCorrect_fromGame=true;
    32.         } else {
    33.         GameProcess.ifCorrect_fromGame=false;
    34.       }
    35. Debug.Log(GameProcess.ifCorrect_fromGame);
    36.  
    37.     }
    38. }
    39.  
    And the JS code:
    Code (JavaScript):
    1. mergeInto(LibraryManager.library, {
    2.  
    3.     resetVaribiles: function(){
    4.         localStorage.removeItem("hagim81Game_Words");
    5.         localStorage.removeItem("hagim81ToGame");
    6.         localStorage.removeItem("hagim81Return");
    7.         localStorage.removeItem("wordSearchSumWords");
    8.         //localStorage.removeItem("");
    9.         localStorage.setItem('refrashIframes',1);
    10.  
    11. },
    12.  
    13.     callGame: function (gameNum) {
    14.         localStorage.setItem("hagim81ToGame",gameNum);
    15.  
    16. },
    17.  
    18. checkGameLocalStorage: function(){
    19. setInterval(function(){
    20. checkGameLocalStorage_1();
    21. },1000)
    22.  
    23.  
    24. function checkGameLocalStorage_1(){
    25.     if(localStorage.getItem("hagim81Game_Words")!= null && localStorage.getItem("hagim81Game_Words")>0){
    26.         console.log("checkGameLocalStorage")
    27.         localStorage.removeItem("hagim81Game_Words");
    28.         localStorage.removeItem("hagim81ToGame");
    29.         localStorage.setItem("hagim81Return",1);
    30.  
    31.         unityInstance.SendMessage('GetJSData' , 'fromJS' , 1);
    32.     }
    33. }
    34. },
    35.  
    36.  
    37. })
    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.
     
    Last edited: Aug 12, 2020
  2. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    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):
    1.  
    2. createUnityInstance(canvas, config, (progress) => {
    3.   progressBarFull.style.width = 100 * progress + "%";
    4. }).then((unityInstance) => {
    5.  
    6.   window.unityInstance = unityInstance; // <-- this
    7.  
    ..and in my .jslib:
    Code (CSharp):
    1. window.unityInstance.SendMessage(...
     
  3. MeirKids

    MeirKids

    Joined:
    Feb 28, 2019
    Posts:
    10
    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.
     
  4. MeirKids

    MeirKids

    Joined:
    Feb 28, 2019
    Posts:
    10
    Honestly, I did not understand where you put the first code ... if you can please explain to me. Thanks!
     
  5. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    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.
     
    MeirKids likes this.
  6. MeirKids

    MeirKids

    Joined:
    Feb 28, 2019
    Posts:
    10
    Maybe the code in 2019 is different. This is what the HTML looks like:
    Code (JavaScript):
    1.  
    2. <!DOCTYPE html>
    3. <html lang="en-us">
    4.   <head>
    5.     <meta charset="utf-8">
    6.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    7.     <title>Unity WebGL Player | %UNITY_WEB_NAME%</title>
    8.     <link rel="shortcut icon" href="TemplateData/favicon.ico">
    9.     <link rel="stylesheet" href="TemplateData/style.css">
    10.     <script src="TemplateData/UnityProgress.js"></script>
    11.     <script src="%UNITY_WEBGL_LOADER_URL%"></script>
    12.     <script>
    13.       var unityInstance = UnityLoader.instantiate("unityContainer", "%UNITY_WEBGL_BUILD_URL%", {onProgress: UnityProgress});
    14.     </script>
    15.   </head>
    16.   <body>
    17.     <div class="webgl-content">
    18.       <div id="unityContainer" style="width: %UNITY_WIDTH%px; height: %UNITY_HEIGHT%px"></div>
    19.       <div class="footer">
    20.         <div class="webgl-logo"></div>
    21.         <div class="fullscreen" onclick="unityInstance.SetFullscreen(1)"></div>
    22.         <div class="title">%UNITY_WEB_NAME%</div>
    23.       </div>
    24.     </div>
    25.   </body>
    26. </html>
     
  7. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    So looking at the 2019 WebGL template it has this:
    Code (JavaScript):
    1. var unityInstance = UnityLoader.instantiate("unityContainer", "%UNITY_WEBGL_BUILD_URL%", {onProgress: UnityProgress});
    Try changing that to:
    Code (JavaScript):
    1. var unityInstance = window.unityInstance = UnityLoader.instantiate("unityContainer", "%UNITY_WEBGL_BUILD_URL%", {onProgress: UnityProgress});
    Edit: And then using window.unityInstance.SendMessage instead of unityInstance.SendMessage
     
    MeirKids likes this.
  8. MeirKids

    MeirKids

    Joined:
    Feb 28, 2019
    Posts:
    10
    Code (JavaScript):
    1.         window.unityInstance.SendMessage('GetJSData' , 'fromJS' , 1);
    2.  
    But:
    Uncaught TypeError: Cannot read property 'SendMessage' of undefined
     
  9. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    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 :D
     
    MeirKids likes this.
  10. MeirKids

    MeirKids

    Joined:
    Feb 28, 2019
    Posts:
    10
    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.
     
  11. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    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.
     
    MeirKids likes this.
  12. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    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
     
    MeirKids likes this.
  13. MeirKids

    MeirKids

    Joined:
    Feb 28, 2019
    Posts:
    10
    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.
     
  14. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    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):
    1.   EnableIFrameParentWindowTest: function(buttonId) {
    2.     var obj = window.parent.document.getElementById(Pointer_stringify(buttonId));
    3.     if (obj != null)
    4.     {
    5.         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
    6.         obj.onclick = function() {
    7.           unityInstance.SendMessage("Javascript", "FromIFrameParentWindow", "msg from iframe parent");
    8.         }
    9.     }
    10.   }
    I have an object in the scene root called "Javascript" with a class "Javascript.cs" doing this:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. public class Javascript : MonoBehaviour
    4. {
    5.   [DllImport("__Internal")] public static extern void EnableIFrameParentWindowTest(string buttonId);
    6.   public Text output; // I have a Text on a Canvas assigned to this
    7. #if UNITY_WEBGL && !UNITY_EDITOR
    8.   void Start()
    9.   {
    10.     EnableIFrameParentWindowTest("btnSendTestMessage");
    11.   }
    12. #endif
    13.   public void FromIFrameParentWindow(string msg)
    14.   {
    15.     output.text = msg;
    16.   }
    17. }
    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.
     
    MeirKids likes this.
  15. MeirKids

    MeirKids

    Joined:
    Feb 28, 2019
    Posts:
    10
    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?
     
  16. MeirKids

    MeirKids

    Joined:
    Feb 28, 2019
    Posts:
    10
    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.
     
  17. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    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\
     
    amateurd and MeirKids like this.
  18. amateurd

    amateurd

    Joined:
    Nov 1, 2016
    Posts:
    97
    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).
     
    adamgolden likes this.
  19. eci22

    eci22

    Joined:
    May 3, 2014
    Posts:
    3
    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):
    1. <script>
    2. var GlobalUnityInstance = null;
    3. </script>
    then in the generated index.html from unity add the line to the onLoad resolution

    Code (JavaScript):
    1. <script>
    2. parent.GlobalUnityInstance  = unityInstance;
    3. </script>
    The entire function would look like:

    Code (JavaScript):
    1.  
    2. script.onload = () => {
    3.         createUnityInstance(canvas, config, (progress) => {
    4.           progressBarFull.style.width = 100 * progress + "%";
    5.         }).then((unityInstance) => {
    6.           loadingBar.style.display = "none";
    7.            parent.GlobalUnityInstance  = unityInstance; //add this line
    8.           fullscreenButton.onclick = () => {
    9.             unityInstance.SetFullscreen(1)
    10.           };
    11.         }).catch((message) => {
    12.           alert(message);
    13.         });
    14.       };
    15.  
    in your main.html create an iframe that references the generated index and you can use GlobalUnityInstance to send messages e.g.

    Code (JavaScript):
    1.  
    2. GlobalUnityInstance.SendMesaage(...
    3.  
     
    Last edited: Dec 24, 2020
  20. travlake

    travlake

    Joined:
    Oct 4, 2019
    Posts:
    50
    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?
     
    Last edited: Jun 2, 2023
    DBarlok and adamgolden like this.
  21. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    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):
    1.  
    2. mergeInto(LibraryManager.library, {
    3.  
    4.   InitializeTestButton: function(buttonId) {
    5.     var obj = document.getElementById(Pointer_stringify(buttonId));
    6.     obj.style.color = "blue"; // changes css text color of the element to blue just to see this function initialized it
    7.     obj.onclick = function() {
    8.       window.unityInstance.SendMessage("Javascript", "TestButtonClicked", "button clicked");
    9.     }
    10.   }
    11.  
    12. });
    ..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):
    1. using System.Runtime.InteropServices;
    2. using UnityEngine;
    3. public class Javascript : MonoBehaviour
    4. {
    5.   [DllImport("__Internal")] public static extern void InitializeTestButton(string buttonId);
    6. #if UNITY_WEBGL && !UNITY_EDITOR
    7.   void Start()
    8.   {
    9.     InitializeTestButton("your_button_id"); // the id="" value of the HTML element that you want to click
    10.   }
    11. #endif
    12.   public void TestButtonClicked(string msg)
    13.   {
    14.     Debug.Log(msg); // javascript console should output "button clicked" when the element is clicked
    15.   }
    16. }
    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 :rolleyes::D
     
    Last edited: Jan 15, 2021
  22. travlake

    travlake

    Joined:
    Oct 4, 2019
    Posts:
    50
    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.

     
  23. demozbox

    demozbox

    Joined:
    Nov 26, 2014
    Posts:
    83
    please could you show your index.html? it is so not clear to understend.
     
  24. naber78

    naber78

    Joined:
    Aug 25, 2020
    Posts:
    13
    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.
     
  25. naber78

    naber78

    Joined:
    Aug 25, 2020
    Posts:
    13
    Code (JavaScript):
    1. function handleCharacteristicValueChanged(event) {
    2.     const value = event.target.value;
    3.     const parsedCscValue = parseCscValue(value);
    4.   //  console.log(parsedCscValue)
    5.     SendMessage('JavascriptHook', 'getSensorReading', JSON.stringify(parsedCscValue));
    6. }
     
  26. naber78

    naber78

    Joined:
    Aug 25, 2020
    Posts:
    13
    Why don't you use return instead of Sendmessage?
    Code (JavaScript):
    1. function checkGameLocalStorage_1(){
    2.     if(localStorage.getItem("hagim81Game_Words")!= null && localStorage.getItem("hagim81Game_Words")>0){
    3.         console.log("checkGameLocalStorage")
    4.         localStorage.removeItem("hagim81Game_Words");
    5.         localStorage.removeItem("hagim81ToGame");
    6.         localStorage.setItem("hagim81Return",1);
    7.  
    8.         return(1);
    9.     }
    10. }
    11. },
    That will return the parameter to the source of call.
     
    DBarlok likes this.
  27. andrew_pearce_

    andrew_pearce_

    Joined:
    Nov 5, 2018
    Posts:
    169
    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):
    1.   StringReturnValueFunction: function () {
    2.     var returnStr = "bla";
    3.     var bufferSize = lengthBytesUTF8(returnStr) + 1;
    4.     var buffer = _malloc(bufferSize);
    5.     stringToUTF8(returnStr, buffer, bufferSize);
    6.     return buffer;
    7.   },
     
    Last edited: Aug 21, 2021
    adamgolden likes this.
  28. manuelgoellnitz

    manuelgoellnitz

    Joined:
    Feb 15, 2017
    Posts:
    397
    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?
     
  29. ibyte

    ibyte

    Joined:
    Aug 14, 2009
    Posts:
    1,048
    Thanks for the solution!
     
    adamgolden likes this.