x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Office Holiday

Epic Games' offices will be on holiday from June 22nd to July 7th. During this period support will be limited. Our offices will reopen on Monday, July 8th. 

HTML5 Content: Control Browser From inside UE4

First question, so be gentle...

I'm trying to create two-way communication between an HTML5-authored UE4 game and the browser running it. After searching around, it looks like emscripten is capable of this, but even when I try a simple test of popping an alert window (as the documentation shows) with:

  emscripten_run_script("alert('hi')");

My game fails to compile with an error:

  Id: symbol(s) not found for architecture x86_64

and

  clang error: linker command failed with exit code -1 (use -v to see invocation)

Prior to this, I was getting errors about missing emscripten header files, but after manually moving them into my project, those went away, so I suspect that this may be a Mac-related problem.

So, two questions:

  1.) Do I need to install emscripten onto my system beyond what comes with UE4?

  2.) I'm new to doing a C++ UE4 project, so it's entirely possible that I'm missing something simple that I don't know how to ask about. Could anyone provide a sample of this alert call working?

Thanks in advance!

Product Version: UE 4.14
Tags:
more ▼

asked Dec 21 '16 at 02:04 AM in Everything Else

avatar image

dmultimediab
21 3 4 7

avatar image Trandana Apr 11 '18 at 03:02 AM

I seem to get an Uncaught ReferenceError that my javascript function is not defined. I call the javascript function, which is in the HTML file running the game, from Unreal using your JS target function and the Comm_GetStringFromJS() function in C++

but i get this when it's called: alt text

Even though I have defined the function in the html file. Any ideas?

capture.png (4.0 kB)
avatar image Invius Jan 16 '19 at 05:58 PM

You got to put your javascript function in the file [YourGameName].js At the end of it!

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

2 answers: sort voted first

So, to sum up (so future readers hopefully won’t have to suffer as much as I did)...

