驚訝歸驚訝吧,作為程序猿,我們更多時候還是淡定滴研究技術,因此本文也還是以如何開發這樣一款“腦殘游戲”為討論主題。
因為我們要做一個一模一樣的游戲,所以我們準備與官方撲騰小鳥長得非常相似的素材。由于這款游戲實在太火爆了,因此網絡上各種版本的山寨,所以素材并不難找。將這些素材使用 Texture Packer 工具拼接好,那么我們的游戲準備工作就完成了。
在這里我們使用 cocos2d-html5-2.2.2,在 cocos2d-html5官方,可以下載到這個版本。
另外我們使用一個叫做 cqwrap 的庫,這是一個我為 cocos2dx-jsb 和 cocos2dx-html5 定制的擴展,我在前一個開源的游戲 HappyGo 中也使用了它,可以從 github 的 這個項目中獲得它的代碼。
素材有了,框架也有了,這樣就可以開工了。
首先,這樣一個簡單的游戲,我們只需要一個場景,我們把它命名為 PlayScene
在 src/view/ 目錄下創建一個 play_scene.js 的文件,
var layers = require("cqwrap/layers"), BaseScene = require("cqwrap/scenes").BaseScene;var GameLayer = layers.GameLayer, BgLayer = layers.BgLayer;var MyScene = BaseScene.extend({ init:function () { this._super(); //添加靜態的背景 var bg = new BgLayer("res/bg.png"); this.addChild(bg); }});module.exports = MyScene;
我們首先創建了一個場景,在這個場景里面,我們添加了一個靜態的背景層。在cqwrap中,封裝了一個BgLayer的類,用來創建靜態的背景層。
這樣,我們就建立了一個只有一個遠背景層的場景。
我們在背景的基礎上創建一個前景層,小鳥、水管和其他元素都在這個層上
var layers = require("cqwrap/layers"), BaseScene = require("cqwrap/scenes").BaseScene;var GameLayer = layers.GameLayer, BgLayer = layers.BgLayer;var MyScene = BaseScene.extend({ init:function () { this._super(); //添加靜態的背景 var bg = new BgLayer("res/bg.png"); this.addChild(bg); //添加前景層 var layer = new MyLayer(); this.addChild(layer); }});module.exports = MyScene;
我們創建了一個MyLayer實例,作為前景層,我們在下面的代碼里展示 MyLayer 的基本結構:
var MyLayer = GameLayer.extend({ init: function(){ this._super(); return true; }});
我們僅僅創建了一個游戲層,這個層繼承自 GameLayer,GameLayer是cqwrap提供的一個類,用來創建包括精靈和動作事件的層。
首先我們得把素材資源加載進來,在 cocos2d-html5 中,我們可以通過 SpriteFrameCache 將之前打包的資源加載進來
var MyLayer = GameLayer.extend({ init: function(){ this._super(); //加載游戲素材資源 var cache = cc.SpriteFrameCache.getInstance(); cache.addSpriteFrames("res/flappy_packer.plist", "res/flappy_packer.png"); return true; }});
在 Flappy Bird 中,小鳥看起來在往前飛翔,是因為用地面的圖片做了一個動畫效果
var MyLayer = GameLayer.extend({ init: function(){ this._super(); //加載游戲素材資源 var cache = cc.SpriteFrameCache.getInstance(); cache.addSpriteFrames("res/flappy_packer.plist", "res/flappy_packer.png"); //顯示地面 var ground = cc.createSprite("res/ground.png", { anchor: [0, 0], xy: [0, 0], zOrder: 3 }); //讓地面運動起來 ground.moveBy(0.5, cc.p(-120, 0)).moveBy(0, cc.p(120, 0)).repeatAll().act(); this.addChild(ground); return true; }});
有了一個會“前進”的地面之后,我們只要在空中放置一只小鳥,那么就可以制造出飛翔的效果了:
//我是一只小小小小鳥var bird = cc.createSprite("bird1.png", { anchor: [0.5, 0], xy: [220, 650], zOrder: 2});this.addChild(bird);
cc.createSprite 是一個非常方便的方法,它是由cqwrap庫提供的額外方法,可以用來更方便地創建各種 Sprite.
現在我們創建了一只在空中的小鳥,它看起來會不斷向前進,但是看起來比較奇怪,因為它的翅膀是不動的。
我們可以執行動畫,讓小鳥動起來——
//給小鳥增加飛行動畫bird.animate(0.6, "bird1.png", "bird2.png", "bird3.png").repeat().act();bird.moveBy(0.3, cc.p(0, -20)).reverse().repeatAll().act();
現在,我們擁有了一只在空中不斷飛翔的小鳥
在 cqwrap 中,一個 GameLayer 是一個代理,它可以代理精靈們的事件,甚至可以代理自身的事件——
this.delegate(this); //將Layer的touch事件代理給Layer自身this.on("touchstart", function(){ //事件處理函數 });
我們添加讓小鳥往高處飛的事件——
this.on("touchstart", function(){ bird.stopAllActions(); bird.animate(0.2, "bird1.png", "bird2.png", "bird3.png").repeat().act(); var jumpHeight = Math.min(1280 - birdY, 125); bird.moveBy(0.2, cc.p(0, jumpHeight)).act(); bird.rotateTo(0.2, -30).act(); });
this.on("touchstart", function(){ var birdX = bird.getPositionX(); var birdY = bird.getPositionY(); var fallTime = birdY / 1000; bird.stopAllActions(); bird.animate(0.2, "bird1.png", "bird2.png", "bird3.png").repeat().act(); var jumpHeight = Math.min(1280 - birdY, 125); bird.moveBy(0.2, cc.p(0, jumpHeight)).act(); bird.rotateTo(0.2, -30).act(); bird.delay(0.2).moveTo(fallTime, cc.p(birdX, 316), cc.EaseIn, 2) .then(function(){ //小鳥掉落下來了 }).act(); bird.delay(0.2).rotateTo(fallTime, 90, 0, cc.EaseIn, 2).act(); });
現在我們有了一只會飛的小鳥,點擊鼠標讓它飛起來,如果不點擊,它就會掉到地上。
精靈的 animate 方法可以播放幀動畫,只需要傳給它每一幀的圖片就行了,repeat表示重復播放,可以傳一個參數表示次數,不傳的話表示無限次重復。
function createHose(dis){ var hoseHeight = 830; var acrossHeight = 250; var downHeight = 200 + (400 * Math.random() | 0); var upHeight = 1000 - downHeight - acrossHeight; var n = (self.hoses.length / 2) | 0; var hoseX = dis + 400 * n; var hoseDown = cc.createSprite("holdback1.png", { anchor: [0.5, 0], xy: [hoseX, 270 + downHeight - 830], }); var hoseUp = cc.createSprite("holdback2.png", { anchor: [0.5, 0], xy: [hoseX, 270 + downHeight + acrossHeight], }); self.addChild(hoseDown); self.addChild(hoseUp); var moveByDis = hoseX+500; hoseUp.moveBy(moveByDis/200, cc.p(-moveByDis, 0)).then(function(){ hoseUp.removeFromParent(true); }).act(); hoseDown.moveBy(moveByDis/200, cc.p(-moveByDis, 0)).then(function(){ createHose(-500); var idx = self.hoses.indexOf(hoseDown); self.hoses.splice(idx, 2); self.scoreBuf ++; hoseDown.removeFromParent(true); }).act(); self.hoses.push(hoseDown, hoseUp);};
我們將上下兩半部分水管的卡通分別計算位置,讓缺口保持固定的大小,出現位置隨機,然后將水管從屏幕外面往中間移動。
由于考慮性能,我們回收使用過的水管精靈,所以我們把當前可用的精靈放在一個隊列中,根據隊列的數量來計算新生成的水管精靈的位置。
//碰撞檢測self.checker = self.setInterval(function(){ //cc.log(111); var score = 0; //這里可以用 boundingBox 的 上下左右的中間點來判斷碰撞,會更準確一些 var box = bird.getBoundingBox(); var bottom = cc.p(box.x + box.width / 2, box.y); var right = cc.p(box.x + box.width, box.y + box.height / 2); var left = cc.p(box.x, box.y + box.height / 2); var top = cc.p(box.x + box.width / 2, box.y + box.height); self.hoses.some(function(hose){ var box = hose.getBoundingBox(); if(hose.getPositionX() <= 220) score ++; //cc.log(score); if(hose.getPositionX() > 0 && hose.getPositionX() < 720 //&& cc.rectIntersectsRect(hose.getBoundingBox(), bird.getBoundingBox()) && (cc.rectContainsPoint(box, left) || cc.rectContainsPoint(box, right) || cc.rectContainsPoint(box, top) || cc.rectContainsPoint(box, bottom))){ //cc.log([hose.getBoundingBox(), bird.getBoundingBox()]); layerMask.fadeIn(0.1).fadeOut(0.1).act(); Audio.playEffect("audio/sfx_hit.ogg"); self.status = "falling"; return true; } }); //碰撞后小鳥掉下來,地面、水管都停止運動 if(self.status == "falling"){ self.clearInterval(self.checker); ground.stopAllActions(); self.hoses.forEach(function(o){ o.stopAllActions(); //o.removeFromParent(true); }); }}, 50)
基本的原理就這些了,剩下的就是一些細節,最終完整版的撲騰小鳥演示 點這里體驗
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com