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.
}
//-------------------------------------