Intermodulation between UE4 HTML5 C++ and JS (4.22 source code version)

Table of contents

1. js to C++

1. c++ statement:

2. Modify HTML5ToolChain.cs file

3. Modify the project_template.js file

4. call

2. C++ calls js

1. Inline 

1) EM_JS call:  

 2)EM_ASM:

3) EM_ASM_INT, EM_ASM_DOUBLE have return value method

2.emscripten_run_script() 

3. Modify the source code

1) Add c++ function in HTML5JavaScriptFx.h extern "c"

2) Add function js implementation in HTML5JavaScriptFx.js

Example 1: Realize c++ calling js to obtain URL by modifying the source code

 Example 2: passing data from c++ to js 

1) EM_ASM implementation:

2) Add new method in HTML5JavaScriptFx


1. js to C++

1. c++ statement:

//声明c++ 函数
//声明可以有返回值,也可以没有返回值
//extern "c" 中可以声明多个函数
//可以向函数中传递参数
extern "C" {
#ifdef __EMSCRIPTEN__
	EMSCRIPTEN_KEEPALIVE
#endif
	char*  web_get_data()
	{    
		return (char*)BufferArray.GetData();
	}

    void   web_set_data(char* indata,int length)
    {
        if(length>sieof(Data))
        {
            retrun;
        }
        Data = indata;
    }
}

//这里需要注意,返回char* 到js中,必须是返回uint8 数组
//BufferArray定义如下
TArray<uint8> BufferArray;

//FString 转到 TArray<uint8> 方法:
    FString SendBuffer = "SendMessage";
	FTCHARToUTF8 Converter(*SendBuff);
	BufferArray.SetNum(Converter.Length());
	FMemory::Memcpy(BufferArray.GetData(), (uint8*)(ANSICHAR*)Converter.Get(), BufferArray.Num());
//这方法是官方使用的方法,可以查看 HTML5HTTP.cpp 中的 SetContentAsString()

2. Modify HTML5ToolChain.cs file

In addition to declaring  the extern "c"  function, you also need to configure the precompiled file in the precompiled configuration. The compiled configuration file is in the HTML5ToolChain.cs file, find the following two lines   

Result += " -s EXPORTED_FUNCTIONS=\"['_main', '_on_fatal']\"";
Result += " -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['Pointer_stringify', 'writeAsciiToMemory', 'stackTrace']\"";

Add the declared c++ function name to the first line, pay attention to add a dash '_'  before the function name

Add a new calling method 'ccall' in the second line, the modified one is as follows:

Result += " -s EXPORTED_FUNCTIONS=\"['_main', '_on_fatal','_web_get_data','_web_set_data']\"";
Result += " -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['Pointer_stringify', 'writeAsciiToMemory', 'stackTrace','ccall']\"";

3. Modify the project_template.js file
 

//找到var UE4 这里的声明
var UE4 = {
	on_fatal: function() {
		try {
			UE4.on_fatal = Module.cwrap('on_fatal', null, ['string', 'string']);
		} catch(e) {
			UE4.on_fatal = function() {};
		}
	}, web_get_data: function () {
		try {
			UE4.web_get_data = Module.ccall('web_get_data', null);
		} catch (e) {
			UE4.web_get_data = function () { };
		}
	},web_set_data: function (indata,inlength) {
		try {
			UE4.web_set_data= Module.ccall('web_set_data', null,['string','string'],['number','number']);
		} catch (e) {
			UE4.web_set_data= function (indata,inlength) { };
		}
	},
};

4. call

When the above three steps are completed, c++ can be called by js

var data = Module.ccall('web_get_data','string');
Module.ccall('web_set_data',null,['string'],['number'])

//其中Module.call里的值表示:
//第一个参数:函数名
//第二个参数:函数返回值类型
//第三个参数起,为函数的传参值类型

2. C++ calls js

1. Inline 

1) EM_JS call:  

//声明EM_JS
EM_JS(void,string_size,(),{
    var str = "123456";
    console.log(str.length);
});

//在cpp函数中调用
string_size();

 2)EM_ASM:

//直接在cpp函数里调用
EM_ASM({
    console.log("打印数据");
});

//可以传参
int32 num = 10;
EM_ASM({
    console.log("传入数字:"+$0);
},num);

//也可以传入多个参数
int32 num1 = 10;
bool bstring = true;
EM_ASM({
    console.log("传入数字:"+$0 + $1);
},num1,bstring);

//传入参数,在js以 $+数字形式表示参数

3) EM_ASM_INT, EM_ASM_DOUBLE have return value method

EM_ASM_INT({
    return 1;
});

EM_ASM_DOUBLE({
    return 1.0;
});

//其它返回值方法一样的
//同样这些调用,都是可以传参

2.emscripten_run_script() 

//这种方式,与EM_ASM类似,但是EM_ASM效率更高
emscripten_run_script("alert('c++ 调用!')");

3. Modify the source code

1) Add c++ function in HTML5JavaScriptFx.h extern "c"

2) Add function js implementation in HTML5JavaScriptFx.js

Note: the implementation in js needs to be written in   $ UE_JSlib

Example 1: Realize c++ calling js to obtain URL by modifying the source code

Add void Get_URLData(const char* outdata, int outsize) in HTML5JavaScriptFx.h;

JS implementation of new functions in HTML5JavaScriptFx.js

//js实现
UE_GetURLString: function (outdata, outsize) {
	var hre = window.location.url;
	if (hre.length >= outsize)
	{
		console.log("window.location.href size harf then OutSize!!!")
		return;
	}
	hre = decodeURI(hre, "utf-8");
	var newhre = window.btoa(encodeURIComponent(hre));
	console.log(newhre);
	Module.writeAsciiToMemory(newhre, outdata);
},

