环境、项目问题解决好,今天就开始我们的代码旅程。第一步,素材……

一、材料准备 

       打飞机版本很多,貌似这个游戏很流行啊,这个是我在百度的时候找到的素材,微信飞机大战

 

因为这个游戏比较简单,适合新手练手,所以拿他来开刀是再好不过了。当然里面许多素材是不需要的,实现基本的游戏逻辑只需要几张图片就够了,用到哪些后面有提及。 

打开项目文件夹,把需要用到的游戏图片资源导入游戏项目根目录下的Resourse文件夹中。注:游戏里要用到的资源是默认从这里开始为根目录去读取的,比如Resourse下有一张simple.png的图片,到时候可以在项目中直接读取资源路径”simple.png”即可。 


二、 屏幕大小调节 

下面就需要我们调戏,啊不是调试和编写我们的代码了。 

先打开vs2012,这个是我当前的IDE,依次点击文件->打开->项目/解决方案,打开游戏项目的proj.win32下的planegame.sln即可以导入游戏项目到vs 2012中。 

打飞机当然是竖屏了才有感觉,在AppDelagate那里修改下窗口的属性,调整游戏窗口的大小。 

打开src目录下的AppDelegate,src目录是我们游戏代码存放的地方,在applicationDidFinishLaunching()的如下位置添加如下代码 

现在可以跑下看看窗口改变的效果。 


三、 背景图片与滚动 

打开HelloWorldScene.cpp。 

以下段落可以略过。 

注意,这个是我们游戏的第一个场景,Cocos2d-x引擎帮我们封装了许多逻辑,以至于我们只需关注我们的游戏场景和处理逻辑。其实游戏的开始是从win32下的main.cpp里面的APIENTRY _tWinMain方法开始,这是个宏,其实就是c++语言的main方法,他定义了AppDelegate的一个对象,随后他会初始化AppDelegate的父类Application然后把他本身的指针给了父类的一个父类自身变量,这样做的目的是为了随后调用AppDelegate的applicationDidFinishLaunching()方法,这个是我们可以接触到用来初始化游戏窗口初始化导演类,还有初始化我们的第一个场景HelloWorld。最后main方法执行到了Application::getInstance()->run(),这里面封装的是我们游戏的循环和渲染等等逻辑。这时候我们的游戏已经跑起来了。这个就是简单的游戏启动过程。更加深层的代码有兴趣可以去研究研究源码,这也是开源带来的好处。 

打开HelloWorldScene.cpp,删除原有的创建菜单和背景的代码,再添加如下代码:

//注意,Vec2是Cocos2d-x的数据结构,里面包含两个float型的数据,分别用来表示x轴和y轴。

//setPosition是设置精灵的相对屏幕的位置(也就是世界坐标)。当前这两个参数表示设置精灵在屏幕的宽度一半,高度为零的位置,即屏幕最下方的中间位置。Cocos2d-x是以OpenGl的坐标的右手坐标为标准,屏幕的左下方为原点。visibleSize.width和height是屏幕的宽高,

bg1->setPosition(Vec2(origin.x + visibleSize.width/2,0));

//注意这里的锚点设置,为了图片永远在屏幕中间,x轴必须设置为0.5,其中的意思就是这个精灵向左移动精灵的一半宽度,y轴则是向下

bg1->setAnchorPoint(Vec2(0.5,0));

bg1->setTag(101); //设置Tag,以后可以通过这个标签找到这个精灵,以便对这个精灵进行操作。

this->addChild(bg1,0); //将背景精灵添加到层中。可以认为是当前的屏幕。其中的0是设置可见优先权,数值越小优先权越小,即当如果有其他的精灵的优先权比他大时,他会被遮挡住的。

//第二张背景图,是跟在第一张的上面,无缝连接,两张图形成不间断的地图滚动 ,getContentSize是获取精灵的size其中有它的宽度和高度。


1  auto bg2 = Sprite::create("background.png");

2  bg2->setPosition(Vec2(origin.x + visibleSize.width/2, bg1->getPositionY()+bg1->getContentSize().height));

3  bg2->setAnchorPoint(Vec2(0.5,0));

4  bg2->setTag(102);

5   this->addChild(bg2,0);



为什么有两个一样的背景精灵呢?这个是为了滚动地图设定的。 

先在HelloWorldScene.h文件中声明定时器的回调方法。添加如下代码: 

1
void backgroundMove(float f); //定时器回调的背景滚动方法

然后在HelloWorldScene.cpp中定义他。

1   void HelloWorld::backgroundMove(float f) //背景滚动的回调方法 { //背景滚动逻辑

2   auto bg1 = this->getChildByTag(101);

3   auto bg2 = this->getChildByTag(102); //当第二张图片退出屏幕时,把第一张图片设置到屏幕中,由于我的背景图片的高度是大于屏幕的高度的,所以判断逻辑要复杂点

