在本教程中,我们将尝试添加到我们的跑酷游戏币和障碍。
在本教程中,我们的主角应该能够收集硬币时,他正在和他死的时候,他与障碍物发生碰撞。
我们还将介绍如何平铺地图编辑器的游戏关卡设计。由于游戏逻辑有点复杂的比以前,
所以我们将重构代码之前我们增加新的游戏组件。
二、准备
在我们开始之前,让我们完成准备的东西。
三、资源和全局设置
我们将为我们的跑酷游戏添加两个游戏元素。所以我们需要添加一些全球性的整数标签来识别每个比赛项目。
让我们添加下面的代码片段在globals.js结束:
// collision type for chipmunk if(typeof SpriteTag == "undefined") { var SpriteTag = {}; SpriteTag.runner = 0; SpriteTag.coin = 1; SpriteTag.rock = 2; };
在这里,我们使用0,1,2分别代表运动员,硬币和岩石。
我们还介绍了另一spritesheet命名background.png和background.plist。我们已把硬币和岩石精灵到spritesheet命名background.png。
详情如何包装这些精灵离开的下一个分段。
接下来,让我们复制资源文件到我们的研究和进一步的参考目录添加两个变量。
var res = { helloBG_png : "res/helloBG.png", start_n_png : "res/start_n.png", start_s_png : "res/start_s.png", PlayBG_png : "res/PlayBG.png", runner_png : "res/running.png", runner_plist : "res/running.plist", map_png : "res/map.png", map00_tmx : "res/map00.tmx", map01_tmx : "res/map01.tmx", background_png :"res/background.png", background_plist : "res/background.plist" }; var g_resources = [ //image res.helloBG_png, res.start_n_png, res.start_s_png, res.PlayBG_png, res.runner_png, res.runner_plist, res.map_png, res.map00_tmx, res.map01_tmx, res.background_png, res.background_plist ];四、 用 TexturePacker 包装硬币 和岩石
在前面的章节中,我们已经学会了怎样把一堆小精灵变成一个大的紧凑型sprite图片。
首先,你应该推出TexturePacker和拖动所有资产RES /TexturePacker /硬币和岩石。(注:你可以整个游戏资源从下载之前。)
通过拖动资源,你应该用一些路径就像XXX /八/研究/background.png或XXX /八/研究/ background.plist指定数据文件的格式和结构。
如果你不想任何优化的spritesheet,只是把它们和新闻发布生成最终的spritesheet。
五、tiledmap对象层介绍
我们用我们的水平tiledmap地图,但缺乏比赛项目。因此在这一节中,我们将讨论如何设计与tiledmap对象层水平项目。
添加硬币对象层
首先,我们将添加硬币对象层。
发射瓷砖和开放map00.tmx和map01.tmx。
创建一个命名的map00.tmx和map01.tmx硬币对象层。
通过拖动矩形对象在地图设计对象层。
你可以改变矩形的大小和它的位置。您也可以复制或删除对象。
对设计对象层提示:你可以在平铺地图图层透明度的变化,你可以很容易地放置对象。
添加岩石对象层
的过程中创造的岩石对象层或多或少是创建对象层相同的硬币。
所以我们将离开了自己的实现。
重构backgroundlayer类和添加一些辅助方法
有时,当你编码时,你会发现它很难增加新的功能到现有的结构。
这是一个糟糕的代码结构,我们应该停止并且开始重构工作。
重构BackgroundLayer类
因为我们将花栗鼠的物理身体进入我们的背景,所以我们需要一种方法来获得playscene空间对象的创建。
让我们在背景层选择器函数名称的变化和传递参数命名空间到它。我们还应该添加一个新的成员变量为
backgroundlayer类。下面的代码片段:
ctor:function (space) { this._super(); // clean old array here this.objects = []; this.space = space; this.init(); },
在这里,我们已经添加了额外的初始化代码。我们添加了一个数组的指定对象并初始化为空数组。
(*注:你应该调用这个方法。()权转让后的this.space =空间。因为我们在init方法*创建物理对象)
添加辅助方法 添加成员变量到backgroundlayer类:
space:null, spriteSheet:null, objects:[],在init 方法 初始化 sprite集合图片 :
// create sprite sheet cc.spriteFrameCache.addSpriteFrames(res.background_plist); this.spriteSheet = cc.SpriteBatchNode.create(res.background_png); this.addChild(this.spriteSheet);添加一个方法 叫 loadobject 初始化 岩石 和硬币 。
loadObjects:function (map, mapIndex) { // add coins var coinGroup = map.getObjectGroup("coin"); var coinArray = coinGroup.getObjects(); for (var i = 0; i < coinArray.length; i++) { var coin = new Coin(this.spriteSheet, this.space, cc.p(coinArray[i]["x"] + this.mapWidth * mapIndex,coinArray[i]["y"])); coin.mapIndex = mapIndex; this.objects.push(coin); } // add rock var rockGroup = map.getObjectGroup("rock"); var rockArray = rockGroup.getObjects(); for (var i = 0; i < rockArray.length; i++) { var rock = new Rock(this.spriteSheet, this.space, rockArray[i]["x"] + this.mapWidth * mapIndex); rock.mapIndex = mapIndex; this.objects.push(rock); } },
在这里,我们重申所有对象的信息在平铺地图创建响应的花栗鼠刚体。最后我们存储这些对象到对象数组。
这些代码是自解释的。你应该只注重mapindex参数。我们使用的参数计算,我们应该将刚体。
我们需要在init方法结束通话loadobject法在第一个屏幕地图创建物理对象。
this.loadObjects(this.map00, 0); this.loadObjects(this.map01, 1);
用于删除未使用的ChipMunk刚体添加两个辅助方法。
第一种方法称为removeobjects。它消除了一个对象的mapindex。这里是实现:
removeObjects:function (mapIndex) {
while((function (obj, index) {
for (var i = 0; i < obj.length; i++) {
if (obj[i].mapIndex == index) {
obj[i].removeFromParent();
obj.splice(i, 1);
return true;
}
}
return false;
})(this.objects, mapIndex));
},
另外一个方法是 removeObjectByShape:
removeObjectByShape:function (shape) {
for (var i = 0; i < this.objects.length; i++) {
if (this.objects[i].getShape() == shape) {
this.objects[i].removeFromParent();
this.objects.splice(i, 1);
break;
}
}
},
六、包装:加入checkandreload方法和一次性的逻辑生成
在地图上移动,我们也应该给loadobject方法重新“硬币和石头”。
我们也可以通过调用removeobjects方法删除所有未使用的对象。
下面的代码片段:
checkAndReload:function (eyeX) { var newMapIndex = parseInt(eyeX / this.mapWidth); if (this.mapIndex == newMapIndex) { return false; } if (0 == newMapIndex % 2) { // change mapSecond this.map01.setPositionX(this.mapWidth * (newMapIndex + 1)); this.loadObjects(this.map01, newMapIndex + 1); } else { // change mapFirst this.map00.setPositionX(this.mapWidth * (newMapIndex + 1)); this.loadObjects(this.map00, newMapIndex + 1); } this.removeObjects(newMapIndex - 1); this.mapIndex = newMapIndex; return true; },
七、添加硬币和石头
现在是时候添加硬币和岩石的实现。尽管实现的细节,你也应该注意到在这两种设计思想
等级。在这里,我们宁愿从CC代替cc.sprite类。我们让每一个对象的实例cc.sprite。
八、 硬币 类的设计 与实现
创建一个新的文件名为coin.js。我们将在本文件中定义我们的硬币类。确保你有这场位于您的src目录。
派生的类命名硬币从cc.Class,让我们在整个实施一看:
var Coin = cc.Class.extend({ space:null, sprite:null, shape:null, _mapIndex:0,// which map belongs to get mapIndex() { return this._mapIndex; }, set mapIndex(index) { this._mapIndex = index; }, /** Constructor * @param {cc.SpriteBatchNode *} * @param {cp.Space *} * @param {cc.p} */ ctor:function (spriteSheet, space, pos) { this.space = space; // init coin animation var animFrames = []; for (var i = 0; i < 8; i++) { var str = "coin" + i + ".png"; var frame = cc.spriteFrameCache.getSpriteFrame(str); animFrames.push(frame); } var animation = cc.Animation.create(animFrames, 0.2); var action = cc.RepeatForever.create(cc.Animate.create(animation)); this.sprite = cc.PhysicsSprite.create("#coin0.png"); // init physics var radius = 0.95 * this.sprite.getContentSize().width / 2; var body = new cp.StaticBody(); body.setPos(pos); this.sprite.setBody(body); this.shape = new cp.CircleShape(body, radius, cp.vzero); this.shape.setCollisionType(SpriteTag.coin); //Sensors only call collision callbacks, and never generate real collisions this.shape.setSensor(true); this.space.addStaticShape(this.shape); // add sprite to sprite sheet this.sprite.runAction(action); spriteSheet.addChild(this.sprite, 1); }, removeFromParent:function () { this.space.removeStaticShape(this.shape); this.shape = null; this.sprite.removeFromParent(); this.sprite = null; }, getShape:function () { return this.shape; } });
让我们一块解释代码块。
首先,我们把三的成员变量命名为:空间,雪碧和形状。我们将使用这些变量来创建对象的物理身体的硬币
它的显示属性。
然后,我们增加了一个成员变量_mapindex。我们使用的获取/设置语法糖来定义变量的访问。
ctor方法是硬币类的构造函数。我们将创建一个spritesheet硬币类,空间和位置的对象。
由于金币是圆形的,所以我们已经创建了附加到刚体的圆形。矢量函数的其余部分是自我解释。
最后,我们需要定义一个做清理工作的方法。这是removefromparent方法。它首先去除刚体的空间,然后拆下雪碧
从它的父。该getshape方法仅仅是一个用于访问存储在硬币的形状对象的getter方法。
九、 岩石 类的设计 与实现
设计原则的岩类是更多或更少的硬币类除了刚性的形状类型的部分。
因为我们的岩类是一个矩形框。因此我们使用cp.boxshape在硬币类取代cc.circleshape。
这里是rock.js完整的源代码:
var Rock = cc.Class.extend({ space:null, sprite:null, shape:null, _map:0,// which map belong to get map() { return this._map; }, set map(newMap) { this._map = newMap; }, /** Constructor * @param {cc.SpriteBatchNode *} * @param {cp.Space *} * @param {cc.p} */ ctor:function (spriteSheet, space, posX) { this.space = space; this.sprite = cc.PhysicsSprite.create("#rock.png"); var body = new cp.StaticBody(); body.setPos(cc.p(posX, this.sprite.getContentSize().height / 2 + g_groundHight)); this.sprite.setBody(body); this.shape = new cp.BoxShape(body, this.sprite.getContentSize().width, this.sprite.getContentSize().height); this.shape.setCollisionType(SpriteTag.rock); this.space.addStaticShape(this.shape); spriteSheet.addChild(this.sprite); }, removeFromParent:function () { this.space.removeStaticShape(this.shape); this.shape = null; this.sprite.removeFromParent(); this.sprite = null; }, getShape:function () { return this.shape; } });十、提升playScene
playscene onenter功能重构
首先,让我们添加一个额外的数组的指定shapestoremove并初始化在playscene.js onenter函数开始的。
//the following line goes in init member variable define area shapesToRemove :[], //the following line goes at the beginning of the *onEnter* function. this.shapesToRemove = [];其次,修改backgroundlayer创作。 在这里, 我们 只是通过 空间 对象 的 构造函数 backgroundlayer 。
this.gameLayer.addChild(new BackgroundLayer(this.space), 0, TagOfLayer.background);
十一、添加碰撞检测回调
首先,我们应该调用这两个方法在initphyiscs方法结束
// setup chipmunk CollisionHandler this.space.addCollisionHandler(SpriteTag.runner, SpriteTag.coin, this.collisionCoinBegin.bind(this), null, null, null); this.space.addCollisionHandler(SpriteTag.runner, SpriteTag.rock, this.collisionRockBegin.bind(this), null, null, null);
该addcollisionhandler方法需要一个回调发生碰撞时。
然后,我们定义这两个回调函数来处理主角和硬币和岩石撞击。
collisionCoinBegin:function (arbiter, space) { var shapes = arbiter.getShapes(); // shapes[0] is runner this.shapesToRemove.push(shapes[1]); }, collisionRockBegin:function (arbiter, space) { cc.log("==game over"); },在背景层删除未使用的刚体。 你应该在 最后 更新 方法 中添加以下代码 :
// Simulation cpSpaceAddPostStepCallback for(var i = 0; i < this.shapesToRemove.length; i++) { var shape = this.shapesToRemove[i]; this.gameLayer.getChildByTag(TagOfLayer.background).removeObjectByShape(shape); } this.shapesToRemove = [];我们无法删除物理身体的物理模拟过程中。 因此我们 使用一个额外的 数组的指定 shapestoremove 将 需要 删除的 时间数据 。
十二、包装所有东西
祝贺你!你几乎到达终点。在我们打调试按钮来查看结果。让我们添加一些额外的胶水代码连接在一起的一切。
开放project.json和添加两个数组项在jslist阵列的末端。
"jsList" : [ "src/resource.js", "src/app.js", "src/AnimationLayer.js", "src/BackgroundLayer.js", "src/PlayScene.js", "src/StatusLayer.js", "src/globals.js", "src/coin.js", "src/rock.js" ]
建立并运行!恭喜,我们做到了!:)
让我们看看我们最后的果实:
十二、总结
在本教程中,我们已经度过了一个很长的旅程。但是值得的,不是吗?
我们已经学会了如何使用tiledmap的对象层设计复杂的游戏水平和如何定制自己的类来扩展你的代码结构。
你可以从这里http://cocos2d-x.org/docs/tutorial/framework/html5/parkour-game-with-javascript-v3.0/chapter8/res/Parkour.zip下载完整的源代码。
十三、接下来怎么办
在下一个教程中,我们将介绍如何不断更新游戏HUD我们还会添加在逻辑和简单的手势识别为我们的游戏制作游戏
玩家跳过障碍。保持协调!