最近在看cocos2d開發,想找個游戲練練手,礙於沒有策划能力,只能從山寨做起。目前最火的游戲,莫過於是這個像素鳥《Flapy Bird》了,下面就開始山寨它吧。
開發環境
系統:Windows8
IDE:Visual Studio 2012
資源文件
隨便那個網站下載一個flapybird的apk(下載地址:http://app.suning.com/d.php?pack=com.dotgears.flappybird),改后綴名為.rar,解壓之后就能找到游戲引用的資源。

打開assets\gfx,會找到一張atlas.png,包含了所有的圖片資源,sound中是聲音資源。
項目搭建
因為是學習項目,就自己搭建cocos框架,不使用cocos模版。新建項目,選擇XNAGame->WindowsPhone游戲。
添加AppDelegate.cs類,繼承 CCApplication,添加構造函數,重寫applicationDidFinishLaunching方法;
public class AppDelegate : CCApplication
{
public AppDelegate(Game game, GraphicsDeviceManager graphics)
: base(game, graphics)
{
CCApplication.sm_pSharedApplication = this;
}
public override bool applicationDidFinishLaunching()
{
CCDirector pDirector = CCDirector.sharedDirector();
pDirector.setOpenGLView();
pDirector.DisplayFPS = true;
pDirector.deviceOrientation = ccDeviceOrientation.CCDeviceOrientationPortrait; ;
pDirector.animationInterval = 1.0 / 60;
CCScene pScene = new CCScene();
pDirector.runWithScene(pScene);
return base.applicationDidFinishLaunching();
}
}
修改Game1.cs構造函數,添加代碼:
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
this.graphics.IsFullScreen = true;
// Windows Phone 的默認幀速率為 30 fps。
TargetElapsedTime = TimeSpan.FromTicks(333333);
// 延長鎖定時的電池壽命。
InactiveSleepTime = TimeSpan.FromSeconds(1);
CCApplication application = new AppDelegate(this, graphics);
this.Components.Add(application);
}
添加場景(CCScene)
我們仔細觀察游戲可以把游戲分為2個場景:
1.菜單場景
2.游戲場景
由於我們在AppDelegate創建了一個CCScene,可以用它來作為菜單場景,再新建一個GamePlayScene作為游戲場景(也可以自己新建一個MenuScene作為菜單場景)。
添加層(CCLayer)
游戲可以大致分為以下個層:
1.背景層(BackgroundLayer)
2.移動的道路層(RoadLayer)
3.游戲層(GamePlayLayer)
4.菜單層(MainLayer)
在菜單場景中,我們需要用到BackgroundLayer,RoadLayer和MainLayer。修改AppDelegate.cs代碼:
public override bool applicationDidFinishLaunching()
{
CCDirector pDirector = CCDirector.sharedDirector();
pDirector.setOpenGLView();
pDirector.DisplayFPS = true;
pDirector.deviceOrientation = ccDeviceOrientation.CCDeviceOrientationPortrait; ;
pDirector.animationInterval = 1.0 / 60;
CCScene pScene = new CCScene();
pScene.addChild(BackgroundLayer.node(), (int)LayerTags.Background, (int)LayerTags.Background);
pScene.addChild(RoadLayer.node(), (int)LayerTags.Road, (int)LayerTags.Road);
pScene.addChild(MainLayer.node(), (int)LayerTags.Game, (int)LayerTags.Game);
pDirector.runWithScene(pScene);
return base.applicationDidFinishLaunching();
}
public enum LayerTags
{
Background = 0,
Game = 1,
Road = 2
}
背景層(BackgroundLayer)
BackgroundLayer繼承自CCLayer,重寫靜態方法node;
public static new CCLayer node()
{
BackgroundLayer ret = new BackgroundLayer();
if (ret.init())
{
return ret;
}
return null;
}
下面我們就需要畫背景圖片了,重寫init方法
首先我們需要算出資源圖片與屏幕大小的縮放比例:
//288,511是背景圖的大小 sX = CCDirector.sharedDirector().getWinSize().width / 288f; sY = CCDirector.sharedDirector().getWinSize().height / 512f;
由於此游戲的資源圖片是一整張大圖,所以我們需要根據區域進行讀取,觀察資源圖片我們可以發現,存在2個背景圖片,看來需要隨機出現一個背景圖片,完整init代碼:
public override bool init()
{
sX = CCDirector.sharedDirector().getWinSize().width / 288f;
sY = CCDirector.sharedDirector().getWinSize().height / 512f;
Random random = new Random();
int number = random.Next(0, 2);
CCRect ccRect;
if (number == 0)
{
ccRect = new CCRect(0, 0, 288, 512);
}
else
{
ccRect = new CCRect(292, 0, 288, 512);
}
CCSprite backgroundCcSprite = CCSprite.spriteWithFile("Images/background", ccRect);
backgroundCcSprite.position = new CCPoint(240, 400);
backgroundCcSprite.scaleY = sY;
backgroundCcSprite.scaleX = sX;
addChild(backgroundCcSprite);
return base.init();
}
效果如下:

