Cocos2d-js模块化开发的一种解决方案

来源:互联网 发布:什么情况下多进程编程 编辑:程序博客网 时间:2024/05/01 22:34

转自:http://blog.csdn.net/levildo/article/details/44709971

一、Cocos2d-js Module


Cocos2d-js Module(下文简称Module)是一个用于Cocos2d-js游戏引擎模块化编程的一种解决方案。Module可以让项目如C++的#include、Lua的require等语法那样加载其他文件/模块的内容

先不谈做Cocos2d-js模块化编程的原因和原理,首先看看具体做了什么。整个解决方案仅包括一个只有100行代码量的名为module.js的文件,对外暴露3个全局函数load、include、dumpModuleInfo,它们分别的作用如下:
  • load(name, func)
添加一个模块,第一个参数name代表模块的名字,第二个参数func是添加模块时调用的函数。
[javascript] view plain copy
  1. // Example  
  2. load("GameScene"function(){  
  3.     var GameScene = cc.Scene.extend({});  
  4.       
  5.     return GameScene;   // return the module  
  6. })  
  • include(name)
获取一个模块,name是该模块的名称。
[javascript] view plain copy
  1. // Example  
  2. var GameScene = include("GameScene")  
  • dumpModuleInfo()
打印日志,显示当前所有已经加载过的模块。
[javascript] view plain copy
  1. // Example  
  2. dumpModuleInfo();   // print the GameScene module  

这三个函数实现了基础的模块化开发功能。
  1. load:在Module中一般要求一个文件就是一个模块,load函数作为一个文件的整体(即一个JavaScript文件调用一次load函数),load函数保证模块被保存在第二个参数func中,避免污染全局的命名空间,同时要求第一个参数name和该文件所处的路径相同。在其他编程语言中和load函数功能类似的语法并不存在,倒是有点类似于在Lua中调用require加载模块时,被加载的文件中的return函数的功能;
  2. include: 类似于C++的include、C#的using、Lua的require等,作用是载入一个模块,该模块名称需要和文件路径对应;
  3. dumpModuleInfo:显示当前所有已经加载过的模块,便于调试。
一个更加完整的例子:
  1. 在src文件夹中添加一个HelloModuleScene.js文件,这个模块是一个显示"Hello Module"的Scene,内容如下
    [javascript] view plain copy
    1. // Content of HelloModuleScene.js  
    2. load("HelloModuleScene"function(){  
    3.       
    4.     var HelloModuleScene = cc.Scene.extend({  
    5.         ctor: function(){  
    6.             this._super();  
    7.             var lbl = cc.LabelTTF("Hello Module""Arial", 38);  
    8.             lbl.setPosition(cc.winSize.width / 2, cc.winSize.height / 2);  
    9.             this.addChild(lbl);  
    10.         }  
    11.     });  
    12.       
    13.     return HelloModuleScene;  
    14. })  
  2. 修改工程中的main.js函数,注意在这一步中使用了Cocos2d-js提供的require函数执行module.js文件
    [javascript] view plain copy
    1. // Content of main.js  
    2.   
    3. require("src/module.js");  
    4.   
    5. cc.game.onStart = function(){  
    6.     cc.view.adjustViewPort(true);  
    7.     cc.view.setDesignResolutionSize(800, 450, cc.ResolutionPolicy.SHOW_ALL);  
    8.     cc.view.resizeWithBrowserSize(true);  
    9.       
    10.     var HelloModuleScene = include("HelloModuleScene");  
    11.       
    12.     //load resources  
    13.     cc.LoaderScene.preload(g_resources, function () {  
    14.         cc.director.runScene(new HelloModuleScene());  
    15.     }, this);  
    16.       
    17.     dumpModuleInfo();  
    18. };  
    19. cc.game.run();  
如果你经常使用Cocos2d-js进行游戏开发的话,此时可能会提出疑问,这里是不是漏了一步,还没在项目中的project.json文件中jsList数组里添加"src/HelloModuleScene.js",怎么能在main.js中调用HelloModuleScene呢。这其实正是Module的功能之一。
运行例子之后显示


二、Module的优势与限制


1.Module的优势:
  • 提供类似于C++的#include、Lua的require的功能,需要使用哪个模块就include哪个模块,调用关系清晰可见
  • 模块化编程,不再污染全局命名空间
  • 一个模块对应于一个继承自cc.Class的类(虽然这不是强制的,即使是一个普通的Object作为模块也可以),要求程序员统一使用面向对象的方式编程
  • 不用再去管project.json中的jsList数组,也就不需要再去思考JavaScript文件执行顺序的问题
  • 轻量级,仅需额外添加一个100行代码的JavaScript文件
  • 适合大中型游戏项目开发
  • 有利于模块的复用
