今天分享一下一款非常不错的html5游戏引擎:
Cocos2d-html5.
    html5的时代正在来临,其可以方便的运行在多平台上并调用OPENGL 进行图形渲染,大量使用html5开发的2D和3D游戏正在涌现,Cocos2d-x也顺应形势推出了相应的版本,今天我们来学习一下Cocos2d-x在Html5上怎么运行和开发及调试。
打开HelloHTML5World,可以看到以下文件和目录:

res:资源图片目录:

src:当前程序的js文件目录:

main.js:主逻辑js代码文件
index.html:html5网页文件
cocos2d.js:加载Cocos2d-x库的文件
build.xml:编译cocos2d-x的html5平台版本生成的文件清单报告。
.DS_Store:系统目录显示属性存储文件,可以删除。
我们用浏览器直接打开index.html,可以看到:

其源码为:
[html]
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Cocos2d-html5 Hello World test</title>
    <link rel="icon" type="image/GIF" href="res/favicon.ico"/>
</head>
<body style="padding:0; margin: 0; background: #000;">
<div style="text-align: center; font-size: 0">
    <canvas id="gameCanvas" width="800" height="450"></canvas>
</div>
</body>
</html>
<script src="cocos2d.js"></script>
[/html]
可以看到,这里面关键的要点是两个地方:
[html]
<canvas id="gameCanvas"width="800" height="450"></canvas>
[/html]
在html5中创建了一个画布(canvas),设定了名称和大小
[html]
<scriptsrc="cocos2d.js"></script>
[/html]
在网页中加载了cocos2d.js
打开cocos2d.js后,可以看到下面的代码:
[html]
(function () {
		//定义变量d为当前网页的文档对象
    var d = document;
		//定义变量c为一个结构,存储了一些配置属性和值。
    var c = {
        COCOS2D_DEBUG:2, //0 to turn debug off, 1 for basic debug, and 2 for full debug
        box2d:false,//不使用box2d
        chipmunk:false,//不使用chipmunk
        showFPS:true,//显示FPS
        frameRate:60,//设定每秒60帧
        loadExtension:false,不载入扩展库
        tag:'gameCanvas', //运行cocos2d-x的画布
        engineDir:'../cocos2d/',//引擎的目录,这里指定为当前上级目录下的cocos2d目录中
        //SingleEngineFile:'',//这里注释掉了。
        appFiles:[//应用程序要使用到的两个js文件。
            'src/resource.js',//资源定义文件
            'src/myApp.js'//逻辑处理文件
        ]
    };
		//当前窗口加载一个事件响应处理,在DOM被加载时调用。
    window.addEventListener('DOMContentLoaded', function () {
        //当前文档创建一个脚本
        var s = d.createElement('script');
        //如果c结构中有SingleEngineFile变量并肯engineDir为空,则s中的脚本引用为SingleEngineFile指示的文件,当然,本例中这个变量注释掉了,这一段不成立。
        if (c.SingleEngineFile && !c.engineDir) {
            s.src = c.SingleEngineFile;
        }
			//如果engineDir有效,则s中的脚本引用为engineDir指定目录下的相应文件,本例中为“../cocos2d/platform/jsloader.js”。
        else if (c.engineDir && !c.SingleEngineFile) {
            s.src = c.engineDir + 'platform/jsloader.js';
        }
        else {
			//如果都不是,弹出对话框提示c结构成员变量设置错误。
            alert('You must specify either the single engine file OR the engine directory in "cocos2d.js"');
        }
			//将结构c做为一个成员变量存入到当前文档。
        document.ccConfig = c;
			//上面创建的‘script’的id设置为’cocos2d-html5’.
        s.id = 'cocos2d-html5';
			//将这个script加入到当前HTML文档的结尾。
        d.body.appendChild(s);
        //else if single file specified, load singlefile
    });
})();
[/html]
本页代码的作用是在html页面尾部中加入:
[html]<scriptsrc = ’../cocos2d/platform/jsloader.js’ id=’cocos2d-html5’></script>[/html]
下面我们来打开cocos2d目录下的’platform/jsloader.js’:
[html]
//脚本执行函数。
(function () {
	    //定义变量engine为一个字符串数组,元素为运行当前版本cocos2d-x要加载的所有代码文件。
    var engine = [
        'platform/CCClass.js',
        'platform/miniFramework.js',
        'platform/CCCommon.js',
        'platform/ZipUtils.js',
        'platform/base64.js',
        'platform/gzip.js',
        'platform/CCMacro.js',
        'platform/CCFileUtils.js',
        'platform/CCTypes.js',
        'platform/zlib.min.js',
        'cocoa/CCGeometry.js',
        'platform/Sys.js',
        'platform/CCConfig.js',
        'cocoa/CCSet.js',
        'cocoa/CCNS.js',
        'cocoa/CCAffineTransform.js',
        'support/CCPointExtension.js',
        'support/CCUserDefault.js',
        'base_nodes/CCNode.js',
        'base_nodes/CCAtlasNode.js',
        'textures/CCTexture2D.js',
        'textures/CCTextureCache.js',
        'textures/CCTextureAtlas.js',
        'misc_nodes/CCRenderTexture.js',
        'misc_nodes/CCProgressTimer.js',
        'effects/CCGrid.js',
        'effects/CCGrabber.js',
        'actions/CCAction.js',
        'actions/CCActionInterval.js',
        'actions/CCActionInstant.js',
        'actions/CCActionManager.js',
        'actions/CCActionProgressTimer.js',
        'actions/CCActionCamera.js',
        'actions/CCActionEase.js',
        'actions/CCActionGrid.js',
        'actions/CCActionTiledGrid.js',
        'actions/CCActionCatmullRom.js',
        'layers_scenes_transitions_nodes/CCScene.js',
        'layers_scenes_transitions_nodes/CCLayer.js',
        'layers_scenes_transitions_nodes/CCTransition.js',
        'layers_scenes_transitions_nodes/CCTransitionProgress.js',
        'layers_scenes_transitions_nodes/CCTransitionPageTurn.js',
        'sprite_nodes/CCSprite.js',
        'sprite_nodes/CCAnimation.js',
        'sprite_nodes/CCAnimationCache.js',
        'sprite_nodes/CCSpriteFrame.js',
        'sprite_nodes/CCSpriteFrameCache.js',
        'sprite_nodes/CCSpriteBatchNode.js',
        'label_nodes/CCLabelAtlas.js',
        'label_nodes/CCLabelTTF.js',
        'label_nodes/CCLabelBMFont.js',
        'particle_nodes/CCParticleSystem.js',
        'particle_nodes/CCParticleSystemQuad.js',
        'particle_nodes/CCParticleExamples.js',
        'particle_nodes/CCParticleBatchNode.js',
        'touch_dispatcher/CCTouchDelegateProtocol.js',
        'touch_dispatcher/CCTouchHandler.js',
        'touch_dispatcher/CCTouchDispatcher.js',
        'touch_dispatcher/CCMouseDispatcher.js',
        'keyboard_dispatcher/CCKeyboardDelegate.js',
        'keyboard_dispatcher/CCKeyboardDispatcher.js',
        'text_input_node/CCIMEDispatcher.js',
        'text_input_node/CCTextFieldTTF.js',
        'CCDirector.js',
        'CCCamera.js',
        'CCScheduler.js',
        'CCLoader.js',
        'CCDrawingPrimitives.js',
        'platform/CCApplication.js',
        'platform/CCSAXParser.js',
        'platform/AppControl.js',
        'menu_nodes/CCMenuItem.js',
        'menu_nodes/CCMenu.js',
        'tileMap_parallax_nodes/CCTMXTiledMap.js',
        'tileMap_parallax_nodes/CCTMXXMLParser.js',
        'tileMap_parallax_nodes/CCTMXObjectGroup.js',
        'tileMap_parallax_nodes/CCTMXLayer.js',
        'tileMap_parallax_nodes/CCParallaxNode.js',
        'menu_nodes/CCMenuItem.js',
        'menu_nodes/CCMenu.js',
        'base_nodes/CCdomNode.js',
        '../CocosDenshion/SimpleAudioEngine.js'
    ];
	   //取得当前文档存入d,取得上一节中创建的当前d的成员变量ccCofing存入c.
    var d = document;
    var c = d.ccConfig;
		//如果c的结构变量loadExtension有效,即当前程序需要加载扩展库,则在上面的变量engine所对应的字符串数组尾部添加cocos2d-x扩展库所涉及的代码文件。
    if (c.loadExtension != null && c.loadExtension == true) {
        engine = engine.concat([
            '../extensions/GUI/CCControlExtension/CCControl.js',
            '../extensions/GUI/CCControlExtension/CCControlButton.js',
            '../extensions/GUI/CCControlExtension/CCControlUtils.js',
            '../extensions/GUI/CCControlExtension/CCInvocation.js',
            '../extensions/GUI/CCControlExtension/CCScale9Sprite.js',
            '../extensions/GUI/CCControlExtension/CCMenuPassive.js',
            '../extensions/GUI/CCControlExtension/CCControlSaturationBrightnessPicker.js',
            '../extensions/GUI/CCControlExtension/CCControlHuePicker.js',
            '../extensions/GUI/CCControlExtension/CCControlColourPicker.js',
            '../extensions/GUI/CCControlExtension/CCControlSlider.js',
            '../extensions/GUI/CCControlExtension/CCControlSwitch.js',
            '../extensions/GUI/CCScrollView/CCScrollView.js',
            '../extensions/GUI/CCScrollView/CCSorting.js',
            '../extensions/GUI/CCScrollView/CCTableView.js',
            '../extensions/CCBReader/CCNodeLoader.js',
            '../extensions/CCBReader/CCBReaderUtil.js',
            '../extensions/CCBReader/CCControlLoader.js',
            '../extensions/CCBReader/CCSpriteLoader.js',
            '../extensions/CCBReader/CCNodeLoaderLibrary.js',
            '../extensions/CCBReader/CCBReader.js',
            '../extensions/CCBReader/CCBValue.js',
            '../extensions/CCBReader/CCBKeyframe.js',
            '../extensions/CCBReader/CCBSequence.js',
            '../extensions/CCBReader/CCBRelativePositioning.js',
            '../extensions/CCBReader/CCBAnimationManager.js',
            '../extensions/CCControlEditBox.js'
        ]);
    }
		//如果c中的engineDir设置无效,清空engine。
    if (!c.engineDir) {
        engine = [];
    }
    else {
		//如果c中的engineDir设置有效
		    //如果c的结构中有结构变量box2d和chipmunk,则在变量engine所对应的字符串数组尾部添加cocos2d-x物理引擎库所涉及的代码文件。
        if(c.box2d || c.chipmunk){
            engine.push('Draw_Nodes/CCDrawNode.js');
            engine.push('physics_nodes/CCPhysicsSprite.js');
            engine.push('physics_nodes/CCPhysicsDebugNode.js');
            if (c.box2d)
                engine.push('../box2d/box2d.js');
            if (c.chipmunk)
                engine.push('../chipmunk/chipmunk.js');
        }
			//遍历engine中的所有元素,将各元素的文件相对目录转变为绝对目录。
        engine.forEach(function (e, i) {
            engine[i] = c.engineDir + e;
        });
    }
		//定义量时变量
    var loaded = 0;
		//在engine最尾部加上c的结构变量appFiles (即当前程序资源和逻辑所对应的js),将新数组保存到que。
    var que = engine.concat(c.appFiles);
		//再加上当前程序的主逻辑js文件。
    que.push('main.js');
		//判断浏览器是否是IE9
    if (navigator.userAgent.indexOf("Trident/5") > -1) {
        //如果是IE9
			//创建一个局部变量serial,存值-1
        this.serial = -1;
			//定义一个函数loadNext
        var loadNext = function () {
				//定义临时变量s为serial+1
            var s = this.serial + 1;
				//如果s所指定的索引小于que的数组数量.
            if (s < que.length) {
					//当前文档创建一个脚本标记,保存为变量f
                var f = d.createElement('script');
					//设置script的src为索引s指定的que数组元素。
                f.src = que[s];
					//将索引s存入f成员变量serial。
                f.serial = s;
					//设定scrip在被加载时调用函数loadNext。
                f.onload = loadNext;
					//将scrip放入到当前HTML文档的结尾.
                d.body.appendChild(f);
					//将数组的加载进度保存到临时变量p,在当前位置你可以处理你的加载进度条。
                p = s / (que.length - 1);
            }
        };
			//调用一下刚创建的函数,执行第一次后,就会不断的在html文档结尾加入:‘<script  src = ‘这里为que[新索引]’ serial=’索引’ onload=’loadNext’></script>’。
        loadNext();
    }
    else {
			//如果不是IE9,则遍历que数组的每个元素。
        que.forEach(function (f, i) {
				//当前文档创建一个脚本标记,保存为变量s
            var s = d.createElement('script');
				//设置scrip的async变量为false. 目前firefox和chrome都是实现了script标签的async属性.这个新的属性能让我们以一种更 简单的方式防止浏览器阻塞
            s.async = false;
				//设置script的src变量值为遍历元素。
            s.src = f;
				//设定加载时调用函数更新进度计算。
            s.onload = function () {
                loaded++;
                p = loaded / que.length;
                //TODO: code for updating progress bar
            };
				//将scrip放入到当前HTML文档的结尾.
            d.body.appendChild(s);
				//将s保存到que数组的第i个数组元素中。
            que[i] = s;
        });
    }
})();//最后的()代表当前函数被调用。
[/html]
可见jsloader.js的作用是加载cocos2d-x所要用到的所有js文件以及‘resource.js’,‘myApp.js’,‘main.js’。
打开’resource.js’:
[html]
//定义一些资源文件字符串变量。
var s_HelloWorld = "res/HelloWorld.png";
var s_CloseNormal = "res/CloseNormal.png";
var s_CloseSelected = "res/CloseSelected.png";
//创建一个结构数组,标记文件类型和对应的文件名称字符串。
var g_ressources = [
    //image
    {type:"image", src:s_HelloWorld},
    {type:"image", src:s_CloseNormal},
    {type:"image", src:s_CloseSelected}
    //plist
    //fnt
    //tmx
    //bgm
    //effect
];
[/html]
上面的文件主要是就是定义程序所要用到的资源信息。
打开myApp.js:
[html]
//创建一个cocos2d-x精灵的派生类存入CircleSprite用于计时.初始化成员变量_radians为0,即不旋转。
var CircleSprite = cc.Sprite.extend({
    _radians:0,
	//重载父类(精灵) 构造函数ctor,调用其父类的相应函数。
    ctor:function () {
        this._super();
    },
	//重载父类(精灵)成员函数draw。增加绘制代码。
    draw:function () {
		//设置要绘制时的设置信息
		//填充色为白色
        cc.renderContext.fillStyle = "rgba(255,255,255,1)";
		//画笔色为白色
        cc.renderContext.strokeStyle = "rgba(255,255,255,1)";
		//如果当前精灵的_radians小于0,则重置为360。
        if (this._radians < 0)
            this._radians = 360;
		//调用cocos2d-x的绘制圆的函数,这个函数是在CCDrawingPrimitives.js中定义的,drawCircle:function (center, radius, angle, segments, drawLineToCenter),参一为中心位置,参二为半径,参三为绘制的起始角度当然也是中心连线的起始角度,参四为圆的段数,参五为是否与中心连线。
        cc.drawingUtil.drawCircle(cc.PointZero(), 30, cc.DEGREES_TO_RADIANS(this._radians), 60, true);
    },
	//定义成员函数myUpdate,用于每次调用时成员变量_raduans自减6。等于每次调用顺时针旋转6度,因为设定FPS为60,所以一秒转一圈正好360度嘛。
    myUpdate:function (dt) {
        this._radians -= 6;
        //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld());
    }
});
//由Cocos2d-x的层派生出一个类HelloWorld.
var Helloworld = cc.Layer.extend({
	//初始化其成员变量
    isMouseDown:false,
    helloImg:null,
    helloLabel:null,
    circle:null,
    sprite:null,
	//初始化函数。
    init:function () {
		//定义临时变量保存当前实例指针。
        var selfPointer = this;
        //首先调用父类精灵的初始化函数。
        this._super();
        //取得窗口的大小
        var size = cc.Director.getInstance().getWinSize();
        //增加一个菜单按钮,设置点击后响应函数退回前一步。
        var closeItem = cc.MenuItemImage.create(
            "res/CloseNormal.png",
            "res/CloseSelected.png",
            function () {
                history.go(-1);
            },this);
		//设置菜单按钮精灵的锚点。
        closeItem.setAnchorPoint(cc.p(0.5, 0.5));
		//由菜单按钮创建一个菜单,设置菜单位置并放入当前层下。
        var menu = cc.Menu.create(closeItem);
        menu.setPosition(cc.PointZero());
        this.addChild(menu, 1);
		//设置菜单按钮的位置。
        closeItem.setPosition(cc.p(size.width - 20, 20));
        //创建一个文字标签,显示字符串“HelloWorld”。
        this.helloLabel = cc.LabelTTF.create("Hello World", "Arial", 38);
        //设置横向居中显示。
        this.helloLabel.setPosition(cc.p(size.width / 2, 0));
        //将文字标签加入到当前层下。
        this.addChild(this.helloLabel, 5);
		//创建一个新层lazyLayer并放入当前层
        var lazyLayer = new cc.LazyLayer();
        this.addChild(lazyLayer);
        // 创建当前类成员精灵,设置位置,缩放,旋转。
        this.sprite = cc.Sprite.create("res/HelloWorld.png");
        this.sprite.setPosition(cc.p(size.width / 2, size.height / 2));
        this.sprite.setScale(0.5);
        this.sprite.setRotation(180);
		//将新建的精灵放入到层lazyLayer.
        lazyLayer.addChild(this.sprite, 0);
		//创建两个动画。
        var rotateToA = cc.RotateTo.create(2, 0);
        var scaleToA = cc.ScaleTo.create(2, 1, 1);
		//让精灵运行一个动画序列,动画序列为这两个新建的动画。
        this.sprite.runAction(cc.Sequence.create(rotateToA, scaleToA));
		//创建一个计时精灵类,设置位置并放入到当前层中。
        this.circle = new CircleSprite();
        this.circle.setPosition(cc.p(40, size.height - 60));
        this.addChild(this.circle, 2);
		//这句很重要,每1/60秒响应一次它的MyUpdate函数更新。
        this.circle.schedule(this.circle.myUpdate, 1 / 60);
		//当前文字标签到运行一个移动动画。
        this.helloLabel.runAction(cc.MoveBy.create(2.5, cc.p(0, size.height - 40)));
		//开启当前视窗的触屏响应处理。
        this.setTouchEnabled(true);
		//这一句调用是让屏幕的分辩率按窗口大小来自适应避免拉伸。
        this.adjustSizeForWindow();
		//lazyLayer层的分辩率按所在画布的大小来自适应避免拉伸。
        lazyLayer.adjustSizeForCanvas();
		//设置当前窗口在改变大小时要调用函数adjustSizeForWindow
        window.addEventListener("resize", function (event) {
            selfPointer.adjustSizeForWindow();
        });
        return true;
    },
	//如果窗口在改变大小时要调用的函数,现实等比调整Cocos2d-x画布大小以适应填充网页客户区。
    adjustSizeForWindow:function () {
		//窗口客户端宽度减去body区域宽度,得到一个差值保存到新创建变量margin。
        var margin = document.documentElement.clientWidth - document.body.clientWidth;
		//如果客户端宽度小于html5中画布宽度800,设置cocos2d-x画布的宽度按照html5中画布宽度800设置,这里是限制了显示cocos2d-x画面的最小宽度。
        if (document.documentElement.clientWidth < cc.originalCanvasSize.width) {
            cc.canvas.width = cc.originalCanvasSize.width;
        } else {
			//否则,设置cocos2d-x画布的宽度按照html5中body宽度设置。
            cc.canvas.width = document.documentElement.clientWidth - margin;
        }
		//如果可见区域高度小于html5中画布高度450,设置cocos2d-x画布的高度按照html5中画布高度450设置。
        if (document.documentElement.clientHeight < cc.originalCanvasSize.height) {
            cc.canvas.height = cc.originalCanvasSize.height;
        } else {
		//否则,设置cocos2d-x画布的高度按照html5中body高度设置。
            cc.canvas.height = document.documentElement.clientHeight - margin;
        }
		//计算出cocos2d-x的画布与HTML5上的画布的缩放比例
        var xScale = cc.canvas.width / cc.originalCanvasSize.width;
        var yScale = cc.canvas.height / cc.originalCanvasSize.height;
		//因为一般窗口都是宽大于高,所以这里做个处理,使画面保持等比缩放。
        if (xScale > yScale) {
            xScale = yScale;
        }
		//根据等比缩放重新计算出Cocos2d-x中画布的宽高。
        cc.canvas.width = cc.originalCanvasSize.width * xScale;
        cc.canvas.height = cc.originalCanvasSize.height * xScale;
		//取得网页中id为Cocos2dGameContainer的文档div元素。
        var parentDiv = document.getElementById("Cocos2dGameContainer");
        if (parentDiv) {
			//如果找到了设置其style中的宽和高按画布的像素大小
            parentDiv.style.width = cc.canvas.width + "px";
            parentDiv.style.height = cc.canvas.height + "px";
        }
		//设置cocos2d-x中渲染区域向上移动相应距离及设置相应缩放。
        cc.renderContext.translate(0, cc.canvas.height);
        cc.renderContext.scale(xScale, xScale);
		//设置cocos2d-x的像素与点的缩放比例。
        cc.Director.getInstance().setContentScaleFactor(xScale);
    },
    // 菜单按扭(关闭按钮)按下时的响应处理。
    menuCloseCallback:function (sender) {
		//终止cocos2d-x设备运行。
        cc.Director.getInstance().end();
    },
	//当触屏按下事件被响应时的处理。
    onTouchesBegan:function (touches, event) {
		//设置当前层的成员变量isMouseDown为ture
        this.isMouseDown = true;
    },
	//当触屏按下并移动事件被响应时的处理。
    onTouchesMoved:function (touches, event) {
		//判断如果isMouseDown为ture。
        if (this.isMouseDown) {
			//如果触点有效.
            if (touches) {
				//这里本来是显示触点的,但屏蔽了。
                //this.circle.setPosition(cc.p(touches[0].getLocation().x, touches[0].getLocation().y));
            }
        }
    },
	//当触屏松开事件响应时的处理
    onTouchesEnded:function (touches, event) {
		//设置当前层的成员变量isMouseDown为false
        this.isMouseDown = false;
    },
	//当触摸被取消(比如触摸过程中被来电打断),就会调用touchesCancelled方法。
    onTouchesCancelled:function (touches, event) {
		//控制台输出日志
        console.log("onTouchesCancelled");
    }
});
//创建一个cocos2d-x的场景的派生类HelloWorldScene。
var HelloWorldScene = cc.Scene.extend({
	//重载onEnter函数指定在场景被加载时要做的处理。
    onEnter:function () {
		//先调用基类cc.Scene的相应处理。
        this._super();
		//创建一个Helloworld层的实例并初始化。
        var layer = new Helloworld();
        layer.init();
		//将这个层加入到当前场景。
        this.addChild(layer);
    }
});
[/html]
    可以看出myApp.js实现了程序所用到的层和场景,它是本程序的核心逻辑,如果你有看过本博对WIN32版本所写的”Cocos2d-x HelloWorld深入分析”,关于层和场景的这些处理还是比较容易理解的。不过adjustSizeForWindow 这个函数对于画布的大小重置也让我很火大,在这里介绍一下修改成让网页窗口大小改变时画布大小自动设置为网页大小的方法。在修改之前先看下这个:

更多关于cocos2d-html5的信息请访问原创文章地址: 
http://blog.csdn.net/honghaier/article/details/8642553