UE4 HTML5 C++ 与JS 互调 (4.22源码版)

目录

一、js 调 C++

1.c++声明:

2.修改HTML5ToolChain.cs 文件

3.修改project_template.js 文件

4.调用

二、C++ 调用 js

1.内联 

1)EM_JS调用:  

 2)EM_ASM:

3)EM_ASM_INT、EM_ASM_DOUBLE 有返回值方法

2.emscripten_run_script() 

3.修改源码

1)在HTML5JavaScriptFx.h extern "c"中新增c++函数

2)在HTML5JavaScriptFx.js中新增函数的 js 实现

例子1:通过修改源码实现c++调用js获取URL

 例子2:从c++ 传递数据 到 js 

1)EM_ASM 实现:

2)在HTML5JavaScriptFx 中新增方法


一、js 调 C++

1.c++声明:

//声明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.修改HTML5ToolChain.cs 文件

除了声明 extern "c" 的函数外,还需要在预编译配置里配置预编译文件,编译配置文件在 HTML5ToolChain.cs 文件,找到以下两行   

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

把声明的c++函数名字,添加到第一行,注意需在函数名称前加下横杠 ‘_’ 

在第二行中添加新的调用方法 ‘ccall’ ,修改后的如下:

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.修改project_template.js 文件
 

//找到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.调用

当完成上面的三个步骤,c++就可以通过js 调用了

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

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

二、C++ 调用 js

1.内联 

1)EM_JS调用:  

//声明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 有返回值方法

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.修改源码

1)在HTML5JavaScriptFx.h extern "c"中新增c++函数

2)在HTML5JavaScriptFx.js中新增函数的 js 实现

注意:js中的实现需要写在   $UE_JSlib

例子1:通过修改源码实现c++调用js获取URL

在HTML5JavaScriptFx.h中新增 void Get_URLData(const char* outdata, int outsize);

在HTML5JavaScriptFx.js中新增函数的 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++的时候,会出现乱码、或者信息不全的情况

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中获取到

 例子2:从c++ 传递数据 到 js 

1)EM_ASM 实现:

//在函数中调用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)在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('请求数据失败')
		}
	});
    
}

在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方法就已经完成了

注意:在cpp里调用js的方法,无论是哪一种,都必须要包含下面的头文件


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

而且在写的时候,如果调用到 js的方法,就必须要这样做,否则在编译项目的时候,会报错,除非是直接打包HTML5

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

以上是UE4 c++ 与 js  常用的互调方法,在引擎里都能找到,我这里是根据我自己的项目做一个整理和总结,分享给大家,希望能帮到大家,如果有错误,希望指正

猜你喜欢

转载自blog.csdn.net/acdfg123/article/details/118932939
今日推荐