2. Module的限制:
  • 当两个或多个模块互相include时,不能在模块的func的作用域中(load的第二个参数)调用被引用模块的功能,这样会造成不可预知的结果;应该仅在当前模块的类的函数作用域中调用被引用的模块功能(即使不使用Module也同样会有这种情况发生,这个概念比较抽象,可能在之后的博文中进行解释)
  • 不适合小型游戏开发
  • 因为使用到Cocos2d-js jsb提供的require函数功能,而这个函数在html5版本的Cocos2d-js中不存在,所以不适合跨Native和Browser的项目使用

三、为什么要模块化开发


C++有#include、C#有using、Java有import、Lua有require,就连同门的Node.js也有自己的module机制,JavaScript却缺乏语法层次上的模块化编程支持。在经历了一些项目的洗礼后,笔者越发感觉使用Cocos2d-js引擎开发的大中型游戏项目需要一种有效的、简单的方法进行模块化编程,而不仅仅是让jsList中配置的JavaScript文件一个一个按顺序执行。

在实际开发中有以下几种文件之间调用关系的解决方案:
1. 全部使用全局变量,这是最次的方案,也是最简单易懂的方案,非常适合小型html5游戏开发,这种方案的代码一般是这样的
缺点就是污染了全局命名空间,维护和复用困难。

2.每个文件用一个函数包起来执行,所有类都统一用一个Object保存起来,这样就避免了污染全局命名空间

在上面的例子中,游戏中使用的类都保存在game这个全局Object之中,需要调用某个类的时候加上game的前缀就可以了。这种方法确实管用,但也有其缺陷,那就是有时候调用层次太多,想要用简短的局部变量来保存某个类时,问题就出来了。例如游戏主角类game.character.Hero需要多次调用怪物类game.character.Monster(甚至有可能更长的前缀),希望使用var Monster = game.character.Monster的方法来简化调用。
[javascript] view plain copy
  1. // Content of Hero.js  
  2.   
  3. (function(){  
  4.     var Monster = game.character.Monster;  
  5.       
  6.     var Hero = cc.Sprite.extend({  
  7.     // ... do something  
  8.     })  
  9.       
  10.     game.character.Hero = Hero;  
  11. })()  
如果仅仅是Hero类调用了Monster类,那么只需要管理好jsList数组,注意文件执行顺序,让Monster.js在Hero.js之前执行,那么一切安好。

但是如果Monster类也使用这种方法调用Hero类
[javascript] view plain copy
  1. // Content of Monster.js  
  2.   
  3. (function(){  
  4.     var Hero = game.character.Hero;  
  5.       
  6.     var Monster = cc.Sprite.extend({  
  7.     // ... do something  
  8.     })  
  9.       
  10.     game.character.Monster = Monster;  
  11. })()  

这个时候不论怎么安排jsList数组的顺序,让Monster.js和Hero.js谁先执行,都会出问题。因为JavaScript文件按顺序执行的原因,假设Hero.js先执行,那么当Hero.js执行的时候Monster.js还没执行过,所以执行var Monster = game.character.Monster的时候其实Monster还未定义,因为当前Monster.js根本就还没执行,game.character.Monster就是一个undefined!

目前已有的一些解决方案思路
  • 使用其他的JavaScript框架,例如pureMVC
这种做法的好处是可以使用比较成熟的框架,缺点是学习成本较高,而且面向对象的实现和Cocos2d-js有较大的差别,可能在类的继承中出现问题。
  • 使用Cocos2d-js提供的require
其实这根本谈不上是一种模块化编程的解决方案,Cocos2d-js提供的require函数和Lua中的有很大区别,前者是执行一遍JavaScript文件,后者是将文件内容加载到模块中并缓存起来。但是require功能为模块化编程提供了基础。

Module解决方案

使用Module解决方案,前面提到的Monster和Hero的例子将变成这样
[javascript] view plain copy
  1. // Content of Hero.js  
  2.   
  3. load("game/character/Hero"function(){  
  4.     var Monster = include("game/character/Monster");  
  5.   
  6.     var Hero = cc.Sprite.extend({  
  7.         // ... do something  
  8.     })  
  9.   
  10.     return Hero;  
  11. })  
[javascript] view plain copy
  1. //Content of Monster.js  
  2.   
  3. load("game/character/Monster"function(){  
  4.     var Hero = include("game/character/Hero");  
  5.   
  6.     var Monster = cc.Sprite.extend({  
  7.         // ... do something  
  8.     })  
  9.   
  10.     return Monster;  
  11. })  
这样一来就可以在Hero类或者Monster类的函数中互相调用对方了,什么jsList,什么文件执行顺序,再也不用理会了!


最后附上git地址
Cocos2d-js-module git: http://git.oschina.net/Levil/Cocos2d-js-Module

参考

[1] [Cocos2d-JS MVC模块开发] http://cn.cocos2d-x.org/tutorial/show?id=1898
原创粉丝点击