4   if(bg2 -> getPositionY() + bg2->getContentSize().height <= -="">getVisibleSize().height)

5   {

6   bg1->setPositionY(-bg1->getContentSize().height + Director::getInstance()->getVisibleSize().height);

7   }

8   bg1->setPositionY(bg1->getPositionY()-3);

9   bg2->setPositionY(bg1->getPositionY()+bg1->getContentSize().height);

10  }

背景滚动逻辑第一次接触也是件麻烦事,下面一张图告诉你。下面的图表示的逻辑也是上面的回调方法的实现。 


1.jpg

游戏一开始是①状态,定时器会不断得修改背景精灵一和背景精灵二的坐标,当两者的坐标到达②的状态时,修改两者的坐标到③状态,其实也就是回到了游戏刚开始的状态①,然后就是你看到的无限背景滚动的效果了。注意背景图片的高度比屏幕小时两张图片就不足以做到以上效果了,该如何做呢,自己斟酌斟酌。不过这里的逻辑容易想清楚,但是其中的坐标计算也不是件头疼的事。 

最后,启动定时器,在添加两个背景精灵下面添加如下代码: 


好,背景滚动大功告成。跑一下看看效果如何吧。 

四、 游戏菜单项 

现在这个项目还比较简单,只有简单的开始游戏、退出游戏这两个选项,玩过游戏的都知道还有游戏设置啊、关于啊帮助啊这些。 

首先在HelloWorldScene.h中声明两个回调方法,这两个方法是在点击两菜单项时触发的函数。 

添加如下代码:

创建如下的菜单项: 


下面是回调函数的实现 

结束游戏的回调函数就直接调用创建时默认生成的。

1
2
void HelloWorld::menuStartCallback(Ref* pSender)
{ //开始游戏}

好,菜单项搞定,编译运行下,是不是发现有两个菜单项在屏幕中间了!!!点击退出还可以退出游戏。不过开始游戏逻辑还没写。呵呵,小有成就是吧。好我们继续。 

五、蠢蠢欲动的飞机 

为了体现出我们的飞机饥渴难耐啊不,是迫不及待想直捣敌人基地的雄姿,我们给他一个动画展示,展示它飞翔的雄姿。 

添加如下代码:

1
2
3
//添加飞机
 auto plane = Sprite::create("hero1.png"); plane->setPosition(visibleSize.width/2+origin.x,200);
 this->addChild(plane);


1
2
3
4
5
6
7
8
9
SpriteFrame * spriteFrame1 = SpriteFrame::create("hero1.png",Rect(0,0,102,126));        
 SpriteFrame * spriteFrame2 = SpriteFrame::create("hero2.png",Rect(0,0,102,126));       
 
 animation->addSpriteFrame(spriteFrame1);
 animation->addSpriteFrame(spriteFrame2);
 animation->setDelayPerUnit(0.15f);        //两张图片交互播放的间隔
 Animate * animate = Animate::create(animation);
 
 plane->runAction(RepeatForever::create(animate));     //开始执行动画

动画的执行是以精灵为单位,即可以为精灵添加动画,创建完动画,只要让精灵执行这个动作即可。如果执行完毕后,必须使用下面代码移除动画 

1
sprite->stopAllActions(); //停止所有动画,应该是清除了动画的内存。

Animate 是继承了Ref,所以的他的内存的Cocos2d-x框架自动回收的,我们不用自动释放他。但是如果不需要时也要手动停止它。因为我们当前的飞机精灵动画是在当前场景无限存活,不需手动停止它,当我们切换到开始游戏启动另一个场景时,它会自动清除内存,吧。为什么这么没自信,这是我猜的。嘿嘿,别冲动别冲动,有话好好说。

ok,飞机动画创建成功,启动运行一下吧。是不是看到小飞机的飞翔在天空中的雄姿了???

2-6 最终界面,如图: 

1425959698327278.gif

注:那个,秉着敬业爱业的精神,代码我会尽量解释清楚和一些路上捡的心得体会外加一堆废话。如果没有解释的,那是我也没看懂,百度去吧。倒是感觉这代码注释过多也是影响了代码的可见度,so如有影响,直接参照源代码。


Cocos2d-x 3.x《飞机大战》教程1:环境与创建项目  https://www.taikr.com/article/1587

Cocos2d-x 3.x《飞机大战》教程3:物理引擎的使用  https://www.taikr.com/article/1590

Cocos2d-x 3.x《飞机大战》教程4:游戏场景   https://www.taikr.com/article/1591
Cocos2d-x 3.x《飞机大战》教程5:敌我碰撞处理、分数计算、音乐播放  https://www.taikr.com/article/1592
Cocos2d-x 3.x《飞机大战》教程6:游戏结束场景  https://www.taikr.com/article/1593