COCOS2D-HTML5系列教程PART-2 制作“Helloworld”

本教程由泰然教程组出品。翻译:sile,Sharyu,蓝羽;校对:Iven。转载请注明出处并通知泰然。


就像在上个教程中承诺的那样,在这篇教程中我们会深入的了解并开始编码。和其他教程一样,我们会用Cocos2D-HTML实现HelloWorld。我在一开始就警告你,这将会是一个看起来比其实际要复杂很多的过程。有相当一部分样板代码需要你保存可以运行的环境,但总的来说它们不是那么困难。在以后的游戏中,你只需要作为模板复制粘帖就行了。

下面的内容将涉及到很多细节,比后面的教程都详细。后面有一个TL;DR(注:Too Long; Didn't Read 太长,不必细究)概要,所以如果你喜欢的话,只需要简单的看下代码示例,然后跳到此篇文章的尾部读下概要,就可以理解究竟发生了什么。



为了使事情对你自己来说变得容易一些,在安装Cocos2D-html的文件夹下创建游戏工程。如果你是按照我在教程1中来做的,路径将是c:wampwww。为你的工程创建一个新文件夹,我的是MyFirstApp。打开那个文件夹,创建一个子文件夹Classes。

现在你的文件夹已经创建了,我们要创建一些文件。首先,需要创建一个HTML网页用来承载游戏。文件名按照以下约定(使Apache服务器可以默认指向它),我使用的是index.html。在文件中输入下面代码:

Your browser does not support the canvas tag

这是最常备的HTML代码。Body标签中的style属性使canvas标签在屏幕左侧刷新,并且拥有一个白色的背景。这里只有两个真正重要的东西。第一个是canvas标签,我们至少需要一个canvas标签让Cocos2D在上面绘制所有东西。为什么长宽是600像素?这是我blog内容栏的尺寸。因此,很明显,可以随意设定你的canvas的尺寸。最后,我们包含了cocos2d.js脚本。

让我们平滑的过渡到下一个任务,创建另一个文本文件,叫做cocos2d.js。文件中输入以下代码:

var cc = cc = cc || {};

//Cocos2d directory

//Cocos2d文件夹

cc.Dir = './';//in relate to the html file or use absolute

//相对于html文件的路径或绝对路径

cc.loadQue = [];//the load que which js files are loaded

//加载队列,储存加载的js文件

cc.COCOS2D_DEBUG = 2;

cc._DEBUG = 1;

cc._IS_RETINA_DISPLAY_SUPPORTED = 0;

//html5 selector method

//html5 selector 方法

cc.$ = function (x) {

return document.querySelector(x);

};

cc.$new = function (x) {

return document.createElement(x);

};

cc.loadjs = function (filename) {

//add the file to the que

//将文件加入到队列

var script = cc.$new('script');

script.src = cc.Dir + filename;

script.order = cc.loadQue.length;

cc.loadQue.push(script);

script.onload = function () {

//file have finished loading,

//if there is more file to load, we should put the next file on the head

//文件加载结束,如果还有需要加载的文件,需要将其放到head中

if (this.order + 1 < cc.loadQue.length) {

cc.$('head').appendChild(cc.loadQue[this.order + 1]);

//console.log(this.order);

}

else {

cc.setup("gameCanvas");

//we are ready to run the game

//我们准备好开始运行游戏

cc.Loader.shareLoader().onloading = function () {

cc.LoaderScene.shareLoaderScene().draw();

};

cc.Loader.shareLoader().onload = function () {

cc.AppController.shareAppController().didFinishLaunchingWithOptions();

};

//preload ressources

//预加载资源

cc.Loader.shareLoader().preload([

//{type:"image", src:"Resources/HelloWorld.png"},

]);

}

};

if (script.order === 0)//if the first file to load, then we put it on the head

//如果是第一个文件要进行加载,将其放到head中

{

cc.$('head').appendChild(script);

}

};

// Engine files,

// They can be packeted to a single file using the Ant tool.

// The shell files and Closure Compiler which Ant needs are provided in tools folder and cocos2d folder.

// 引擎文件,可以使用Ant工具将它们打包成一个文件。Ant需要的shell文件和Closure编译器在工具文件夹和cocos2d文件夹中提供了。

cc.loadjs('../cocos2d/platform/CCClass.js');//0

cc.loadjs('../cocos2d/platform/CCCommon.js');//1

cc.loadjs('../cocos2d/platform/platform.js');//2

cc.loadjs('../cocos2d/cocoa/CCGeometry.js');//3

cc.loadjs('../cocos2d/cocoa/CCSet.js');//4

cc.loadjs('../cocos2d/platform/CCTypes.js');//5

cc.loadjs('../cocos2d/cocoa/CCAffineTransform.js');//5

cc.loadjs('../cocos2d/support/CCPointExtension.js');//12

cc.loadjs('../cocos2d/base_nodes/CCNode.js');//6

cc.loadjs('../cocos2d/platform/ccMacro.js');//7

cc.loadjs('../cocos2d/platform/ccConfig.js');//7

cc.loadjs('../cocos2d/textures/CCTexture2D.js');//12

cc.loadjs('../cocos2d/textures/CCTextureCache.js');//12

cc.loadjs('../cocos2d/actions/CCAction.js');//7

cc.loadjs('../cocos2d/actions/CCActionInterval.js');//7

cc.loadjs('../cocos2d/actions/CCActionManager.js');//7

cc.loadjs('../cocos2d/actions/CCActionEase.js');//7

cc.loadjs('../cocos2d/layers_scenes_transitions_nodes/CCScene.js');//8

cc.loadjs('../cocos2d/layers_scenes_transitions_nodes/CCLayer.js');//9

cc.loadjs('../cocos2d/layers_scenes_transitions_nodes/CCTransition.js');

cc.loadjs('../cocos2d/sprite_nodes/CCSprite.js');//10

cc.loadjs('../cocos2d/label_nodes/CCLabelTTF.js');//11

cc.loadjs('../cocos2d/text_input_node/CCIMEDispatcher.js');//12

cc.loadjs('../cocos2d/touch_dispatcher/CCTouchDelegateProtocol.js');//12

cc.loadjs('../cocos2d/touch_dispatcher/CCTouchHandler.js');//12

cc.loadjs('../cocos2d/touch_dispatcher/CCTouchDispatcher.js');//12

cc.loadjs('../cocos2d/keypad_dispatcher/CCKeypadDelegate.js');//12

cc.loadjs('../cocos2d/keypad_dispatcher/CCKeypadDispatcher.js');//12

cc.loadjs('../cocos2d/CCDirector.js');//13

cc.loadjs('../cocos2d/CCScheduler.js');//14

cc.loadjs('../cocos2d/CCLoader.js');//14

cc.loadjs('../cocos2d/CCDrawingPrimitives.js');//15

cc.loadjs('../cocos2d/platform/CCApplication.js');//16

cc.loadjs('../cocos2d/platform/CCSAXParser.js');//16

cc.loadjs('../cocos2d/platform/AppControl.js');//18

cc.loadjs('../cocos2d/menu_nodes/CCMenuItem.js');

cc.loadjs('../cocos2d/menu_nodes/CCMenu.js');

cc.loadjs('../CocosDenshion/SimpleAudioEngine.js');

// User files

// 用户文件

cc.loadjs('Classes/AppDelegate.js');//17

cc.loadjs('MyFirstApp.js');//19

这段代码几乎涵盖了最基本的Cocos2D-HTML例子,注释和其它东西。我推荐你直接使用它,而不是去重新发明轮子。

让我给你概括下这段代码实际上做了些什么:首先它声明了所有重要的cc命名空间。这个命名空间是Cocos2D绝大多数方法驻留的地方。如果你对JavaScript不熟悉,下面这行可能会很费解:

var cc = cc = cc || {};

实际上它没有看起来那么费解。它是下面代码的简单形式:

var cc;

if(cc) // If cc is already defined, assign our var to that value

// 如果cc已经定义,将我们的cc变量传给它。

{

cc = cc;

}

else

{

cc = { }; // Otherwise, create a new empty variable

// 否则创建一个新的空的变量

}

如你看到的那样,简写会短一些。你将会经常遇到这个构造方法,所以最好知道这里的原理。

下面几行定义了Cocos2D中的一些全局变量。随后,定义了两个代码快捷符号$和$new。如果你使用过jQuery或其它许多Javascript库的话,会对此非常熟悉。这样一来,不需要输入cc.document.querySelector(),输入cc.$就足够了。cc.$new也是如此,你可以在后面的cc.loadjs调用中看到。然后,使用onLoad方法加载所有的脚本和资源。对你来说,最重要的是下面一行代码:

cc.setup("gameCanvas");

这句话使你Index.html中的canvas标签与cocos2d连接在一起,并开始工作。如果你对canvas标签与我起了不一样的名字,要确保修改此处。另一个需要提到的地方是cc.Loader.shareLoader().preload。你可能已经注意到我注释的那行,在那里你可以创建一个你想要预加载的资源的数组。在这个特定的例子中,我们不需要使用任何资源,所以不要缓存任何东西。以上所有代码的运行结果就是:我们所有的脚本都加载到网页中的head标签中。

下面的步骤是调用一系列的cc.loadjs(),用来将所有需要的Cocos2d js文件填充到我们的head标签中。正是这些确保了cocos2d引擎可以正常工作。如果你是在不同的文件夹中安装的cocos2d,就需要相应的修改这些路径。例如,如果你想将cocos2d文件放到你工程目录的子文件夹中,就需要将路径改为“./cocos2d/path/scriptname.js”。如果你需要额外加载其他脚本(例如box2d支持),就在这里加载它们吧。

最后两个loadjs调用可能是最重要的。

cc.loadjs('Classes/AppDelegate.js');

 

这使你的AppDelegate.js脚本可以被调用到。如果你从事过其它游戏开发工作,可以把这个类想象成“main”或是入口,如果你愿意的话。这是你游戏的心脏和所有事情开始的地方。

cc.loadjs('MyFirstApp.js');

 

这个调用加载了你的第一个场景,包含在MyFirstApp.js中。Cocos2D将你的游戏逻辑拆分为一系列的场景。别担心,到后面就会理解了。

现在在Classes文件夹中,创建一个文件,命名为AppDelegate.js

var cc = cc = cc || {};

cc.AppDelegate = cc.Application.extend({

ctor:function () {

this._super();

},

initInstance:function () {

return true;

},

applicationDidFinishLaunching:function () {

var pDirector = cc.Director.sharedDirector();

pDirector.setDisplayFPS(true);

pDirector.setAnimationInterval(1.0 / 60);

var pScene = MyFirstApp.scene();

pDirector.runWithScene(pScene);

return true;

},

applicationDidEnterBackground:function () {

cc.Director.sharedDirector().pause();

},

applicationWillEnterForeground:function () {

cc.Director.sharedDirector().resume();

}

});

此时这里的第一行你应该很熟悉了,正如我所说的,你会经常看到它。接下来我们创建一个cc.Application派生的新对象。如果你熟悉C++,Java或者C#,你看到代码时,既会感到有点熟悉,又会觉得有点不同。原因是这样的,JavaScript不是你所熟悉的面向对象,而是基于原型的。这不是在这我能解释的范畴,但是基本上它是没有类的,用原型来代替,原型可以克隆和扩展。所以本质上你定义一次,然后它以后将作为原型来创建对象,是不是还是不清楚?

 

实质上我们在这做的是定义AppDelegate对象来扩展cc.Application原型。这是在使用Cocos2D时相当普遍的行为,所以希望你能多动动脑筋。然后我们实现了5个方法,ctor(结构体),

initInstance

,

applicationDidFinishingLaunching

,

applicationDidEnterBackground

applicationWillEnterForeground

再一次说明,如果你习惯了类似C++的语言,你可以把AppDelegate当作是cc.Application派生的,然后覆盖默认的结构体和其它4个虚方法。如果你不是一个C++/C#或者Java开发者,忘记我刚刚所说的。

首先是ctor,它是AppDelegate经过cc.Application原型扩展得到的。super()可能是一个比较容易混淆的概念,更多细节请看阅读 (警告,nsfw语言,需翻墙)。需要注意的是,在Javascript中,结构体(或者ctor)仅仅是另一个方法,与在其它语言中不同。

applicationDidEnterBackground

 和

applicationWillEnterForegroun

d方法非常简单,它们在程序得到和失去焦点时调用。它们都执行了一个相似的动作,暂停或者恢复了cc.Director的单例sharedDirector的运行。Director是一个全局对象,基本上控制所有事情的执行,我们之后会经常看到它,现在只是知道它非常重要,之后你会了解了。sharedDirector()调用方法是在等式起作用的单例部分。这已经超出了我能研究的范畴,但是现在只是把它当作全局的,唯一的公用变量。

AppDelegate的核心是didFinishLaunching方法。这个方法是AppDelegate类一加载时就调用的,也是项目的开始的时候。首先告诉(首要的!) Director我们要显示FPS,每秒60次更新。接下来创建MyFirstApp场景,使其运行。Director在一段时间只有一个场景使活动的,正如我先前所说,Cocos使按场景来组织你的游戏的。这是你的场景被创建和开始执行的地方。

现在开始创建场景对象,在你的项目根目录( c:wampwwwMyFirstApp ),创建一个名为MyFirstApp.js的文件,如下所示

var MyFirstApp = cc.Layer.extend({
 init:function()
 {
  this._super();

  var s = cc.Director.sharedDirector().getWinSize();
  var layer1 = cc.LayerColor.layerWithColorWidthHeight(cc.ccc4(255, 255, 0, 255), 600, 600);
        layer1.setPosition(cc.ccp(s.width/2,s.height/2));
        layer1.setIsRelativeAnchorPoint(true);

  var helloLabel = cc.LabelTTF.labelWithString("Hello world", "Arial", 30);
  helloLabel.setPosition(cc.ccp(s.width/2,s.height/2));
  helloLabel.setColor(cc.ccc3(255,0,0));
  var rotationAmount = 0;
  var scale = 1;
  helloLabel.schedule(function()
   {
    this.setRotation(rotationAmount++);
    if(rotationAmount > 360)
     rotationAmount = 0;
    this.setScale(scale);
    scale+= 0.05;
    if(scale > 10)
     scale =1;
   });

  layer1.addChild(helloLabel);
        this.addChild(layer1);

  return true;
 }

});

MyFirstApp.scene = function() {
 var scene = cc.Scene.node();
 var layer = this.node();

 scene.addChild(layer);
 return scene;
}

MyFirstApp.node = function() {
 var pRet = new MyFirstApp();

 if(pRet && pRet.init()){
  return pRet;
 }
 return null;
}

就像在创建AppDelegate一样,我们通过扩展Cocos2D原型来创建MyFirstApp,这个例子是ccLayer。如果你曾经用过图形程序,比如Photoshop或者GIMP,图层的概念应该很熟了。如果对于图层你是新手,你可以把图层看成图像或者图形,可能部分是透明的。你可以把图层分层放置来创建复合图像。下面这个图像解释了这个概念:

你可以把MyFirstApp当作整个图层集合的代表,它最后将会画到我们的HTML画布tag上

正如我所说的那样,我们的画布由一系列图层组成,首先要做的是创造一个纯黄色图层,象下面的代码那样:

var layer1 = cc.LayerColor.layerWithColorWidthHeight(cc.ccc4(255, 255, 0, 255), 600, 600);

 

我们建立一个600*600大小的层(就像画布标签一样),填充上RGBA颜色(255,255,0,255)。这些颜色分别代表的是红,绿,蓝和alpha值,从0到255。这个alpha值可以看作是层中透明色的数值,而在这个例子里是100%的不透明。如你所看到的那样,我们通过调用cc.ccc4创建了颜色,这种命名方式非常普遍。在Cocos2d里函数名前加前缀cc,最后面的c4代表的是颜色和参数数量,所以就是ccc4。

现在有一个纯黄色层了,我们需要给它定位。Cocos2D中的定位与我们以前有所不同。默认放置元素位置由左上变成了中心点,另外Cocos2D坐标也从左上角开始变为左下角。因此当设置层的位置时,我们想让它成为屏幕的中心。

现在创建HelloWorld文本,我们要做的是通过创建一个cc.LabelTTF(TTF=True Text Font),并传入参数字符串“Hello world”,字体Arial和字体大小30。我们也想把它显示到屏幕的中间并且字体是红色。此时需要声明两个局部变量rotationAmount和scale,这两个一会就要用到。

接下来的代码是关键,我们声明一个lambda(或者匿名)方法,用来传递参数到schedule方法。而最后的结果是,只要我们的层一直是活动的,这些代码会在每个更新时(我们早些时候设的每秒60次)执行。这个结果的本质是Director调用我们的场景进行每60s一次的更新,然后我们的场景调用每个node的update方法来进行更新。这一行注册了这个方法,这个方法会在更新时调用。(这里简化了实际的流程,但是就目前来说是可行的)。所有的函数会沿着开始的点旋转文本并缩放它直到10倍大小。

最后我们把Hello World 文本添加到层里,并同时添加到MyFirstApp变量。

你可能需要在AppDelegate进行回调,我们可以这么做:

var pScene = MyFirstApp.scene();

 

当我们创建场景时,我们需要在这里声明,并构造cc.Scene对象并且创建其主层,这个层本身是在MyFirstApp.node()方法中创建的,它可以创建MyFirstApp变量和调用其init()方法。语法可能刚开始看起来很奇怪,但你可以很快的上手。

正如我开始所讲的,虽然看起来有点复杂(事实上也是),我将简单的介绍所有在很短时间发生的事情,来看看我们辛苦所做的成果吧(点击下图进入)!


TL;DR顾刚刚生的:

 

  1. web浏览器载入index.html
  2. 画布tag被定义
  3. cocos2d.js载入
  4. cocos2d.js载入所有cocos2D库到页面顶部,AppDelegate.js和MyFirstApp.js也是这样
  5. AppDelegate创建MyFirstApp实例,Director将其设为运行的场景。
  6. 作为创建的一部分,MyFirstApp(衍生于Layer)创建一个场景并把自己加到上面。

 

 

你的游戏已经在运行了,你可以更仔细的观察程序执行和处理输入了。

你可以在这里下载整个工程,解压到www文件夹,在浏览器中打开index.html

 

标签: none

?>