第一步,环境搭建
首先去官网下载cocos2d-x压缩包:https://cocos2d-x.org/download
这里我选择的是3.x版本的完整SDK:
解压到任意目录运行python setup.py安装即可,不过这里的python版本是2.x。
新建工程:cocos new -l js 工程名
进入工程目录运行:cocos run -p web
在浏览器上看到如下画面即可表示程序运行成功:
第二步,cocos2d-js基础
上面的环境搭建和使用命令行新建工程是一般的流程,不过如果我们想具体了解一下cocos是如何运行一个程序的话,可以手动来创建一个简单的工程,在下载好的SDK工具包中需要使用的是如下图所示的web文件夹:
运行cocos需要服务器环境,于是在服务器的www目录下新建一个game文件夹,将SDK中的web文件夹解压到此处并重命名为cocos2d-html5,下图是必要的工程结构:
cocos2d-html5是web游戏引擎应该不需要解释;
res文件夹用来存放一些音频或者图片资源;
src存放游戏的脚本资源,主要在该文件夹下编写游戏逻辑和加载游戏的媒体资源,包含app.js和resource.js;
index.html是运行游戏的默认web页面;
main.js是用于开始加载游戏cocos2d-js脚本;
project.json是运行游戏必要的配置文件。
准备好工程目录以后开始编写游戏的代码:
首先是index.html:
1 <!DOCTYPE html> 2 <html lang="zh-CN"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>Document</title> 8 </head> 9 <body> 10 <canvas id="gameCanvas"></canvas> 11 <script src="cocos2d-html5/CCBoot.js"></script> 12 <script src="main.js"></script> 13 </body> 14 </html>
canvas节点是运行游戏必要的“舞台”,然后再引入CCBoot.js和main.js文件,html代码应该不怎么需要解释吧。
然后是main.js:
1 cc.game.onStart = function() { 2 cc.view.setDesignResolutionSize(1280, 800, cc.ResolutionPolicy.SHOW_ALL); 3 cc.director.runScene(new gameScene()); 4 }; 5 cc.game.run();
cc是cocos2d对象的简称,新建的游戏叫做cc.game,首先给游戏开始运行事件设置一些参数:
使用setDesignResolutionSize 方法来设置分辨率,同时设置游戏适应浏览器全屏运行;
然后运行gameScene场景,这是我们自己定义的游戏场景,后续会详细介绍。
接着是project.json配置文件:
1 { 2 "debugMode" : 0, 3 "showFPS" : false, 4 "frameRate" : 60, 5 "id" : "gameCanvas", 6 "renderMode" : 0, 7 "engineDir":"cocos2d-html5", 8 "modules" : ["cocos2d"], 9 "jsList" : [ 10 "src/resource.js", 11 "src/app.js" 12 ] 13 }
debugMode:有0~6共7个等级,用来设置显示哪些错误或警告信息,这里设置为0即不显示信息;
showFPS : 是否显示FPS,玩游戏大家应该都懂吧;
frameRate:设置游戏帧数,24帧人眼就不会感到画面闪烁了,因此60帧应该算比较流畅的;
id:用来指定哪个DOM节点来运行游戏,还记得前面设置过的id为gameCanvas的canvas节点吗,你设置了没有?
renderMode:渲染模式,官方推荐设为0;
engineDir :引擎目录,就是前面SDK中解压的web文件夹,不过改名为cocos2d-html5了;
jsList :数组,说明需要哪些必要的游戏脚本文件,一般都放置在src文件夹下。
最后,来编写游戏的基本逻辑src/app.js:
1 var gameScene = cc.Scene.extend({ 2 onEnter: function () { 3 this._super(); 4 console.log("my first game starts here"); 5 } 6 });
这里主要定义gameScene场景方法,将在main.js文件中建立场景而运行。现在打开浏览器输入localhost/game应该可以在控制台看到输出了“my first game starts here”字样,到目前为止游戏里还没有精灵(一般在游戏里将可以与玩家交互的图片称为“精灵”),下面将导入图片资源来创建精灵res/player.png:
这是一只可爱的像素鸟,现在打开src/resource.js文件:
1 var gameResources = [ 2 "res/player.png" 3 ];
然后修改main.js来预加载该图片资源:
1 cc.game.onStart = function() { 2 cc.view.setDesignResolutionSize(1280, 800, cc.ResolutionPolicy.SHOW_ALL); 3 cc.LoaderScene.preload(gameResources, function () { 4 cc.director.runScene(new gameScene()); 5 }, this); 6 }; 7 cc.game.run();
最后修改src/app.js来显示精灵:
1 var gameScene = cc.Scene.extend({ 2 onEnter: function () { 3 this._super(); 4 var gameLayer = new game(); 5 gameLayer.init(); 6 this.addChild(gameLayer); 7 } 8 }); 9 10 var game = cc.Layer.extend({ 11 init: function () { 12 this._super(); 13 var target = cc.Sprite.create("res/player.png"); 14 this.addChild(target, 0); 15 } 16 });
这个时候打开浏览器,emmm。。。注意屏幕左下角应该可以看到1/4像素鸟:
不是很明显,这是因为cocos的锚点设置默认在图片中心,而坐标系原点又在屏幕左下角(一般的程序都是在左上角),引用cocos中文网站的一张图片加以说明:
注意每张图片中的红点,它表示图片的锚点位置。
现在来修改精灵的显示位置,由于我这里设置的分辨率为1280x800,所以精灵的显示位置应该是(640, 400),修改src/app.js:
1 var gameScene = cc.Scene.extend({ 2 onEnter: function () { 3 // same as before 4 } 5 }); 6 7 var game = cc.Layer.extend({ 8 init: function () { 9 this._super(); 10 var target = cc.Sprite.create("res/player.png"); 11 this.addChild(target, 0); 12 target.setPosition(640, 400); 13 } 14 });
然后重新打开浏览器可以看到像素鸟已经在屏幕中间显示了。有了显示精灵的基础,接下来我们可以来制作一些简单逻辑的小游戏。
第三步,简单逻辑游戏的开发:Concentration game
Concentration game 是一个和连连看差不多的小游戏,首先准备素材:
在前面的基础上需要修改的主要是src文件夹下的js文件,这也是模块化设计的优点。先修改src/resource.js文件导入资源:
1 var gameResources = [ 2 "res/cover.jpg", 3 "res/tile_0.jpg", 4 "res/tile_1.jpg", 5 "res/tile_2.jpg", 6 "res/tile_3.jpg", 7 "res/tile_4.jpg", 8 "res/tile_5.jpg", 9 "res/tile_6.jpg", 10 "res/tile_7.jpg" 11 ];
接下来是修改src/app.js文件,都是一些简单逻辑,部分函数不太明白的可以去cocos官方查看API手册,这里不再详细展开,源码如下:
1 // 定义瓷砖序列 2 var gameArray = [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7]; 3 // 翻开的瓷砖 4 var pickedTiles = []; 5 // 显示移动步数 6 var scoreText; 7 var moves = 0; 8 9 // 打乱数组:从数组末端出发,每次与前面随机一个元素交换 10 var shuffle = function(v) { 11 for(var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x); 12 return v; 13 }; 14 15 // 游戏场景方法 16 var gameScene = cc.Scene.extend({ 17 onEnter: function() { 18 gameArray = shuffle(gameArray); 19 this._super(); 20 var gameLayer = new game(); 21 gameLayer.init(); 22 this.addChild(gameLayer); 23 } 24 }); 25 26 // 游戏主要逻辑层 27 var game = cc.Layer.extend({ 28 init: function () { 29 this._super(); 30 // 背景渐变 31 var gradient = cc.LayerGradient.create(cc.color(0, 0, 0, 255), cc.color(0x46, 0x82, 0xB4, 255)); 32 this.addChild(gradient); 33 // 移动步数 34 scoreText = cc.LabelTTF.create("Moves: 0", "Arial", "32", cc.TEXT_ALIGNMENT_CENTER); 35 this.addChild(scoreText); 36 scoreText.setPosition(100, 50); 37 // 4x4瓷砖 38 for (var i = 0; i < 16; i++) { 39 var tile = new MemoryTile(); 40 tile.pictureValue = gameArray[i]; 41 this.addChild(tile, 0); 42 tile.setPosition(440 + 50 + Math.floor(i / 4) * 100, 200 + 50 + i % 4 * 100); 43 } 44 } 45 }); 46 47 // 瓷砖对象 48 var MemoryTile = cc.Sprite.extend({ 49 ctor: function() { 50 this._super(); 51 this.initWithFile("res/cover.jpg"); 52 cc.eventManager.addListener(listener.clone(), this); 53 } 54 }); 55 56 // 响应点击事件 57 var listener = cc.EventListener.create({ 58 event: cc.EventListener.TOUCH_ONE_BY_ONE, 59 swallowTouches: true, 60 onTouchBegan: function(touch, event) { 61 if(pickedTiles.length < 2) { 62 var target = event.getCurrentTarget(); 63 var location = target.convertToNodeSpace(touch.getLocation()); 64 var targetSize = target.getContentSize(); 65 var targetRectangle = cc.rect(0, 0, targetSize.width, targetSize.height); 66 if (cc.rectContainsPoint(targetRectangle, location)) { 67 if(pickedTiles.indexOf(target) == -1) { 68 target.initWithFile("res/tile_" + target.pictureValue + ".jpg"); 69 pickedTiles.push(target); 70 if(pickedTiles.length == 2) { 71 checkTiles(); 72 } 73 } 74 } 75 } 76 } 77 }); 78 79 // 两块瓷砖是否相同 80 function checkTiles() { 81 moves++; 82 scoreText.setString("Moves: " + moves); 83 setTimeout(function() { 84 // 不相同则全部还原 85 if(pickedTiles[0].pictureValue != pickedTiles[1].pictureValue) { 86 pickedTiles[0].initWithFile("res/cover.jpg"); 87 pickedTiles[1].initWithFile("res/cover.jpg"); 88 // 移除相同瓷砖 89 } else { 90 var layer = cc.director.getRunningScene().getChildren()[0]; 91 layer.removeChild(pickedTiles[0]); 92 layer.removeChild(pickedTiles[1]); 93 } 94 pickedTiles = []; 95 }, 1000); 96 }
这里有一个需要注意的是移除瓷砖的编写方法,刚开始我以为要将gameLayer上面的tile对象移除可以这么写:
1 gameScene.gameLayer.removeChild(pickedTiles[0]); 2 gameScene.gameLayer.removeChild(pickedTiles[1]);
因为很容易可以看出gameLayer是gameScene的子节点,然而犯的错误是gameScene只是一个方法并没有实例化,gameLayer是已经实例化的,如果将它声明到全局是可以直接调用gameLayer.removeChild()来移除tile节点的。