用JavaScript开发HTML5与C++游戏

这是个对cocos JS的简单经验谈,让大家明白引擎原理,以及使用JS开发过程中要注意的事项,但不会涉及太多基本编程知识以及基础安装等,在阅读本文前请先对Javascript与Cocos有基本了解。

Cocos JS项目组成

首先,你可能会问:为什么cocos JS能同时开发HTML5与C++游戏?
我们先看看cocos JS 3.x(3.3)项目目录构成:
先建立一个项目:
cocos new HelloCococJS -p com.163.cocosjs -l js --ios-bundleid com.163.cocosjs

文件结构

大家看图上的标注:

  1. cocos2d-html5引擎:纯html5引擎,在浏览器上的图形处理全靠它了;
  2. js-bindings:为什么JS可以写原生游戏,关键就在这里;
  3. runtime-src:原生的runtime工程,基础的Class文件,Android到iOS甚至Win32的工程;
  4. tools:JSBinding工具

最让人好奇的是js-bindings,进入里面,里面有3个目录:

  1. bindings:绑定脚本,一个脱水层,用于把cocos JS耦合到到原生引擎,稍候会讲到绑定原理;
  2. cocos2d-x:cocos2d-x的C++引擎,就是那个原生引擎了;
  3. external:里面就只有SpiderMonkey JS引擎,Firefox就是用它了,相比V8引擎,在kraken time与sunspider time测试中略胜V8。
上图:鲜红色为Firefox,分数越低越好

上图:鲜红色为Firefox,分数越低越好

看到这里,相信不少同学已经发出“哦~”一声,原来如此!
没错,cocos JS组件内包含了3个引擎1个工具:

  • 在HTML5时,运行的是cocos2d-html5引擎,采用canvas或WebGL渲染;
  • 在打包成原生时,SpiderMonkey做JS脚本运行解释器打包进去,同时编译cocos2d-x C++,前者通过JSB让JS调用后者的C++处理图形,原生除了JS外,已经与html5完全无关了,JS就是脚本语言,类似Lua。

JSBinding原理

JSBinding项目最初由zynga实现,用于胶合JS与Objective C的cocos2d-iphone,项目Github: jsbindings

JavaScript Bindings for C / Objective-C (JSB) is the "glue" code (or wrapper code) that sits between native code (C or Objective-C) and JavaScript (JS) code. JSB allows calling native code from JS and vice-versa.

现在已经移植到这里来,用于把cocos2d-x生成对应的JS接口,方法大致为:

编写配置文件.ini,通过tools下的bindings-generator,把C++类生成对应的辅助C++脚本以及Javascript接口脚本。

配置文件.ini作用

JSB本身就是一个工具,在项目中不负责运行代码,只生成C++对应的JS,而配置文件就是生成的规则,例如JSBinding 上的描述:

// CCAnimation (from cocos2d-iphone v2.0)
+(id) animationWithAnimationFrames:(NSArray*)arrayOfAnimationFrames delayPerUnit:(float)delayPerUnit loops:(NSUInteger)loops;

默认生成以下JS:

// ugly
cc.CCAnimation.animationWithAnimationFrames_delayPerUnit_loops_( frames, delay, loops );

通过规则配置,

method_properties = CCAnimation # animationWithAnimationFrames:delayPerUnit:loops: = name:"create",

可以生成:

// more JS friendly
cc.Animation.create( frames, delay, loops );

JS调用C++细节

JS:

gnode = cc.Node.create();

JSB C++:

JSBool js_cocos2dx_CCNode_create(JSContext *cx, uint32_t argc, jsval *vp)
{
    if (argc == 0) {
        cocos2d::CCNode* ret = cocos2d::CCNode::create();
        jsval jsret;
        do {
        if (ret) {
            js_proxy_t *proxy = js_get_or_create_proxy(cx, 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, "wrong number of arguments");
    return JS_FALSE;
}

到这里,相信大家明白其中原理了,更深入的可以看文件夹:tools/bindings-generator/test 里的例子。

好了,JSBinding原理就简单说到这里。

实践心得

明白了上面的原理,假如要用JS作用脚本写原生游戏,应该怎么做呢?

在实践过程中,JS中大部分在 cc 命名空间下的接口都有对应的C++ API,小部分html5引擎特有的,有可能是历史遗留,幸好在开发过程中H5是可以实时预览的,也可以容易的放到iOS原生模拟器中看,如果出现类似JSB class not found的错误基本就是这个原因了,换一个写法就可以,这也是一个坑,需要慢慢踩。这里主要说一下JS怎么编码,因为JS自由度太大,所以最好结合JSB原理遵守一些规范。

(如有错误请不吝指正)

  • 规范命名空间,例如 var tt = {} || tt;
  • JS类:统一用cc.Class.extend来实现,例如:
tt.Monster = cc.Class.extend({
    id:         ""
    level:      1,
    texture:    null
});

这个好处是让JS类做到类似C++那样的继承,构造函数中通过this._super()可以调用父类构造函数;

  • 除了cc,ccui这些引擎指定的命名空间可能会调用C++外,所有JS的编写与浏览器中编写没有任何区别;
  • 效率问题:一般来说,不可交互的动画动作,都直接转化成C++接口,之后再也与JS无关不用担心效率问题;
  • 可交互动作,例如touch事件拖动精灵,减少JS里的复杂运算增加效率,但经过测试拖动精灵原生中没有让人感觉有区别;
  • 资源:虽然加载方式不一样,但原生使用上区别不大,两个点:一是touch事件接口有区别有坑要避雷,二是cocos studio只支持1.6及以前的json文件,以及最新cocos 2.1的json文件。
this.stage = guiReader.widgetFromJsonFile(res.StageUI_json);
this.addChild(this.stage, 200);
this.player = ccui.helper.seekWidgetByName(this.stage, "Player");
  • 其它:虽然很多JS写法比较便利,例如 sripte.x = 20 ,但建议写成 sprite.setPositionX(20),只为避雷;

包大小问题

采用JSB写游戏的好处是可以真正的多端运行,但从原理可知这是怎么一个回事,如果要做原生,唯一问题就是包大小了,SpiderMonkey本身就近10m的.so文件,最后打出的iOS近10M,apk包7M,纯C++的iOS才2M多,元芳怎么看?

扩展思考

看了上面的原理,应该不难知道Egret也是类似这么干了吧,但Egret优点是JS的编写用了TypeScript,微软的进阶版JS,更OO和利于组件模块化。

时间有限,先介绍到这里,下一篇会写关于网络请求相关。

参考文献

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容