android 脚本引擎,玩转 Cocos2d-x 脚本引擎

本文介绍了在Cocos2d-x项目中同时运行Lua和JavaScript引擎的实践过程,通过修改源代码以解决类型冲突问题。作者详细阐述了对Cocos2d-x脚本引擎运行机制的理解,探讨了脚本绑定技术的特性,并展示了如何在不同脚本之间控制同一游戏元素。此外,还提到了Cocos2d-x的内存管理和不同脚本语言的异同。
摘要由CSDN通过智能技术生成

在 Unix 文化中,有这样一种理念,Happy Hacking!使用 Cocos2d-x/C++ 写过一些游戏,其绑定的脚本语言,用的也不少,脚本语言的一个好处就是快速开发,你无需明白它之运行机理,便可容易的完成所想要的效果,三天上手,五天就能写出像样的程序来,C++ 则不然,其各种语言细节特性,各种开发技巧,内存管理等细枝末叶 ~

计算机不会魔法,在一叶看来其内容,只有 “知、或者不知” ,没有 “懂、或者不懂”,“知或者不知”来自于你的学习历程与经验,至于“不懂”么,我还没接触到的领域内容,我都不懂,哈 ~ Cocos2d-x 脚本引擎也用过一段时间了,但其运行机理还不明白,就使用而言也无需明白,不过于在意细节的实现,可能更好的从宏观角度把握整体。过去只是对其“存疑”(对于这里的“不懂”,一叶通常美其名曰:要学会存疑 :p ),而现在想要对其运行机制多了解一些,那就只有一步步去探究喽,要了解到什么程度,那就随意了~

对 Cocos2d-x 的运行机制只是略懂一二,C++ 的场景由 C++ 运行,脚本呢,先开启脚本引擎,让后将控制权交由脚本代码执行,在这过程中发生了什么,由脚本所控制的元素和 C++ 有什么不同,或者说它的本质是什么!这之前一叶一直说的是脚本引擎,而非具体那种脚本引擎,lua 或者 js (jsvascript)的引擎实现!凭着对已有知识的了解和直觉,很多疑问和可能性随之而来,它所支持的脚本语言有两种,此两种的共同之处是什么,其使用了脚本绑定技术,什么是绑定 ?各种对象在内存中如何分布,如何配合在一起工作。

为了增加探究过程的趣味性,所以一叶试想着能不能让 Cocos2d-x 现今所支持的两种脚本引擎同时运行 (lua and js),然后确定是否能在三者之间(C++, js, lua)访问同一个内存元素,如果行,便弄出来,即便不能做到,那也无所谓,这其中的过程比结果更有意思,不是吗 ~

两种脚本引擎同时运行

这里使用了 cocos2d-x-3.0alpha0-pre 版本,原因有二。一者:这是最新版本,反正是折腾,顺便了解一下 3.0 的新特性和 代码 style ,其二:3.0 对 三大开发平台(windows, linux ,mac),两大运行平台(android, ios)的支持更好更全一些,比如,lua 可以跑在 mac 上面,这一点最新的 2.2 版本不行(lua )。这样的选择,可以让我在当前系统(Mac OS X) 系统下,直接运行看效果,而不用开模拟器或者虚拟机,使过程更为方便。(过去使用 Linux 作为开发环境,也很方便,一叶的博客也有其具体的开发环境搭建配置等)而 windows 系统,几年前就几乎不怎么用了,各种不顺手。

3.0 中去除了使用项目模板来构建项目,而改为使用脚本创建,支持的平台如下(这个脚本是 github 上最新的版本),观其关键代码:

// [Cocos2d-x]/tools/project-creator/create_project.py

PLATFORMS = {

"cpp": ["ios_mac","android","win32","linux"],

"lua": ["ios_mac","android","win32","linux"],

"javascript": ["ios_mac","android","win32"]

}

从这里脚本看出 cpp 和 lua ,对五个平台已经全面支持,javascript 对 linux 还没有支持(脚本上是这样),相比 -x 2.x 版本支持更好,更全面,其代码也经过重构,更模块化,还有很多 C++ 11 的新特性,这里同样也期待 3.0 早日成熟,达到实用阶段 :p

