Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Converting to WebGL

Discussion in 'Web' started by bpatton00, Apr 9, 2015.

  1. bpatton00

    bpatton00

    Joined:
    Sep 10, 2013
    Posts:
    13
    I have been unable to locate any information regarding this problem.

    With the traditional unity webplayer I was able to pass parameters to the viewer and then reference them in order to get the data that I needed.

    I used this javascript snippet
    var raceid = $.getUrlVar('RaceID');
    u.initPlugin(jQuery("#unityPlayer")[0], "TestWebPlayer.unity3d?RaceID=" + raceid);

    I do not see any similar ability with WebGL documented anywhere, could someone guide me in the right direction?
     
  2. De-Panther

    De-Panther

    Joined:
    Dec 27, 2009
    Posts:
    589
  3. bpatton00

    bpatton00

    Joined:
    Sep 10, 2013
    Posts:
    13
    Thanks for the response, I will give it a try by taking the first line and putting it in a jslib compiled within the project thanks.
     
  4. gfoot

    gfoot

    Joined:
    Jan 5, 2011
    Posts:
    550
    A jslib is probably overkill for this - so long as you don't mind waiting until the next frame to get the data asynchronously, you can just use Application.ExternalCall to call a javascript function from the wrapper page itself, and have that function use SendMessage to send any data back to your app.
     
  5. De-Panther

    De-Panther

    Joined:
    Dec 27, 2009
    Posts:
    589
    In my opinion, "Application.ExternalCall" for this case is the overkill.
    You'll have to make sure there's an object in the scene with specific name, that has a component with specific method.

    "Application.ExternalCall" is great if you only want to send info from inside unity to the web page. If you want to get info from the page - using "jslib" is very simple
     
  6. bpatton00

    bpatton00

    Joined:
    Sep 10, 2013
    Posts:
    13
    Just to confirm on this point " send info from inside unity to the web page" . My need is to go the other way exclusively. I need to be able to get a value from the webpage to inside the application. That value is then used to call an XML feed which brings in the appropriate data.

    Does this change anything? or did you already understand my question?
     
  7. gfoot

    gfoot

    Joined:
    Jan 5, 2011
    Posts:
    550
    Yes, these methods are to support that. If you just wanted to call some Javascirpt the ExternalCall is definitely the way to go, as that is all it does. Equally, if you just want to pass the data from Javascript to C# in a manner that is initiated by the Javascript (e.g. user pressed a button in the web page) then you should probably use message sending.

    I was assuming that you wanted C# to initiate the process, and also receive the data back. jslib might be the west way to do that, it's just rather fiddly and error-prone.

    Using a jslib your function can return simple data types, but complex types may need marshaling. Alternatively you can pass a pointer and have your Javascript function write data into your C# memory directly; then you usually need to marshal that into normal managed C# types (e.g. decode UTF8 string data or whatever, depending on the format you used from the Javascript side).

    With the message route, your C# would call some Javascript in your web page, which would get the data and send it back to your C#. The only data you can pass here is a string, I think, and you receive it in C# via a call to a method on a Monobehaviour, possibly not until the next update frame I think. It is rather naff.
     
    bpatton00 likes this.
  8. bpatton00

    bpatton00

    Joined:
    Sep 10, 2013
    Posts:
    13
    Thanks to both of you. It sounds like the message route is the best for my purposes as I it meets all my needs in terms of sending a simple string value to the viewer that can be converted by the c# function.
     
  9. De-Panther

    De-Panther

    Joined:
    Dec 27, 2009
    Posts:
    589
    I don't know how you understood that SendMessage is what you need :confused:

    You can write jslib file like this:
    Code (JavaScript):
    1. var MyPlugin = {
    2.     GetRaceIDFromPage: function()
    3.     {
    4.         return $.getUrlVar('RaceID');
    5.     }
    6. };
    7.  
    8. mergeInto(LibraryManager.library, MyPlugin);

    ( edited, fixed the code: )
    Code (JavaScript):
    1. var MyPlugin = {
    2.     GetRaceIDFromPage: function()
    3.     {
    4.        var returnStr = $.getUrlVar('RaceID');
    5.        var buffer = _malloc(returnStr.length + 1);
    6.        writeStringToMemory(returnStr, buffer);
    7.        return buffer;
    8.     }
    9. };
    10. mergeInto(LibraryManager.library, MyPlugin);
    and then a sample class in unity c#:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Runtime.InteropServices;
    3.  
    4. public class NewBehaviourScript : MonoBehaviour {
    5.  
    6.     [DllImport("__Internal")]
    7.     private static extern string GetRaceIDFromPage();
    8.  
    9.     void Start() {
    10.         Debug.Log(GetRaceIDFromPage());
    11.     }
    12. }
    you just need to make sure that jQuery is loaded before the unity webgl module, because "$.getUrlVar" uses it.
     
    Last edited: Apr 11, 2015
  10. gfoot

    gfoot

    Joined:
    Jan 5, 2011
    Posts:
    550
    De-Panther, last time I tried returning strings from jslib functions it didn't work. Maybe I did something wrong. But I think you need to marshal it manually. You can only return really simple types, like ints and floats.

    Edit: I just tested to check again, and it didn't work, it just gets null. The generated code is pretty clear:

    Code (JavaScript):
    1. function _GetRaceIDFromPage()
    2. {
    3.     return "Race ID";
    4. }
    5.  
    6. function __Z2m1P2t4P10MethodInfo($__this,$method) {
    7.     $__this = $__this|0;
    8.     $method = $method|0;
    9.     var $0 = 0, $1 = 0, $2 = 0, $3 = 0, label = 0, sp = 0;
    10.     sp = STACKTOP;
    11.     $0 = HEAP32[1621000>>2]|0;
    12.     $1 = ($0|0)==(0|0);
    13.     if ($1) {
    14.         HEAP32[1621000>>2] = 79;
    15.     }
    16.     $2 = (_GetRaceIDFromPage()|0);
    17.     $3 = (__ZN6il2cpp2vm14PlatformInvoke36MarshalCppStringToCSharpStringResultEPKc($2)|0);
    18.     __ZN6il2cpp2vm14PlatformInvoke11MarshalFreeEPv($2);
    19.     STACKTOP = sp;return ($3|0);
    20. }
    21.  
    The first function is Emscripten embedding the function from the jslib - I hard-coded the string to return. The second function is the compiled C# method. You can see that it calls the jslib function, but then forces the result to integer type, getting zero. It then tries to do some marshalling of what it assumes is a C++ string, but it's zero, so the result is null.

    I think you could make it work if your jslib function created a C++ string on the heap and returned the address of that. Emscripten has some helpers to do this, so maybe this would work in the jslib function:

    Code (csharp):
    1.  
    2. return allocate(intArrayFromString($.getUrlVar('RaceID')), 'i8', ALLOC_NORMAL);
    3.  
    It allocates a buffer on the C++ heap and stores the string there in UTF-8 format. That might be what's needed to get Unity's marshalling to work, if it expects UTF-8.
     
    Last edited: Apr 11, 2015
  11. De-Panther

    De-Panther

    Joined:
    Dec 27, 2009
    Posts:
    589
    well, now that I look at it(I needed only float value in my projects) they do some "writeStringToMemory" in order to return string.
    so a fix for this could be:
    Code (JavaScript):
    1. var MyPlugin = {
    2.     GetRaceIDFromPage: function()
    3.     {
    4.        var returnStr = $.getUrlVar('RaceID');
    5.        var buffer = _malloc(returnStr.length + 1);
    6.        writeStringToMemory(returnStr, buffer);
    7.        return buffer;
    8.     }
    9. };
    10. mergeInto(LibraryManager.library, MyPlugin);
    But if RaceID is Int, I don't think it should have a problem
     
    Last edited: Apr 11, 2015