//需要注意的是这里使用了Decode64的转码,如果不使用转码,有中文或是一些符号,在写入内存到c++的时候,会出现乱码、或者信息不全的情况

Call in c++:

//通过EM_ASM_INT 获取url的长度
int32 Hlength= EM_ASM_INT({
		var hre = window.location.url;
		var newhre = window.btoa(encodeURIComponent(hre));
		return newhre.length + 1;
});

//根据url长度创建char
char OutData[Hlength];

//调用js实现
UE_GetURLString(OutData, sizeof(OutData));

//把数据转到FString
FString URL = FString(StringCast<TCHAR>(OutData).Get());

//解码
FString SaveURL;
FBase64::Decode(URL,SaveULR);
SaveURL = FGenericPlatformHttp::UrlDecode(SaveURL);

//到此,URL成功的从js中获取到

 Example 2: passing data from c++ to js 

1) EM_ASM implementation:

//在函数中调用EM_ASM
//上文说到,EM_ASM可以传参,所以直接传参

TArray<uint8> BufferArray;
FString SendBuff = "发送中文消息";

//需要Decode64对SendBuff转码,防止出现乱码
SendBuff = FGenericPlatformHttp::UrlEncode(SendBuff);
SendBuff = FBase64::Encode(SendBuff );

//需要把FString 转换为TArray<uint8>
FTCHARToUTF8 Converter(*SendBuff);
BufferArray.SetNum(Converter.Length());
FMemory::Memcpy(BufferArray.GetData(), (uint8*)(ANSICHAR*)Converter.Get(), BufferArray.Num());

//调用EM_ASM
EM_ASM({
    //转化uint8 数组,使js能识别
    var sdata = Module.HEAP8.subarray($0,$0+$1);
    
    //把uint8 数组转换为string    
	var dataString = "";
	for (var i = 0; i < sdata.length; i++) {
		dataString += String.fromCharCode(sdata[i]);
	}
    
    //解码
    var sendBuff = decodeURIComponent(window.atob(dataString));
    
    //打印原数据
    console.log(sendBuff); 

    //设置URL
    var URL = "http://............"

    //发送到服务器
    $.ajax({
	    url: URL,
		type: "post",
		data: { data: sendBuff },
		dataType: 'text',
		async: true,
		success: function (res) {
	    	console.log("Send Message Success!");
				// 成功回调函数
				//alert('请求数据成功')
			},
		error: function (XMLHttpRequest, textStatus, errorThrown) {
				// 失败回调函数
				console.log(XMLHttpRequest, textStatus, errorThrown);
				//alert('请求数据失败')
			}
		});

},(char*)BufferArray.GetData(),BufferArray.Num());

2) Add new method in HTML5JavaScriptFx

//在HTML5JavaScriptFx.h 的 extern "c" 中新增
void UE_PostRequest(const char* senddata,int sendlength);


//在HTML5JavaScriptFx.js 中新增实现
//注意,要写在 $UE_JSlib 里面
UE_PostRequest:function(senddata,sendlength){
    
    //转换uint8 数组
    var sdata = Module.HEAP8.subarray(senddata, senddata+ sendlength);
    
    //uint8 -> string
	var dataString = "";
	for (var i = 0; i < sdata.length; i++) {
		dataString += String.fromCharCode(sdata[i]);
	}

    //解码
	var sendBuff = decodeURIComponent(window.atob(dataString));
    console.log(sendBuff);

    //设置URL
    var URL = "http://.......";
	$.ajax({
		url: URL,
		type: "post",
		data: { data: sendBuff },
		dataType: 'text',
		async: true,
		success: function (res) {
			console.log("Send Message Success!");
			// 成功回调函数
			//alert('请求数据成功')
		},
		error: function (XMLHttpRequest, textStatus, errorThrown) {
			// 失败回调函数
			console.log(XMLHttpRequest, textStatus, errorThrown);
			//alert('请求数据失败')
		}
	});
    
}

call in cpp

TArray<uint8> BufferArray;
FString SendBuff = "发送中文消息";

//需要Decode64对SendBuff转码,防止出现乱码
SendBuff = FGenericPlatformHttp::UrlEncode(SendBuff);
SendBuff = FBase64::Encode(SendBuff );

//需要把FString 转换为TArray<uint8>
FTCHARToUTF8 Converter(*SendBuff);
BufferArray.SetNum(Converter.Length());
FMemory::Memcpy(BufferArray.GetData(), (uint8*)(ANSICHAR*)Converter.Get(), BufferArray.Num());

#ifdef __EMSCRIPTEN__ 
	UE_PostRequest( (char*)BufferArray.GetData(), BufferArray.Num());
#endif	

//到此,c++调用js方法就已经完成了

Note: The method of calling js in cpp, no matter which one it is, must include the following header files


#ifdef __EMSCRIPTEN__ otherwise ignore”.
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
#else
#define EMSCRIPTEN_KEEPALIVE
#endif

And when writing, if you call the js method, you must do this, otherwise when compiling the project, you will report an error, unless you directly package HTML5

#ifdef __EMSCRIPTEN__ 
	//调用js实现必须写这里面
#endif	

The above are the commonly used intermodulation methods between UE4 c++ and js, which can be found in the engine. I am here to make a summary and summary based on my own project, and share it with everyone. I hope it can help you. If there are mistakes, I hope to correct them.

Guess you like

Origin blog.csdn.net/acdfg123/article/details/118932939