最近在看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); }
我们仔细观察游戏可以把游戏分为2个场景:
1.菜单场景
2.游戏场景
由于我们在AppDelegate创建了一个CCScene,可以用它来作为菜单场景,再新建一个GamePlayScene作为游戏场景(也可以自己新建一个MenuScene作为菜单场景)。
游戏可以大致分为以下个层:
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继承自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(); }
效果如下:
与背景层类似,继承自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); }
*******************************************
至此,第一屏已经山寨完毕,楼主去写第二屏算法去了。