为了简化操作步骤,一叶尽量利用现有的环境内容,使用 XCode打开 samples 项目,这里包含了所有项目内容,起初一叶打算基于 HelloCpp 项目做扩展,添加脚本支持,来实现自己的功能,也许是我对 XCode 环境了解不够,通过手动配置,让它能够支持 lua 扩展,但是 js 扩展却没能跑起来(没理由 lua 可以 js 却不行,知道一定是哪里配置有误),此时看来,在已有的项目中添加 lua 比 js 成功几率要大,固转而使用 HelloLua 项目添加 js 扩展支持,以达到项目组织上能够独立运行 js 引擎 或者 lua 引擎。(注:使用 HelloCpp 作为扩展,主要想看看怎么配置方便,如果都好配置,就基于 Cpp ,如果其中一个配置难,或者没通过,就基于没有通过配置的已有实现,扩展它。比如,这里 lua 通过 js 却没有,那就基于已有的 HelloLua 扩展支持 js,更为节省精力,这是策略问题 :p)

以上只是让项目同时支持 js 或者 lua 的运行,比如只跑 lua,或者只跑 js,但是 两者却不能同时跑,如果在代码中,同时用到了 lua 和 js 的支持库,在编译时会报错。这是因为在绑定的时候,两种脚本引擎分别实现了自己的 GLNode 。

// [Cocos2d-x]/scripting/javascript/bindings/js_bindings_opengl.h

#include "cocos2d.h"

#include "ScriptingCore.h"

#include "cocos2d_specifics.hpp"

classGLNode :publiccocos2d::Node {

public:

voiddraw();

};

voidjs_register_cocos2dx_GLNode(JSContext *cx, JSObject *global);

// [Cocos2d-x]/scripting/lua/cocos2dx_support/LuaOpengl.h

#ifndef __LUA_OPENGL_H__

#define __LUA_OPENGL_H__

#ifdef __cplusplus

extern "C"{

#endif

#include "tolua++.h"

#ifdef __cplusplus

}

#endif

#include "base_nodes/CCNode.h"

classGLNode:publiccocos2d::Node

{

virtual voiddraw();

};

TOLUA_API inttolua_Cocos2d_CCDrawNode_drawPolygon00(lua_State* tolua_S);

TOLUA_API inttolua_opengl_open(lua_State* tolua_S);

#endif //__LUA_OPENGL_H__

同时使用两种引擎,就意味着同时用到两者的库依赖,而重复的类型定义导致编译通不过,所以只能根据需要 hack 源码了,如果修改,二者修改其一,看修改哪个方便,引用的地方少。如下是使用 Emacs 的 find-grep 命令,在 scripting 目录搜索 “GLNode” 关键字的结果。(Emacs 是一叶的必备工具,这里搜索出的结果可以快速定位代码位置)

-*- mode: grep;default-directory:"~/Tools/cocos2d-x/cocos2d-x-3.0alpha0-pre/scripting/"-*-

Grep started at Mon Nov 11 13:55:04

find . -type f -exec grep -nH -e GLNode {} +

./javascript/bindings/js/jsb_cocos2d_extension.js:167:cc.GLNode.extend = cc.Class.extend;

