最近のモバイル ゲーム界における最大の勝者は間違いなく Flappy Bird の作者です。こんなに単純で愚かなゲームがこれほど人気になるとは誰も考えていませんでした。
驚いたことに、プログラマーとして私たちは技術を冷静に勉強することがほとんどなので、この記事でもそのような「頭を使わないゲーム」の開発方法に焦点を当てています。
正確なゲームを作りたいので、公式の羽ばたき鳥によく似た素材を用意します。このゲームは非常に人気があるため、インターネット上にはさまざまなコピーバージョンが存在しており、素材を見つけるのは難しくありません。 Texture Packer ツールを使用してこれらのマテリアルを接合すると、ゲームの準備が完了します。
ここでは、cocos2d-html5 公式 Web サイトからダウンロードできる cocos2d-html5-2.2.2 を使用します。
さらに、cqwrap というライブラリを使用します。これは、以前のオープンソース ゲーム HappyGo でも使用した、cocos2dx-jsb および cocos2dx-html5 用にカスタマイズした拡張機能です。プロジェクト内のコードを取得します。
マテリアルとフレームワークが揃ったので、作業を開始できます。
まず、このような単純なゲームに必要なシーンは 1 つだけです。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 クラスがカプセル化されて静的な背景レイヤーが作成されます。
このようにして、遠方の背景レイヤーが 1 つだけあるシーンを確立しました。
背景に基づいて前景レイヤーを作成し、鳥、水道管、その他の要素がこのレイヤー上にあります
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 ライブラリによって提供される追加メソッド。これを使用すると、さまざまなスプライトをより便利に作成できます。
これで、空中に鳥が作成されました。鳥は前に進み続けているように見えますが、羽が伸びているので奇妙に見えます。動かない。
鳥を動かすアニメーションを実行できます—
//给小鸟增加飞行动画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(); });
水道管の上半分と下半分の漫画の位置をそれぞれ計算し、ギャップを固定サイズに保ち、位置をランダムにして、水道管を画面の外側から中央に向かって移動します。
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)