道路層(RoadLayer)
與背景層類似,繼承自CCLayer並重寫方法node()、init();
public static new CCLayer node() { RoadLayer ret = new RoadLayer(); if (ret.init()) { return ret; } return null; } public override bool init() { sX = CCDirector.sharedDirector().getWinSize().width / 288f; sY = CCDirector.sharedDirector().getWinSize().height / 512f; CCSprite downRoadCcSprite1 = CCSprite.spriteWithFile("Images/background", new CCRect(584, 0, 336, 112)); downRoadCcSprite1.position = new CCPoint(240, 72); downRoadCcSprite1.scaleX = sX; downRoadCcSprite1.scaleY = sY; addChild(downRoadCcSprite1, 1, (int)SpriteTags.Road); return base.init(); }

讓道路動起來
此處我采用的是添加2個道路CCSprite,像小火車一樣跟着,每動一幀,2個道路的坐標減少4,當第一個的x坐標等於0之后,將其放到第二個的后面,也就是x坐標等480(屏幕寬度)。
添加一個schedule定時器執行更新道路坐標方法,代碼如下:
public override bool init()
{
sX = CCDirector.sharedDirector().getWinSize().width / 288f;
sY = CCDirector.sharedDirector().getWinSize().height / 512f;
downRoadCcSprite1 = CCSprite.spriteWithFile("Images/background", new CCRect(584, 0, 336, 112));
downRoadCcSprite1.position = new CCPoint(240, 72);
downRoadCcSprite1.scaleX = sX;
downRoadCcSprite1.scaleY = sY;
addChild(downRoadCcSprite1, 1, (int)SpriteTags.Road);
downRoadCcSprite2 = CCSprite.spriteWithFile("Images/background", new CCRect(584, 0, 336, 112));
downRoadCcSprite2.position = new CCPoint(480, 72);
downRoadCcSprite2.scaleX = sX;
downRoadCcSprite2.scaleY = sY;
addChild(downRoadCcSprite2, 1, (int)SpriteTags.Road);
this.schedule(updateDownRoad, 0.01f);
return base.init();
}
private void updateDownRoad(float dt)
{
downRoadCcSprite1.position = new CCPoint(downRoadCcSprite1.position.x - 4, downRoadCcSprite1.position.y);
if (downRoadCcSprite1.position.x == 0)
{
downRoadCcSprite1.position = new CCPoint(480, downRoadCcSprite1.position.y);
}
downRoadCcSprite2.position = new CCPoint(downRoadCcSprite2.position.x - 4, downRoadCcSprite2.position.y);
if (downRoadCcSprite2.position.x == 0)
{
downRoadCcSprite2.position = new CCPoint(480, downRoadCcSprite2.position.y);
}
}

添加菜單
開始我做菜單的方法和上面一樣從大圖中截取圖片生成CCMenuItemSprite,但是加入后發現菜單並非是一直顯示的,不知道哪里設置出了問題:
CCSprite testCcSprite = CCSprite.spriteWithFile("Images/background", new CCRect(705, 235, 107, 58));
testCcSprite.scaleX = sX;
testCcSprite.scaleY = sY;
CCMenuItemSprite testMenuItemSprite = CCMenuItemSprite.itemFromNormalSprite(testCcSprite, testCcSprite);
CCMenu testmenuOperate = CCMenu.menuWithItems(testMenuItemSprite);
testmenuOperate.position = new CCPoint(240, 205);
this.addChild(testmenuOperate);
效果:

沒辦法了,只能切成單個圖片,然后用另外一種方式試試,發現居然可行,求解!!!

CCMenuItemImage rateCcMenuItemSprite = CCMenuItemImage.itemFromNormalImage("Images/rate", "Images/rate_pressed", this, rateCallback);
rateCcMenuItemSprite.scaleX = sX;
rateCcMenuItemSprite.scaleY = sY;
CCMenu menuRate = CCMenu.menuWithItems(rateCcMenuItemSprite);
menuRate.position = new CCPoint(240, 350);
this.addChild(menuRate);
CCMenuItemImage startCcMenuItemSprite = CCMenuItemImage.itemFromNormalImage("Images/play", "Images/play_pressed", this, playCallback);
startCcMenuItemSprite.scaleX = sX;
startCcMenuItemSprite.scaleY = sY;
CCMenuItemImage scoreCcMenuItemSprite = CCMenuItemImage.itemFromNormalImage("Images/rank", "Images/rank_pressed", this, rankCallback);
scoreCcMenuItemSprite.scaleX = sX;
scoreCcMenuItemSprite.scaleY = sY;
CCMenu menuOperate = CCMenu.menuWithItems(startCcMenuItemSprite, scoreCcMenuItemSprite);
menuOperate.alignItemsHorizontallyWithPadding(30);
menuOperate.position = new CCPoint(240, 205);
this.addChild(menuOperate);
CCMenuItemImage startCcMenuItemSprite = CCMenuItemImage.itemFromNormalImage("Images/play", "Images/play_pressed", this, playCallback);第一個參數為默認圖片,第二個為按下時候圖片,playCallback為按下的處理事件

