目录
3)EM_ASM_INT、EM_ASM_DOUBLE 有返回值方法
1)在HTML5JavaScriptFx.h extern "c"中新增c++函数
2)在HTML5JavaScriptFx.js中新增函数的 js 实现
一、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 常用的互调方法,在引擎里都能找到,我这里是根据我自己的项目做一个整理和总结,分享给大家,希望能帮到大家,如果有错误,希望指正