To communicate between UE4 and JS in the HTML wrapper of an HTML5-authored piece:

 //--------------------------------------------------------------------------------------------
 //--------------------------------------------------------------------------------------------
 //-------------------------------------
 // In C++ top of page:
 //-------------------------------------
 
 //Gotta have these to do onscreen messages
 #include "EngineGlobals.h"
 #include "Engine.h"
 
 //Gotta have these to make emscripten work
 #ifdef __EMSCRIPTEN__ //This basically says “if emscripten is doing stuff, add this, otherwise ignore”.
 #include "SDL/SDL.h"
 #include "emscripten/emscripten.h"
 #include "emscripten/html5.h"
 #else
 #define EMSCRIPTEN_KEEPALIVE
 #endif //This closes the “ifdef __EMSCRIPTEN__” tag.
 
 //Gotta have this to make translation of strings to JavaScript work
 #include "string"
 #include "iostream"
 
 #include "sstream"
 
 //-------------------------------------
 
 
 
 //--------------------------------------------------------------------------------------------
 // Use emscripten to run a pre-existing javascript function in the container HTML page
 //--------------------------------------------------------------------------------------------
 
 //-------------------------------------
 // In header file (public section):
 //-------------------------------------
 
 // The BlueprintCallable is the key to making the function callable via Blueprints (duh), and Category is required
     UFUNCTION(BlueprintCallable, Category = "Comms")
     void Comm_RunExtJS();
 
 //-------------------------------------
 // In C++ body:
 //-------------------------------------
 
 void ABrowserCommActor::Comm_RunExtJS()
 {
     GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, TEXT("Comm_RunExtJS() Triggered!”)); //This puts a message onscreen inside UE4, so you know this side executed. If you want to verify the JS side, you’ll need an alert in that function.
 
 #ifdef __EMSCRIPTEN__ // Shields the JavaScript code from the C++ compiler to prevent errors.
 
     emscripten_run_script(“targetJSFunction();”); //This runs a function WITHOUT passing any parameters
 
 #endif
 }
 //-------------------------------------
 
 
 
 //--------------------------------------------------------------------------------------------
 // Use emscripten to send a string to a function in the container HTML page
 //--------------------------------------------------------------------------------------------
 
 //-------------------------------------
 // In header file (public section):
 //-------------------------------------
 
 // The BlueprintCallable is the key to making the function callable via Blueprints (duh), and Category is required
     UFUNCTION(BlueprintCallable, Category = "Comms")
     void Comm_SendStringToJS();
 
 //-------------------------------------
 // In C++ body:
 //-------------------------------------
 
 void ABrowserCommActor::Comm_SendStringToJS()
 {
     GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, TEXT("Comm_SendStringToJS() Triggered!”)); //This puts a message onscreen inside UE4, so you know this side executed. If you want to verify the JS side, you’ll need an alert in that function.
 
     //SendDataString = “Yo!”; // Use this to artificially set the data string for testing.
     std::string OutboundSendString(TCHAR_TO_UTF8(*SendDataString)); // This converts the FString (UE4 string) to a regular string.
     
 #ifdef __EMSCRIPTEN__ // Shields the JavaScript code from the C++ compiler to prevent errors.
     //This executes JS code and passes arguments (in this case a string).
     EM_ASM_ARGS({
         var theData = Pointer_stringify($0); // Convert the string to a JS string.
         //alert(“JS Triggered!”);            // Pop an alert for testing.
         destJSFunction(theData);             // Call a JS function in the wrapper HTML, passing the string to it.
     }, OutboundSendString.c_str());          // Pass the string from UE4 world into the emscripten function.
 #endif
 }
 
 //-------------------------------------
 // In HTML Wrapper JavaScript:
 //-------------------------------------
 
 function destJSFunction(dataFromC){
 
     alert(dataFromC); // Displays the string from UE4.
 
 }
 //-------------------------------------
 
 
 
 //--------------------------------------------------------------------------------------------
 // Use emscripten to receive a string from a function in the container HTML page
 //--------------------------------------------------------------------------------------------
 
 //-------------------------------------
 // In header file (public section):
 //-------------------------------------
 
 // The BlueprintCallable is the key to making the function callable via Blueprints (duh), and Category is required
     UFUNCTION(BlueprintCallable, Category = "Comms")
     void Comm_GetStringFromJS();
 
 // String to hold received data from JS
     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= "Comms")
     FString LMSReturnedDataString = "hi";
 
 //-------------------------------------
 // In C++ body:
 //-------------------------------------
 
 void ABrowserCommActor::Comm_GetStringFromJS()
 {
     GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, TEXT("Comm_GetStringFromJS() Triggered!”)); //This puts a message onscreen inside UE4, so you know this side executed. If you want to verify the JS side, you’ll need an alert in that function.
 
 #ifdef __EMSCRIPTEN__ // Shields the JavaScript code from the C++ compiler to prevent errors.
 
     emscripten_run_script(“getJSFunction();"); //This runs a function WITHOUT passing any parameters. Basically, it’s just calling the JS, which does the work.
 
 #endif
 }
 
 // This receives the string back from JS and displays it onscreen
 extern "C" void EMSCRIPTEN_KEEPALIVE receiveString(char *str) {
     FString returnedDataString(str); //This converts the string from JavaScript to a UE4 FString
     GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, returnedDataString); //Displays the result onscreen
 }
 
 //-------------------------------------
 // In HTML Wrapper JavaScript:
 //-------------------------------------
 
 function targetJSFunction(){
 
     var value = “Hi from JS!”;
     
     //window.alert("JS Triggered!"); // Used to make sure the function is getting called.
     
     var lenUTF8 = Module.lengthBytesUTF8(value) + 1; // This gets the length of the string, adds 1 to it for a stop bit, and stores that.
     var ptr = Module._malloc(lenUTF8); // This calls back to the C++ code, allocates the appropriate amount of space, and creates a variable that's a pointer to that memory.
     Module.stringToUTF8(value, ptr, lenUTF8); // This calls the C++ and tells it to convert the JS string to a UTF8 char array (like a string, but NOT a string) in C++.
     Module.ccall('receiveString', 'null',['string'], [value]); // This calls the function "receiveString" in UE4 and passes the string as a parameter into it.
     Module._free(ptr); // This clears the memory at the pointer location, so old stuff doesn't just pile up.
     
 }
 //-------------------------------------
more ▼

answered Mar 15 '17 at 09:05 PM

avatar image

dmultimediab
21 3 4 7

avatar image dmultimediab Mar 16 '17 at 08:33 PM

OK, One addition:

I found that, while I was able to get the string on the screen, I wasn’t able to get it into my returnedDataString variable (which I also noticed was miswritten as LMSreturnedDataString). I’m sure this isn’t the best solution (as a matter of fact, I know I’m going to get yelled at for doing it this way), but I added a global variable in the C++ body (above Comm_GetStringFromJS()) with the line:

 FString returnedStringHold_Global;

Then I added a line in the extern “C” function to move the string into it:

 returnedStringHold_Global = convertedString;

And, finally, I added a line in Comm_GetStringFromJS() to move it into the final, blueprint-accessible, destination:

 LMSReturnedDataString = returnedStringHold_Global;

NOW everything is happy! ;-)

avatar image strangeman Dec 13 '17 at 05:57 AM

I think UPROPERTY is optional...

FString UCubeJSFunctionLibrary::Comm_GetStringFromJS() { #ifdef EMSCRIPTEN emscripten_run_script("getJSFunction();"); #endif
return returnedStringHold_Global; }

... and UTF8_TO_TCHAR is needed to convert JS string into FString correctly:

extern "C" void EMSCRIPTEN_KEEPALIVE receiveString(char *str) {
FString returnedDataString(UTF8_TO_TCHAR(str)); returnedStringHold_Global = returnedDataString; }

Thanks for example!

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

To be able to call Emscripten APIs from your own project, you should need to install Emscripten SDK manually, since it is already bundled to UE4, and no copying should be needed either. What you are trying looks fairly good. Try doing the following:

At the top of a .c/.cpp file:

 #ifdef __EMSCRIPTEN__
 #include <emscripten/emscripten.h>
 #endif
 

and then at the site of use:

 #ifdef __EMSCRIPTEN__
     emscripten_run_script("alert('hi')");
 #endif

The ifdefs ensure that the UE4 compiler doesn't get confused when it tries to build the code also for other platforms natively. Also try out the latest UE 4.15 if that would work better? If not, please post the full logs, it feels that the "symbol(s) not found" error would be a secondary error after some first error occurred. Was there anything else printed prior to that?

more ▼

answered Mar 09 '17 at 02:01 PM

avatar image

juj
1.2k 15 4 17

avatar image dmultimediab Mar 09 '17 at 05:13 PM

Thank you for the response!!! I assumed everyone thought my question was too noob to answer, so in the ensuing time between my original post and now, I've managed to battle my way through this (your answer jives exactly with what I discovered) and even passing strings from UE4 to the browser via emscripten/JS. What's left that I still can't get is passing a string from JS back to UE4. I found a few threads on here that had some pieces, but not a single coherent answer (or at least one that I could understand). Do you know of a working example of the JS and C++ code?

This is the solution I'm dancing around (but can't get right): JS: function sendToUE(){ var str = "TEST"; var buffer = Module._malloc(str.length + 1); Module.writeStringToMemory(str, buffer); Module.ccall('printString', 'null',['string'], buffer); }

