一,示例js绑定c++的过程,实现在js中设定应用程序的“设计分辨率”以及在c++中调用js脚本。
二,手动绑定入口
新建一个.h文件,此处定义为“JsbUtil.h”,定义函数:void register_jsb_util(JSContext *cx, JSObject *global);
参数含义简介:
cx:
几乎所有的js api都要求我们传递一个上下文做为参数。在cocos2d-x中的js上下文在类ScriptingCore中定义:rt_。它就像是一台小机器,它涉及JavaScript代码和对象的很 多东西。它可以编译和执行脚本、获取和设置对象属性、调用JavaScript函数、一种类型转换为另一种JavaScript数据、创建对象,等等。
global:
全局js对象,全局对象包含所有可以在JavaScript代码中使用 的类、函数和变量。
三,实现绑定入口函数
新建一个.cpp文件,此处命名为“JsbUtil.cpp”。
首先声明两个变量:
JsClass *js_util_class; //要和c++绑定的js类
JsObject *js_util_prototype; //在创建上述class类时的原型
第二步中声明函数的实现如下:
/*
此函数做的事情:
1,为js_util_class分配空间
2,设置js_util_class的各个属性
3,设置要绑定的函数
4,获取js_util_class的原型
5,将js_util_class注册到全局属性中
*/
void register_jsb_util(JSContext *cx, JSObject *global)
{
//js class的属性:https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/JSClass
js_util_class = (JSClass*)calloc(1, sizeof(JSClass));
js_util_class->name = "APP_UTIL";//class 的name属性,这样可以在js脚本中直接new APP_UTIL()来使用此类
js_util_class->addProperty = JS_PropertyStub;//在添加新属性后被调用的函数。默认使用“JS_PropertyStub”
js_util_class->delProperty = JS_PropertyStub; //删除属性时被调用的函数。默认使用“JS_PropertyStub”
js_util_class->getProperty = JS_PropertyStub; //获取一个属性时调用的函数。这是js类的默认获取属性的方法。默认使用“JS_PropertyStub”。
js_util_class->setProperty = JS_StrictPropertyStub; //在一个脚本中新建一个属性后它会在调用“addProperty”之后被调用。默认使用“JS_StrictPropertyStub”。
js_util_class->enumerate = JS_EnumerateStub; //枚举(enumrate)对象属性的方法。默认使用“JS_EnumerateStub”。
js_util_class->resolve = JS_ResolveStub; //分解类中属性的函数
js_util_class->convert = JS_ConvertStub; //对类中属性值进行转换的函数
js_util_class->finalize = js_utility_finalize; //类的对象释放时调用的析构函数,<span style="font-family: Arial, Helvetica, sans-serif;">需要声明到此函数的前面,下一步实现</span>
//类的属性(成员变量)。0表明属性不是一 个集合.JS_HAS_PRIVATE: 类可以使用私有数据.JS_NEW_ENUMERATE: 返回由类定义的getObjectOps方法获取所有属性的一个新方法.JS_NEW_RESOLVE: 返回由类定义的 getObjectOps方法获取属性值的一个新方法
js_util_class->flags = JSCLASS_HAS_RESERVED_SLOTS(2);
//以上部分创建了一个js类,定义其属性:name,addProperty,delProperty, getProperty, setProperty, enumerate, resolve, convert, finalize, flags.
//要注册的属性
//struct JSPropertySpec {
// const char *name; //Name to assign the property
// int8 tinyid; //Obsolete since JSAPI 31 Unique ID number for the property to aid in resolving getProperty and setProperty method calls.
//This value should be zero if you are not using tinyIDs (i.e. the getter/setter function is only used by one property).
// uint8 flags; //Property attributes.
// JSPropertyOp getter; //Getter method for the property.
// JSPropertyOp setter; //Setter method for the property. If a property is read-only, it's setter is never called.
//};
static JSPropertySpec properties[] = {
{0, 0, 0, 0, 0}
};
//实例函数
//struct JSFunctionSpec {
// const char *name; //在js脚本中调用的函数名字;
// JSNative call; //映射的本地函数名称
// uint16 nargs; //The value used for Function.length. This no longer guarantees anything about the vp array.
// uint16 flags; //The bitwise OR of any number of property attributes and function flags, and optionally JSFUN_STUB_GSOPS.
// uint32 extra; //JSAPI 1.8 and earlier The lower 16 bits indicate the number of extra local roots the function desires,
//available at argv[MAX(nargs, argc)] onward. The upper 16 bits must be zero and are currently reserved.
//In older versions of SpiderMonkey, this field was a uint16 required to be 0.
//};
//#define JS_FN(name,call,nargs,flags) \
//{name, JSOP_WRAPPER(call), nargs, (flags) | JSFUN_STUB_GSOPS}
//#define JS_FS_END JS_FS(NULL,NULL,0,0)
static JSFunctionSpec funcs[] = {
JS_FN("setDesignResolution", js_util_set_design_resolution, 3, JSPROP_ENUMERATE|JSPROP_PERMANENT),
JS_FS_END
};
//类函数:看cocos2dx中的绑定此处绑定的是类的静态函数
// static JSFunctionSpec st_funcs[] = {
// JS_FN("create", js_cocos2dx_CCNode_create, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),
// JS_FS_END
// };
static JSFunctionSpec st_funcs[] = {
JS_FS_END
};
//初始化js类
//JS_InitClass : Make a JSClass accessible to JavaScript code by creating its prototype, constructor, properties, and functions.
// JSObject * JS_InitClass(JSContext *cx, //Pointer to a JS context from which to derive runtime information. Requires request.
//In a JS_THREADSAFE build, the caller must be in a request on this JSContext.
// JSObject *obj, //Pointer to the "globals" object to use for initializing the class.
// JSObject *parent_proto,// Pointer to an object to be used as a prototype
// JSClass *clasp, //Pointer to the class structure to initialize. This structure defines the class for use by other API functions.
// JSNative constructor,//The constructor for the class. Its scope matches that of the obj argument.
//If constructor is NULL, then static_ps and static_fs must also be NULL.
// uintN nargs, //Number of arguments for the constructor.
// JSPropertySpec *ps, //Either NULL or a pointer to the first element of an array of JSPropertySpecs,
//terminated by the null JSPropertySpec, which can be written {0, 0, 0, 0, 0}. 要注册的属性
// JSFunctionSpec *fs, //Either NULL or a pointer to the first element of an array of JSFunctionSpecs, terminated by JS_FS_END.
// 要注册的函数
// JSPropertySpec *static_ps, //Either NULL or a pointer to the first element of an array of JSPropertySpecs, terminated by the null JSPropertySpec.
// JSFunctionSpec *static_fs);
js_util_prototype = JS_InitClass(cx,
global,
NULL,
js_util_class,
js_util_constructor,//需要声明到此函数的前面,下一步实现
0,
properties,
funcs,
NULL,
st_funcs);
//注册到全局变量中
JSBool found;
JS_SetPropertyAttributes(cx, global, "APP_UTIL", JSPROP_ENUMERATE|JSPROP_READONLY, &found);
}
四,定义js类的析构和构造函数
析够函数定义如下:
void js_utility_finalize(JSFreeOp *op, JSObject *obj)
{
CCLOG("js_util_class is free : %p", obj);
}
构造函数定义如下:
JSBool js_util_constructor(JSContext *cx, uint32_t argc, jsval *vp)
{
if (argc != 0) {
JS_ReportError(cx, "Wrong number of args: %d, was expecting: %d", argc, 0);
return JS_FALSE;
}
JSObject *obj = JS_NewObject(cx, js_util_class, js_util_prototype, NULL);
UtilHelper *cobj = new UtilHelper();//绑定的本地c++类,下一步实现
//Link the native object with the javascript object
js_proxy_t *p = jsb_new_proxy(cobj, obj);
JS_AddNamedObjectRoot(cx, &p->obj, "APP_UTIL");
//设置js构造函数的返回值为obj
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
return JS_TRUE;
}
五,本地c++相关代码(UtilHelper)的实现
在此简略实现,直接定义在“JsbUtil.cpp”文件中,现在只实现一个功能:设置项目的分辨率解决方案,如下:
class UtilHelper
{
public:
void setDesignResolutionSize(float width, float height, ResolutionPolicy resolutionPolicy)
{
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(width, height, resolutionPolicy);
}
};
六,实现绑定函数js_util_set_design_resolution
/*
绑定设置designResolution的函数
根据当前的js object获取到其绑定的c++对象,再通过c++对象调用c++对象中的方法
*/
JSBool js_util_set_design_resolution(JSContext *cx, uint32_t argc, jsval *vp)
{
if (argc != 3) {
JS_ReportError(cx, "Wrong number of argc:%d, was expecting: %d", argc, 3);
return JS_FALSE;
}
JSObject *obj_this = JS_THIS_OBJECT(cx, vp);
js_proxy_t *proxy = jsb_get_js_proxy(obj_this);
UtilHelper *cobj = (UtilHelper*)(proxy?proxy->ptr:NULL);
JSB_PRECONDITION2(cobj, cx, JS_FALSE, "Invalid Native object");
jsval *argv = JS_ARGV(cx, vp);
int width = 0;
if (JSVAL_IS_INT(argv[0])) {
jsval_to_int32(cx, argv[0], &width);
}
int height = 0;
if (JSVAL_IS_INT(argv[1])) {
jsval_to_int32(cx, argv[1], &height);
}
std::string data;
if(JSVAL_IS_STRING(argv[2])){
jsval_to_std_string(cx, argv[2], &data);
}
ResolutionPolicy policy = kResolutionUnKnown;
if (data == std::string("kResolutionExactFit")) {
policy = kResolutionExactFit;
}else if(data == std::string("kResolutionNoBorder")){
policy = kResolutionNoBorder;
}else if(data == std::string("kResolutionFixedHeight")){
policy = kResolutionFixedHeight;
}else if(data == std::string("kResolutionFixedWidth")){
policy = kResolutionFixedWidth;
}else if(data == std::string("kResolutionShowAll")){
policy = kResolutionShowAll;
}
cobj->setDesignResolutionSize(width, height, policy);
return JS_TRUE;
}
新建test_util.js文件放在Resource/script目录下,添加如下代码:
require("jsb.js")
var director = cc.Director.getInstance();
var winSize = director.getWinSize();
var scene = cc.Scene.create();
var testSprite = cc.Sprite.create('HelloWorld.png'); //使用系统自带的图片,分辨率为480x320
scene.addChild(testSprite);
testSprite.setPosition(cc.p(winSize.width/2, winSize.height/2));
director.runWithScene(scene);
修改AppDelegate.cpp:
1,添加绑定:sc->addRegisterCallback(register_jsb_util);
2,修改执行的脚本为test_util.js:ScriptingCore::getInstance()->runScript("script/test_util.js");
之后使用480x320的模拟器效果图如下:
切换到分辨率大的模拟器后不能再全屏:
在require("jsb.js")下添加如下代码:
var util = new APP_UTIL();
util.setDesignResolution(480, 320, 'kResolutionFixedHeight');
之后运行效果如下:
九,添加c++回调脚本的功能
在UtilHelper中添加如下代码:
void CallBackJs()
{
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
JSObject *jsobj = JS_NewObject(cx, NULL, NULL, NULL);
// 组织传递给脚本的参数
int attr1Val = 1;
jsval vp;
vp = int32_to_jsval(cx, attr1Val);
JS_SetProperty(cx, jsobj, "myAttr1", &vp);
jsval args = OBJECT_TO_JSVAL(jsobj);
//转给脚本
ScriptingCore::getInstance()->executeFunctionWithOwner(OBJECT_TO_JSVAL(m_pJsDelegate), "onTestCallJs", 1, &args);
}
void SetJsDelegate(JSObject *pDelegate) {
m_pJsDelegate = pDelegate;
}
private:
JSObject *m_pJsDelegate;
在方法setDesignResolutionSize调用callBackJs
void setDesignResolutionSize(float width, float height, ResolutionPolicy resolutionPolicy)
{
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(width, height, resolutionPolicy);
CallBackJs();
}
在js_util_constructor中设置util关联的js类:
JSBool js_util_constructor(JSContext *cx, uint32_t argc, jsval *vp)
{
if (argc != 0) {
JS_ReportError(cx, "Wrong number of args: %d, was expecting: %d", argc, 0);
return JS_FALSE;
}
JSObject *obj = JS_NewObject(cx, js_util_class, js_util_prototype, NULL);
UtilHelper *cobj = new UtilHelper();
cobj->SetJsDelegate(obj); //添加此句
.....
}
在test_util.js脚本中实现onTestCallJs:
var util = new APP_UTIL();
util.onTestCallJs = function(arg) {
cc.log('call by c++, arg is = ' + JSON.stringify(arg));//打印出c++传递来的参数
};
再次运行程序,在xcode的输出窗口可以看到如下log:
十,至此,js\C++手动绑定,混合调用示例书写完毕。
相关参考:
JavaScript-C/C++ (SpiderMonkey) 引擎嵌入开发指南(中文向导)
JSAPI_reference