./javascript/bindings/js_bindings_opengl.cpp:3:voidGLNode::draw() {

./javascript/bindings/js_bindings_opengl.cpp:27:JSClass  *js_cocos2dx_GLNode_class;

./javascript/bindings/js_bindings_opengl.cpp:28:JSObject *js_cocos2dx_GLNode_prototype;

./javascript/bindings/js_bindings_opengl.cpp:30:JSBool js_cocos2dx_GLNode_constructor(JSContext *cx, uint32_t argc, jsval *vp)

./javascript/bindings/js_bindings_opengl.cpp:34:    GLNode* cobj = newGLNode();

./javascript/bindings/js_bindings_opengl.cpp:41:    TypeTest t;

./javascript/bindings/js_bindings_opengl.cpp:51:    JS_AddNamedObjectRoot(cx, &p->obj, "cocos2d::GLNode");

./javascript/bindings/js_bindings_opengl.cpp:59:voidjs_cocos2dx_GLNode_finalize(JSFreeOp *fop, JSObject *obj) {

./javascript/bindings/js_bindings_opengl.cpp:62:staticJSBool js_cocos2dx_GLNode_ctor(JSContext *cx, uint32_t argc, jsval *vp)

./javascript/bindings/js_bindings_opengl.cpp:65:    GLNode *nobj = newGLNode();

./javascript/bindings/js_bindings_opengl.cpp:68:    JS_AddNamedObjectRoot(cx, &p->obj, "GLNode");

./javascript/bindings/js_bindings_opengl.cpp:73:JSBool js_cocos2dx_GLNode_create(JSContext *cx, uint32_t argc, jsval *vp)

./javascript/bindings/js_bindings_opengl.cpp:75:  GLNode* ret = newGLNode();

./javascript/bindings/js_bindings_opengl.cpp:79:      js_proxy_t *proxy = js_get_or_create_proxy(cx, ret);

./javascript/bindings/js_bindings_opengl.cpp:91:voidjs_register_cocos2dx_GLNode(JSContext *cx, JSObject *global) {

./javascript/bindings/js_bindings_opengl.cpp:92:  js_cocos2dx_GLNode_class = (JSClass *)calloc(1, sizeof(JSClass));

./javascript/bindings/js_bindings_opengl.cpp:93:  js_cocos2dx_GLNode_class->name = "GLNode";

./javascript/bindings/js_bindings_opengl.cpp:94:  js_cocos2dx_GLNode_class->addProperty = JS_PropertyStub;

./javascript/bindings/js_bindings_opengl.cpp:95:  js_cocos2dx_GLNode_class->delProperty = JS_PropertyStub;

./javascript/bindings/js_bindings_opengl.cpp:96:  js_cocos2dx_GLNode_class->getProperty = JS_PropertyStub;

./javascript/bindings/js_bindings_opengl.cpp:97:  js_cocos2dx_GLNode_class->setProperty = JS_StrictPropertyStub;

./javascript/bindings/js_bindings_opengl.cpp:98:  js_cocos2dx_GLNode_class->enumerate = JS_EnumerateStub;

./javascript/bindings/js_bindings_opengl.cpp:99:  js_cocos2dx_GLNode_class->resolve = JS_ResolveStub;

./javascript/bindings/js_bindings_opengl.cpp:100:  js_cocos2dx_GLNode_class->convert = JS_ConvertStub;

./javascript/bindings/js_bindings_opengl.cpp:101:  js_cocos2dx_GLNode_class->finalize = js_cocos2dx_GLNode_finalize;

./javascript/bindings/js_bindings_opengl.cpp:102:  js_cocos2dx_GLNode_class->flags = JSCLASS_HAS_RESERVED_SLOTS(2);

./javascript/bindings/js_bindings_opengl.cpp:109:      JS_FN("ctor", js_cocos2dx_GLNode_ctor, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),

./javascript/bindings/js_bindings_opengl.cpp:114:    JS_FN("create", js_cocos2dx_GLNode_create, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE),

./javascript/bindings/js_bindings_opengl.cpp:118:  js_cocos2dx_GLNode_prototype = JS_InitClass(

./javascript/bindings/js_bindings_opengl.cpp:121:                         js_cocos2dx_GLNode_class,

./javascript/bindings/js_bindings_opengl.cpp:122:                         js_cocos2dx_GLNode_constructor, 0, // constructor

./javascript/bindings/js_bindings_opengl.cpp:129:  JS_SetPropertyAttributes(cx, global, "GLNode", JSPROP_ENUMERATE | JSPROP_READONLY, &found);

./javascript/bindings/js_bindings_opengl.cpp:132:  TypeTest t;

./javascript/bindings/js_bindings_opengl.cpp:139:    p->jsclass = js_cocos2dx_GLNode_class;

./javascript/bindings/js_bindings_opengl.cpp:140:    p->proto = js_cocos2dx_GLNode_prototype;

./javascript/bindings/js_bindings_opengl.h:5:classGLNode :publiccocos2d::Node {

./javascript/bindings/js_bindings_opengl.h:10:voidjs_register_cocos2dx_GLNode(JSContext *cx, JSObject *global);

./javascript/bindings/jsb_opengl_registration.cpp:56:    js_register_cocos2dx_GLNode(_cx, ccns);

./javascript/bindings/obfuscate/obfuscate_exclude_cocos2d.js:3049:CSSProperties.prototype.GLNode;

./lua/cocos2dx_support/CCLuaEngine.cpp:615:    extendGLNode(lua_S);

./lua/cocos2dx_support/CCLuaEngine.cpp:726:voidLuaEngine::extendGLNode(lua_State* lua_S)

./lua/cocos2dx_support/CCLuaEngine.cpp:731:    lua_pushstring(lua_S,"GLNode");

./lua/cocos2dx_support/CCLuaEngine.cpp:736:        lua_pushcfunction(lua_S,tolua_Cocos2d_GLNode_registerScriptDrawHandler00);

./lua/cocos2dx_support/CCLuaEngine.cpp:739:        lua_pushcfunction(lua_S,tolua_Cocos2d_GLNode_unregisterScriptDrawHandler00);

./lua/cocos2dx_support/CCLuaEngine.h:146:    voidextendGLNode(lua_State* lua_S);

./lua/cocos2dx_support/LuaOpengl.cpp:24:voidGLNode::draw()

./lua/cocos2dx_support/LuaOpengl.cpp:26:    inthandler = ScriptHandlerMgr::getInstance()->getObjectHandler((void*)this, ScriptHandlerMgr::kGLNodeDrawHandler);

./lua/cocos2dx_support/LuaOpengl.cpp:38:    tolua_usertype(tolua_S, "GLNode");

./lua/cocos2dx_support/LuaOpengl.cpp:42:staticinttolua_collect_GLNode (lua_State* tolua_S)

./lua/cocos2dx_support/LuaOpengl.cpp:44:    GLNode *self = (GLNode*) tolua_tousertype(tolua_S,1,0);

./lua/cocos2dx_support/LuaOpengl.cpp:63:/* method: create of class  GLNode */

./lua/cocos2dx_support/LuaOpengl.cpp:64:#ifndef TOLUA_DISABLE_tolua_Cocos2d_GLNode_create00

./lua/cocos2dx_support/LuaOpengl.cpp:65:staticinttolua_Cocos2d_GLNode_create00(lua_State* tolua_S)

./lua/cocos2dx_support/LuaOpengl.cpp:70:        !tolua_isusertable(tolua_S,1,"GLNode",0,&tolua_err) ||

./lua/cocos2dx_support/LuaOpengl.cpp:77:        GLNode *glNode = newGLNode();

./lua/cocos2dx_support/LuaOpengl.cpp:83:            toluafix_pushusertype_ccobject(tolua_S, nID, pLuaID, (void*)glNode,"GLNode");

./lua/cocos2dx_support/LuaOpengl.cpp:100:/* method: setShaderProgram of class  GLNode */

./lua/cocos2dx_support/LuaOpengl.cpp:101:#ifndef TOLUA_DISABLE_tolua_Cocos2d_GLNode_setShaderProgram00

./lua/cocos2dx_support/LuaOpengl.cpp:102:staticinttolua_Cocos2d_GLNode_setShaderProgram00(lua_State* tolua_S)

./lua/cocos2dx_support/LuaOpengl.cpp:107:        !tolua_isusertype(tolua_S,1,"GLNode",0,&tolua_err) ||

./lua/cocos2dx_support/LuaOpengl.cpp:115:        GLNode* self = (GLNode*)  tolua_tousertype(tolua_S,1,0);

./lua/cocos2dx_support/LuaOpengl.cpp:292:/* method: glBindFramebuffer of class  GLNode */

./lua/cocos2dx_support/LuaOpengl.cpp:300:        !tolua_isusertype(tolua_S,1,"GLNode",0,&tolua_err) ||

./lua/cocos2dx_support/LuaOpengl.cpp:6386:      tolua_cclass(tolua_S,"GLNode","GLNode","CCNode",tolua_collect_GLNode);

./lua/cocos2dx_support/LuaOpengl.cpp:6387:        tolua_beginmodule(tolua_S,"GLNode");

./lua/cocos2dx_support/LuaOpengl.cpp:6388:            tolua_function(tolua_S, "create", tolua_Cocos2d_GLNode_create00);

./lua/cocos2dx_support/LuaOpengl.cpp:6389:            tolua_function(tolua_S, "setShaderProgram", tolua_Cocos2d_GLNode_setShaderProgram00);

./lua/cocos2dx_support/LuaOpengl.h:13:classGLNode:publiccocos2d::Node

./lua/cocos2dx_support/LuaScriptHandlerMgr.cpp:584:inttolua_Cocos2d_GLNode_registerScriptDrawHandler00(lua_State* tolua_S)

./lua/cocos2dx_support/LuaScriptHandlerMgr.cpp:588:    if(!tolua_isusertype(tolua_S,1,"GLNode",0,&tolua_err) ||

./lua/cocos2dx_support/LuaScriptHandlerMgr.cpp:595:        GLNode* glNode = (GLNode*)  tolua_tousertype(tolua_S,1,0);

./lua/cocos2dx_support/LuaScriptHandlerMgr.cpp:597:        ScriptHandlerMgr::getInstance()->addObjectHandler((void*)glNode, handler, ScriptHandlerMgr::kGLNodeDrawHandler);

./lua/cocos2dx_support/LuaScriptHandlerMgr.cpp:608:inttolua_Cocos2d_GLNode_unregisterScriptDrawHandler00(lua_State* tolua_S)

./lua/cocos2dx_support/LuaScriptHandlerMgr.cpp:612:    if(!tolua_isusertype(tolua_S,1,"GLNode",0,&tolua_err) ||

./lua/cocos2dx_support/LuaScriptHandlerMgr.cpp:618:        GLNode* glNode = (GLNode*)tolua_tousertype(tolua_S,1,0);

./lua/cocos2dx_support/LuaScriptHandlerMgr.cpp:619:        ScriptHandlerMgr::getInstance()->removeObjectHandler((void*)glNode,ScriptHandlerMgr::kGLNodeDrawHandler);

./lua/cocos2dx_support/LuaScriptHandlerMgr.cpp:779:        tolua_constant(tolua_S,"kGLNodeDrawHandler",ScriptHandlerMgr::kGLNodeDrawHandler);

./lua/cocos2dx_support/LuaScriptHandlerMgr.h:99:       kGLNodeDrawHandler,

./lua/cocos2dx_support/LuaScriptHandlerMgr.h:137:TOLUA_API inttolua_Cocos2d_GLNode_registerScriptDrawHandler00(lua_State* tolua_S);

./lua/cocos2dx_support/LuaScriptHandlerMgr.h:138:TOLUA_API inttolua_Cocos2d_GLNode_unregisterScriptDrawHandler00(lua_State* tolua_S);

./lua/script/Opengl.lua:297:    returnGLNode:create()

Grep finished (matches found) at Mon Nov 11 13:55:07

目测感觉两者差不多,而且代码量也不多,所以修改其中之一的 GLNode 类名称是可行的,总共八十多行结果,修改一种就四十行左右,而且其中出现的 “GLNode” 关键字,并不是都需要修改,有的是绑定到脚本引擎内部的,我们只需要修改其 C++ 端绑定的类型就 OK 了,一叶修改了 lua 绑定中的 GLNode 名字为 LuaGLNode,然后定位到 LuaOpengl 和相关文件(两个文件左右),查找并替换其中一部分 “GLNode” 关键字代码,这里借助 Emacs ,递进式的关键字查询替换,通过匹配规则,一个个过滤,手动选择是替换还是不替换,最后替换了所有 C++ 中 Lua 端的 GLNode 类名实现(替换的内容很少,比我想象中要少)。

到这里就已经完成能够让两种脚本引擎同时运行的方法。并且一叶修改了入口函数,在 AppDelegate 入口处,添加修改:

bool AppDelegate::applicationDidFinishLaunching()

{

// initialize director

Director *pDirector = Director::getInstance();

pDirector->setOpenGLView(EGLView::getInstance());

EGLView::getInstance()->setDesignResolutionSize(480, 320, ResolutionPolicy::NO_BORDER);

// turn on display FPS

pDirector->setDisplayStats(true);

// set FPS. the default value is 1.0/60 if you don't call this

pDirector->setAnimationInterval(1.0 / 60);

//    runLua();

runCpp();

//    runJsb();

returntrue;

}

voidAppDelegate::runCpp(){

auto scene = HelloLeafsoar::scene();

Director::getInstance()->runWithScene(scene);

}

voidAppDelegate::runJsb(){

ScriptingCore* sc = ScriptingCore::getInstance();

sc->addRegisterCallback(register_all_cocos2dx);

sc->addRegisterCallback(register_all_cocos2dx_extension);

sc->addRegisterCallback(register_cocos2dx_js_extensions);

sc->addRegisterCallback(jsb_register_chipmunk);

sc->addRegisterCallback(register_all_cocos2dx_extension_manual);

sc->addRegisterCallback(register_CCBuilderReader);

sc->addRegisterCallback(jsb_register_system);

sc->addRegisterCallback(JSB_register_opengl);

sc->start();

ScriptEngineProtocol *pEngine = ScriptingCore::getInstance();

ScriptEngineManager::getInstance()->setScriptEngine(pEngine);

ScriptingCore::getInstance()->runScript("main.js");

}

voidAppDelegate::runLua(){

LuaEngine* luaEngine = LuaEngine::getInstance();

ScriptEngineManager::getInstance()->setScriptEngine(luaEngine);

std::string path = FileUtils::getInstance()->fullPathForFilename("hello.lua");

luaEngine->executeScriptFile(path.c_str());

}

可以看到这里有三个方法,runCpp 、runLua 和 runJsb,这三者可单独独立运行,也可同时运行,在脚本实现不同的 log 打印,便能知晓,但如如果你在这三处同时运行了一个场景的话,那么根据场景的运行规则,后面运行的场景会入栈,替换之前场景的运行,三者的运行顺序,可以随意修改,并观其运行结果。为了测试这一点,我们同时运行 runLua、runCpp 和 runJsb ,然后使用一个全局定时器(请看 实现 『Cocos2d-x 全局定时器』 一文),每三秒钟弹出一个场景。而每一个场景的内部不同,这样我们便能看见首先运行了 runJsb 场景,三秒后 runLua ,之后 runCpp 场景,最终三秒后,所有场景弹出,Game Over 了。

脚本绑定技术的特性

以上通过对源代码的修改,一个例子,让 lua 和 js 两种脚本引擎同时运行,由于在任何时候只能有一个场景运行,所以,无论由 C++、lua 还是 js 来启动游戏,另外两种语言将会得不倒执行的权利,但是从另一个侧面,全局定时器它得的运行并不依赖场景的运行,这对我们研究程序执行过程中对象的特性提供了方便,前文通过一个 C++ 端实现的全局定时器,不论你运行的是不是脚本,是什么脚本,都不影响它之运行,那么我们就能能用这个定时器去定期的调用各种脚本,以让这三种脚本语言同时运行,在这样一个过程中,去验证对它们的内存分布,操作机制的的情况等 ~

一叶在 C++ 端开启了一个定时器,且由 C++ 运行了第一个场景,然后修改全局定时器的定时调用实现,添加对 lua 和 js 的脚本调用:

voidGlobalSchedule::globalUpdate(floatdt) {

// 这里写全局定时器的逻辑处理代码

CCLOG("global update");

//    Director::getInstance()->popScene();

count ++;

auto scene = Director::getInstance()->getRunningScene();

LabelTTF* label = (LabelTTF*)scene->getChildByTag(100);

if(!label){

label = LabelTTF::create("一叶 v 5~","", 24);

label->setPosition(Point(250, 300));

label->setColor(Color3B::BLACK);

scene->addChild(label, 1, 100);

} else{

if(count % 3 == 0)

label->setScale(1);

elseif(count % 3 == 1)

ScriptingCore::getInstance()->runScript("jsbMethod.js");

elseif(count %3 == 2)

LuaEngine::getInstance()->executeScriptFile("luaMethod.lua");

}

}

这是全局定时器的实现,它在当前运行的场景中添加了一个 Label ,label 的内容 “一叶 v 5~“,并且通过一个执行技术 count 来决定当前执行的是 C++ 还是 lua 或者 js,在不同的语言中,修改同一个元素的大小Scale,看看 js 和 lua 的 method 文件实现:

-- luaMethod.lua 具体实现

cclog = function(...)

print(string.format(...))

end

cclog("lua method .");

local scene = CCDirector:getInstance():getRunningScene()

local label = scene:getChildByTag(100)

label:setScale(0.8)

// jsbMethod.js 具体实现

cc.log(" jsb method .");

varscene = cc.Director.getInstance().getRunningScene();

varlabel = scene.getChildByTag(100);

label.setScale(1.2);

以上的代码都很简单,各种语言的逻辑一样,首先或者当前运行着的场景,然后通过 tag 获取场景中的元素 Label,再之修改它的大小,这样将程序运行,便能看见此 Label 定时改变大小,而且是三种大小状态不停的切换,可见已经完成了我们之前的目标,双开脚本引擎,用不语言控制同一个元素(文章最后给出所有代码)。

绑定技术的特性浅析

说道脚本绑定技术,这里可以插入一个新的内容来说,Cocos2d-html5 版本!作为对比,更为明了,h5 的实现和 jsb 的实现显然不同,h5 是跑在浏览器上的,jsb 是跑在移动终端上的。但是它们都用统一的接口实现,即用 js 写的游戏(同一套代码)即能够跑在浏览器上,又能够能跑在手机的 js 引擎上,这两者之间 表面相同,本质不同 ,也是隐藏了内部实现,提供统一的接口让写程序更为简单。有兴趣的朋友可以去了解一下。在 Emacs 内置的 lisp 语言函数中有很多性能要求比较高的是使用 c 语言实现的,但在调用的时候全然不知(不知道就对了)。

而 jsb 和 lua 之间:知其不同,是见其表,知其皆同,是知其本,舍不同而观其同,可游心于物之初,哈。 在脚本引擎库中,以 C++ 实现的类型为基本,通过动态往虚拟机(引擎环境,或者上下文对象)里添加类型定义并绑定,每钟脚本类型都有其对应的 C++ 类型作为依据,通过脚本创建的对象最终被映射为调用 C++ 创建对象,而在 C++ 中创建的对象,也可以在脚本中随时获取,并修改其属性,当然其内部还有复杂的内存管理解决方案 (特别是本文中这样混合形的运行时环境,其对象生命周期就更复杂了,关系到引擎内部实现的细节,由不同语言创建的对象,由谁管理,由谁释放等等),在需要之时可以深究,而这里显然没有必要(这里引擎双开仅作学习之用),宏观角度考量,内存管理无非是定义一套规则,或是规范,这样能保证出错误的最小可能性(在 『Cocos2d-x 内存管理浅说』 , 『Cocos2d-x 内存管理的一种实现』, 『深入理解 Cocos2d-x 内存管理』) 几篇文章有怎样通过编程规范来尽量避免内存出现的问题)。

如果我们需要添加一种新脚本绑定实现,比如使用 lisp 语言作为绑定 (Emacs 用户首先想到的就是 lisp 了),那么我们需要一个 lisp 运行时环境的实现,然后通过函数绑定,javascript 和 lua 的第一类型是 函数(First-class Function),它们都有很强的函数式语言特性,其封装的 Cocos2d-x 调用方式不过是语法糖衣,看起来像面相对象而已,此点 js 表现更甚。所以对于 lisp 实现来说,是可行的,至于最后写起来是否顺手不得而知,目测如果实现,写法更像 lua 对 Cocos2d-x 的 style,可能很好使,可能很糟糕 :P 也许使用对象形的脚本语言更加合宜。

非吾小天下 宏观而已

想要玩转这里脚本引擎,那么你至少会绑定,知道怎么绑定,其具体步骤 ?jsb 怎么手动绑定,lua 怎么手动绑定,而且还有自动绑定脚本,jsb 的内部使用了 spidermonkey 开源的 js 引擎,lua 还可以开 jit,其运行环境有很多复杂的上下文参数,各种错综复杂,提出各种专有的概念,有着各自不同的游戏规则,其内部的细枝末叶是对具体问题的解决方案,然而有的时候我们并不知道需要解决的问题由来,一环套着一环,我在这里却避而不谈,一方面是因为我也不知 :p ,另一方面是因为不想让我或者别人陷入这样那样的泥沼中去,从宏观的角度去看问题,或者抽象,在 lua 和 js 这两种脚本引擎中,其特点为何,能达到什么样的效果,在本文的操作过程,并没有什么复杂的步骤,修改了一处编译报错问题,引擎双开,操作同一对象。避开了很多各脚本的内部实现细节内容。把复杂的问题简单化 ~

关于本文的内容,一叶会将代码项目提交到 github 以供参考(在文章最后给出1),内容不多,组织凌乱(时不时想修改,临时添加以看不同的测试效果),所以就将就着看了,哈,我的博文从不倾向给出一个完整的解决方案,以思路为重,其过程比结果更为重要。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值