C++: EMSCRIPTEN_KEEPALIVE void printString(char * str) { //std::cout << str << std::endl; //killed this and replacing with onscreen debug, since UE4 doesn't do cout well GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, TEXT("How about this?:")); GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, str); }

I apologize again for my rank amateur coding abilities. The problem may be as simple as syntax/structure (or the whole thing may be total garbage), but it's killing me! If you've got any insights, you'd forever be my hero!

avatar image juj Mar 09 '17 at 11:32 PM

The example you have looks good, although three things I notice are that it won't work with non-ascii text characters, so sending the string over as UTF-8 (or UTF-16) would be better; I think it should use ['number'] instead of ['string']; and it should remember to call _free at the end.

Perhaps something like this, JS side:

 function sendToUE() {
   var str = "TEST";
   var lenUTF8 = Module.lengthBytesUTF8(str) + 1;
   var ptr = Module._malloc(lenUTF8);
   Module.stringToUTF8(str, ptr, lenUTF8); 
   Module.ccall('printString', 'null', ['number'], ptr);
   Module._free(ptr);
 }

and C++ side:

 // top of page:
 #ifdef __EMSCRIPTEN__
 #include <emscripten/emscripten.h>
 #else
 #define EMSCRIPTEN_KEEPALIVE
 #endif
 
 // at site of use:
 void EMSCRIPTEN_KEEPALIVE printString(char *str) {
   printf("%s\n", str);
 }
 

I am not familiar with GEngine->AddOnScreenDebugMessage() API, but probably that should work as well.

avatar image dmultimediab Mar 10 '17 at 06:27 PM

Thank you so much! Your modifications definitely make sense. On the C++ side I had to add 'extern "C"' to the printString function to get it recognized when the JS calls it. I also added:

 GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, FString::Printf(TEXT("I got: %s"), &str));

(which I know you're not familiar with, but has served me well during the rest of development) so that I can see the result onscreen in realtime (along with an alert in JS to verify execution).

So, now everything seems to trigger – I can trigger the JS from UE4, which in turn triggers the printString function back in UE4, and no errors get thrown along the way. Unfortunately, what still isn't happening is the string being displayed. Instead, the data appears to be blank. I'm going to try and verify that my GEngine line is structured properly (so I know that it's not misleading me), but is it possible that the string conversion is not working right, or maybe that the string isn't getting placed into the "str" variable? Do you have any other ideas as to what might be going on? Thanks again!

avatar image dmultimediab Mar 16 '17 at 05:35 AM

Looks like I spoke too soon on "got it working"...

The string is coming in, and displays onscreen with the GEngine line, but now the data is trapped in str, inside the extern "C" scope. I can't get it to a variable in the main UE4 code. Again, this is probably due simply to my inexperience with variables and scope in C++/UE4. Would you have any insight on this piece, by chance?

Thanks, thanks, and thanks again!

avatar image dmultimediab Mar 11 '17 at 01:05 AM

GOT IT WORKING!!!

I had to tweak the Module.ccall line in the JS to this:

 Module.ccall('printString', 'null',['string'], [str]);

Also, the problem with nothing showing in the onscreen output was that GEngine->AddOnScreenDebugMessage doesn't work with char variables. Once I added a variable to convert it:

 FString outString(str);

and changed my output line to this:

 GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, outString);

It worked like a charm! A million - no a billion "thank yous" for your help and guidance!

avatar image Trandana Apr 09 '18 at 04:31 AM

I've been trying to implement this and it compiles okay, but I can't access the function from within blueprints. In fact the C++ class isn't even showing up in my content folder or class viewer

avatar image Invius Jan 16 '19 at 05:58 PM

You got to put your javascript function in the file [YourGameName].js At the end of it!

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question