【cocos2d-x从c++到js】09:JS与C++的交互1——JS代码调用C++代码

简介:

之前我们讲过,在游戏启动时,我们要通过SpiderMonkey引擎的注册接口,向SpiderMonkey注册相应的从C++到JS的绑定函数,这些函数用于把JS函数调用代码转换成对应C++函数调用来执行。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//在AppDelegate::applicationDidFinishLaunching函数中
     ScriptingCore* sc = ScriptingCore::getInstance();
     sc->addRegisterCallback(register_all_cocos2dx);
     sc->addRegisterCallback(register_all_cocos2dx_extension);
     sc->addRegisterCallback(register_cocos2dx_js_extensions);
     sc->addRegisterCallback(register_all_cocos2dx_extension_manual);
     sc->addRegisterCallback(jsb_register_chipmunk);
     sc->addRegisterCallback(JSB_register_opengl);
     sc->addRegisterCallback(jsb_register_system);
     sc->addRegisterCallback(MinXmlHttpRequest::_js_register);
     sc->addRegisterCallback(register_jsb_websocket);
     sc->addRegisterCallback(register_all_cocos2dx_builder);
     sc->addRegisterCallback(register_CCBuilderReader);
     sc->addRegisterCallback(register_all_cocos2dx_gui);
     sc->addRegisterCallback(register_all_cocos2dx_gui_manual);
     sc->addRegisterCallback(register_all_cocos2dx_studio);
     sc->addRegisterCallback(register_all_cocos2dx_studio_manual);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
     sc->addRegisterCallback(register_all_cocos2dx_spine);


可以看到上面导入了Cocos2d-x的各种库,核心库,扩展,opengl,物理引擎,websocket,CCB等等等等。


下面我们说JS代码如何调用C++代码。

首先,在创建JS对象的时候,也会创建一个对应的C++对象。换句话说,JS对象是和C++对象一一对应的(当然必须是引擎支持的,而且绑定了接口的)。然后,在JS对象执行函数时,发生了什么呢?SpiderMonkey引擎会通过注册的接口,找到对应的C++对象,调用该对象上对应的C++函数。


换句话说,如果有下面的JS代码:

1
2
var  node = cc.Node.create();
node.setVisible( false );


那么经过SpiderMonkey执行后,会调用下面的代码:

1
2
auto  node = CCNode::create();
node->setVisible( false );


当然,SpiderMonkey远远还不止干了这些,还做了很多事,比如绑定和查找JS和C++对象的对应关系,包装参数为对应类型,类型安全检查,返回值包装等等。要知道他干了些什么,直接看引擎代码是更好的选择。


在Cocos2d-x 3.0版的引擎中,引擎目录结构进行了大规模重构。

wKioL1LeN2WTLO7WAABQNtZHSLU725.jpg

两个脚本语言被放到一个类似的目录中。其中auto-generated/js-bindings文件夹是gxx-generator工具自动生成的所有C++绑定JS代码。而javascript/bingdings文件夹是手写的绑定代码,因为工具无法做到完全自动绑定,所以必须有一部分手写的(脚本语言都是这样,习惯就好了,谢谢)。


好,我们继续找刚才说的源代码。打开jsb_cocos2dx_auto.cpp

   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