然后添加Logo
CCSprite logoCcSprite = CCSprite.spriteWithFile("Images/background", new CCRect(700, 177, 185, 55));
logoCcSprite.position = new CCPoint(240, 550);
logoCcSprite.scaleX = sX;
logoCcSprite.scaleY = sY;
addChild(logoCcSprite);
添加會動的小鳥
此處采用CCAnimation進行繪制小鳥的動畫,在init中調用initActiveBird()
private void initActiveBird()
{
CCTexture2D texture = CCTextureCache.sharedTextureCache().addImage("Images/background");//2D紋理
CCSpriteFrame birFrame6 = CCSpriteFrame.frameWithTexture(texture, new CCRect(117, 976, 40, 50));
CCSpriteFrame birFrame7 = CCSpriteFrame.frameWithTexture(texture, new CCRect(117, 975, 40, 50));
CCSpriteFrame birFrame8 = CCSpriteFrame.frameWithTexture(texture, new CCRect(117, 974, 40, 50));
CCSpriteFrame birFrame9 = CCSpriteFrame.frameWithTexture(texture, new CCRect(117, 975, 40, 50));
CCSpriteFrame birFrame10 = CCSpriteFrame.frameWithTexture(texture, new CCRect(117, 976, 40, 50));
CCSpriteFrame birFrame0 = CCSpriteFrame.frameWithTexture(texture, new CCRect(61, 982, 40, 50));
CCSpriteFrame birFrame1 = CCSpriteFrame.frameWithTexture(texture, new CCRect(61, 981, 40, 50));
CCSpriteFrame birFrame2 = CCSpriteFrame.frameWithTexture(texture, new CCRect(61, 980, 40, 50));
CCSpriteFrame birFrame3 = CCSpriteFrame.frameWithTexture(texture, new CCRect(61, 979, 40, 50));
CCSpriteFrame birFrame4 = CCSpriteFrame.frameWithTexture(texture, new CCRect(61, 978, 40, 50));
CCSpriteFrame birFrame5 = CCSpriteFrame.frameWithTexture(texture, new CCRect(61, 977, 40, 50));
CCSpriteFrame birFrame11 = CCSpriteFrame.frameWithTexture(texture, new CCRect(5, 977, 40, 50));
CCSpriteFrame birFrame12 = CCSpriteFrame.frameWithTexture(texture, new CCRect(5, 978, 40, 50));
CCSpriteFrame birFrame13 = CCSpriteFrame.frameWithTexture(texture, new CCRect(5, 979, 40, 50));
CCSpriteFrame birFrame14 = CCSpriteFrame.frameWithTexture(texture, new CCRect(5, 980, 40, 50));
CCSpriteFrame birFrame15 = CCSpriteFrame.frameWithTexture(texture, new CCRect(5, 981, 40, 50));
CCSpriteFrame birFrame16 = CCSpriteFrame.frameWithTexture(texture, new CCRect(5, 982, 40, 50));
CCSprite birdCcSprite = CCSprite.spriteWithSpriteFrame(birFrame0);
birdCcSprite.position = new CCPoint(240, 430);
birdCcSprite.scaleX = sX;
birdCcSprite.scaleY = sY;
addChild(birdCcSprite);
List<CCSpriteFrame> animFrames = new List<CCSpriteFrame>();
animFrames.Add(birFrame0);
animFrames.Add(birFrame1);
animFrames.Add(birFrame2);
animFrames.Add(birFrame3);
animFrames.Add(birFrame4);
animFrames.Add(birFrame5);
animFrames.Add(birFrame6);
animFrames.Add(birFrame7);
animFrames.Add(birFrame8);
animFrames.Add(birFrame9);
animFrames.Add(birFrame10);
animFrames.Add(birFrame11);
animFrames.Add(birFrame12);
animFrames.Add(birFrame13);
animFrames.Add(birFrame14);
animFrames.Add(birFrame15);
animFrames.Add(birFrame16);
CCAnimation animation = CCAnimation.animationWithFrames(animFrames, 0.03f);
CCAnimate animate = CCAnimate.actionWithAnimation(animation, true);
CCActionInterval seq = (CCActionInterval)(CCSequence.actions(animate));
birdCcSprite.runAction(CCRepeatForever.actionWithAction(seq));
}

場景切換
在點擊開始按鈕時候,我們需要進行場景切換,跳到游戲場景,為playCallback添加實現
private void playCallback(CCObject sender) { GamePlayScene gamePlayScene = new GamePlayScene(); gamePlayScene.addChild(BackgroundLayer.node(), (int)LayerTags.Background, (int)LayerTags.Background); gamePlayScene.addChild(RoadLayer.node(), (int)LayerTags.Road, (int)LayerTags.Road); gamePlayScene.addChild(GamePlayLayer.node(), (int)LayerTags.Game, (int)LayerTags.Game); float t = 0.8f; CCTransitionScene reScene = CCTransitionFade.transitionWithDuration(t, gamePlayScene); CCDirector.sharedDirector().replaceScene(reScene); }

*******************************************
至此,第一屏已經山寨完畢,樓主去寫第二屏算法去了。