JSBool js_cocos2dx_Node_create(JSContext *cx, uint32_t argc, jsval *vp)
{
     if  (argc == 0) {
         cocos2d::Node* ret = cocos2d::Node::create();
         jsval jsret = JSVAL_NULL;
         do  {
         if  (ret) {
             js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::Node>(cx, (cocos2d::Node*)ret);
             jsret = OBJECT_TO_JSVAL(proxy->obj);
         else  {
             jsret = JSVAL_NULL;
         }
     while  (0);
         JS_SET_RVAL(cx, vp, jsret);
         return  JS_TRUE;
     }
     JS_ReportError(cx,  "js_cocos2dx_Node_create : wrong number of arguments" );
     return  JS_FALSE;
}


这就是cc.Node.create()执行时,底层C++跑的代码。所有的通过JS调用C++的代码都与这个形式非常一致,首先看函数接口:

第一个参数JSContext *cx是JS的上下文

第二个参数uint32_t argc是JS代码中的参数个数,在这个里argc==0

第三个参数jsval *vp是JS代码中的具体参数


继续分析

1
cocos2d::Node* ret = cocos2d::Node::create();

这个代码再熟悉不过了,标准的Cocos2d-x静态工场生成对象的代码


1
jsval jsret = JSVAL_NULL;

jsval jsret是这个函数的返回值,这是表示的是一个JS对象


1
2
js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::Node>(cx, (cocos2d::Node*)ret);
jsret = OBJECT_TO_JSVAL(proxy->obj);

注意这个模板函数,get_or_create,这就是把JS对象和C++对象绑到一起的函数。他非常重要,注意JS和C++对象是一一对应关系,理解这个特效,有助于我们利用JS语言的动态性进行更方便的编程。绑完之后,下面那个函数是用于获得返回值。


最后,函数都要返回一个JSBool,表面这个函数执行是否成功。如果返回JS_FALSE,还会通过JS_ReportError打印一条报错信息。注意!脚本语言有一个特点,如果函数运行失败了,则该函数后面的函数(在同一作用域中的)都会跳过执行。


继续看下一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
JSBool js_cocos2dx_Node_setVisible(JSContext *cx, uint32_t argc, jsval *vp)
{
     jsval *argv = JS_ARGV(cx, vp);
     JSBool ok = JS_TRUE;
     JSObject *obj = JS_THIS_OBJECT(cx, vp);
     js_proxy_t *proxy = jsb_get_js_proxy(obj);
     cocos2d::Node* cobj = (cocos2d::Node *)(proxy ? proxy->ptr : NULL);
     JSB_PRECONDITION2( cobj, cx, JS_FALSE,  "js_cocos2dx_Node_setVisible : Invalid Native Object" );
     if  (argc ==  1 ) {
         JSBool arg0;
         ok &= JS_ValueToBoolean(cx, argv[ 0 ], &arg0);
         JSB_PRECONDITION2(ok, cx, JS_FALSE,  "js_cocos2dx_Node_setVisible : Error processing arguments" );
         cobj->setVisible(arg0);
         JS_SET_RVAL(cx, vp, JSVAL_VOID);
         return  JS_TRUE;
     }
     JS_ReportError(cx,  "js_cocos2dx_Node_setVisible : wrong number of arguments: %d, was expecting %d" , argc,  1 );
     return  JS_FALSE;
}

这个函数和前一个函数的区别是,这个函数有参数,并且他是一个类成员函数(上一个是类静态函数),所以,这里要有this指针。

1
2
3
4
5
6
jsval *argv = JS_ARGV(cx, vp);
JSBool ok = JS_TRUE;
JSObject *obj = JS_THIS_OBJECT(cx, vp);
js_proxy_t *proxy = jsb_get_js_proxy(obj);
cocos2d::Node* cobj = (cocos2d::Node *)(proxy ? proxy->ptr : NULL);
JSB_PRECONDITION2( cobj, cx, JS_FALSE,  "js_cocos2dx_Node_setVisible : Invalid Native Object" );

这一大段函数都在找那个this指针。注意,这里面有一个Cocos2d-x引擎经常出现的错误提示Invalid Native Object。底层C++对象被回收了,所以找不到了。


1
2
3
4
5
6
7
8
if  (argc == 1) {
     JSBool arg0;
     ok &= JS_ValueToBoolean(cx, argv[0], &arg0);
     JSB_PRECONDITION2(ok, cx, JS_FALSE,  "js_cocos2dx_Node_setVisible : Error processing arguments" );
     cobj->setVisible(arg0);
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return  JS_TRUE;
}

CCNode::setVisible(xx)只有一个参数,所以先判断JS的参数个数为1。JS_ValueToBoolean完成JS对象到C++对象的转换,注意!这是基本类型的转换,和查找对应的对象指针不同。你在gxx-generator生成的代码中会看到大量的这种转换。每次转换都要进行结果判断,如果失败,就打印错误信息。后面是直接调用对应C++对象的setVisible,以及设置返回值。


很繁琐不是吗?如果这种代码全部手写是不是会死人呢。肯定的吧。所以这些代码都是用脚本生成器做出来的(绝大部分)。



后面我们会继续讲解各种JS的绑定代码。






 本文转自 老G 51CTO博客,原文链接:http://blog.51cto.com/goldlion/1353583,如需转载请自行联系原作者


相关文章
|
2月前
|
算法框架/工具 C++ Python
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
117 0
|
2天前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
10 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
13天前
|
C++
继续更新完善:C++ 结构体代码转MASM32代码
继续更新完善:C++ 结构体代码转MASM32代码
|
13天前
|
C++ Windows
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
|
13天前
|
C++
2合1,整合C++类(Class)代码转换为MASM32代码的平台
2合1,整合C++类(Class)代码转换为MASM32代码的平台
|
13天前
|
C++
HTML+JavaScript构建一个将C/C++定义的ANSI字符串转换为MASM32定义的DWUniCode字符串的工具
HTML+JavaScript构建一个将C/C++定义的ANSI字符串转换为MASM32定义的DWUniCode字符串的工具
|
13天前
|
前端开发 C++ Windows
C++生成QML代码与QML里面集成QWidget
这篇文章介绍了如何在C++中生成QML代码,以及如何在QML中集成QWidget,包括使用Qt Widgets嵌入到QML界面中的技术示例。
|
1月前
|
JavaScript 前端开发
JavaScript 与 DOM 交互
【9月更文挑战第01天】
27 2
|
2月前
|
JavaScript 前端开发 UED
Vue.js动画魔法:解锁流畅过渡,让每一次交互都成为用户心中的小确幸!
【8月更文挑战第30天】在Vue.js中,动画与过渡效果不仅是视觉点缀,更是提升用户体验的关键。通过流畅的动态效果,应用的互动性和吸引力得以增强,从而提高用户满意度和参与度。`&lt;transition&gt;`和`&lt;transition-group&gt;`组件结合CSS过渡,可轻松实现元素的进入、离开及列表变化动画。合理的性能优化,如使用硬件加速,能避免页面卡顿,确保动画既美观又高效。下面是一个简单的淡入淡出效果示例,展示了如何利用Vue.js实现平滑的动画过渡。总之,恰当的动画设计能显著提升应用的用户体验。
43 0
Vue.js动画魔法:解锁流畅过渡,让每一次交互都成为用户心中的小确幸!
|
2月前
|
Devops 持续交付 测试技术
JSF遇上DevOps:开发流程将迎巨变?一篇文章带你领略高效协同的魅力!
【8月更文挑战第31天】本文探讨了如何在JavaServer Faces(JSF)开发中融入DevOps文化,通过持续集成与部署、自动化测试、监控与日志记录及反馈机制,提升软件交付速度与质量。文中详细介绍了使用Jenkins进行自动化部署、JUnit与Selenium进行自动化测试、ELK Stack进行日志监控的具体方法,并强调了持续改进的重要性。
31 0