zoukankan      html  css  js  c++  java
  • Cocos2d-x 3.0final 终结者系列教程16-《微信飞机大战》实现

    看到cocos2d-x推出了3.1版本号,真是每月一次新版本号,速度。

    另一个好消息就是http://cn.cocos2d-x.org/上线了,祝贺!啥时候把我的视频和教程放上去呢?!!


    视频下载地址:http://pan.baidu.com/s/1jGiLOG2  

    本文介绍一款纵版射击游戏的实现,开发环境:

    win7

    vs2012

    cocos2d-x3.0final

    android adt

    android ndk r9

    首先看下最后的效果:

    (图1。微信飞机大战执行效果)

    源代码下载地址:http://download.csdn.net/detail/sdhjob/7513863

    一、游戏资源准备

    menu.png

    首页的菜单背景

    about.png

    关于界面

    help.png

    帮助界面

    background3.png

    游戏场景的

    end.png
     
      bullet.png aaa.png ccc.png 子弹和敌机文件

    爆炸效果

    (被CSDN的博客加上水印。就这样了,大家还是下源代码吧)

    二、Cocos2d-x3.0项目创建和VS2012编译

        2.1进入命令提示符输入:

             cocos new -p com.xdl.game -d c:/users/administrator/desktop/game2014 -l cpp planegame

        2.2 然后进入桌面/game2014/planegame/proj.win32

        2.3使用vs2012打开

               planegame.sln

        2.4 按F5编译执行项目,将会出现HelloWorld的界面

       2.5.把全部的资源复制到 桌面/game2014/planegame/Resources文件夹下(处理图片还有3个声音文件)   

    三、场景跳转和主菜单实现

      3.1.改动HelloWorldScene,在init方法中加入3个菜单栏目:

      auto gameItem=MenuItemFont::create("StartGame",CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));

      auto helpItem=MenuItemFont::create("Help",CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));

       auto aboutItem=MenuItemFont::create("About",CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));

      gameItem->setPosition(Point(origin.x + visibleSize.width/2 - closeItem->getContentSize().width/2,200 ));

      helpItem->setPosition(Point(origin.x + visibleSize.width/2 - closeItem->getContentSize().width/2,150 ));

        aboutItem->setPosition(Point(origin.x + visibleSize.width/2 - closeItem->getContentSize().width/2,100 ));

    gameItem->setColor(Color3B::BLACK);

    helpItem->setColor(Color3B::BLACK);

    aboutItem->setColor(Color3B::BLACK);

    gameItem->setTag(11);

    helpItem->setTag(12);

    aboutItem->setTag(13);

    3.2 改动菜单的回调方法MenuCallBack

     void HelloWorld::menuCloseCallback(Ref* pSender)

    {  MenuItem * nowItem=(MenuItem *)pSender;

      SimpleAudioEngine::getInstance()->playEffect("select.wav"); //播放音乐

      switch(nowItem->getTag())

      {

      case 10:

        #if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)

        MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");

         return;

        #endif

        Director::getInstance()->end();

        #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)

    exit(0);

    #endif

    break;

      case11://game

      Director::getInstance()->replaceScene(GameScene::createScene());

      break;

      case12://help

      Director::getInstance()->replaceScene( TransitionFlipY::create(1,HelpScene::createScene()));

      break;

      case13://about

      Director::getInstance()->replaceScene( TransitionFlipY::create(1,AboutScene::createScene()));

      break;

    }

    }

    这样通过导演对象实现场景跳转。

    3.3 在帮助。关于场景实现跳转回来

    须要在项目中加入3个.h文件和3个.cpp文件,保存到Classes文件夹下(注意文件夹,不要保存错了)

    HelpScene.h

    HelpScene.cpp

    GameScene.h

    GameScene.cpp

    AboutScene.h

    AboutScene.cpp

    若想在帮助和关于场景跳转回来须要增加触摸消息处理,见HelpScene的init方法:

    bool HelpScene::init(){

    if(!Layer::init())

    {

    return false;

    }

    auto spbk=Sprite::create("help.png");

    spbk->setPosition(Point::ZERO);

    spbk->setAnchorPoint(Point::ZERO);

    this->addChild(spbk);

    EventListenerTouchOneByOne * touch=EventListenerTouchOneByOne::create();

    touch->onTouchBegan=[](Touch * touch,Event * event){

    return true;

    };

    touch->onTouchMoved=[](Touch * touch,Event * event){

    Director::getInstance()->replaceScene( HelloWorld::createScene());

    };

    _eventDispatcher->addEventListenerWithSceneGraphPriority(touch,this);

    return true;

    }

    四、游戏场景背景滚动

    4.1首先在GameScene的init方法加入背景层,代码例如以下:

    auto spbk=Sprite::create("background4.png");

    spbk->setAnchorPoint(Point::ZERO);

    spbk->setPosition(Point::ZERO);

    this->addChild(spbk);

    spbk->setTag(10);

    auto spbk02=Sprite::create("background4.png");

    spbk02->setAnchorPoint(Point::ZERO);

    spbk02->setPosition(Point::ZERO);

    spbk02->setPositionY(spbk->getPositionY()+680);

    this->addChild(spbk02);

    spbk02->setTag(11);

    为什么要加入2遍呢?由于要实现循环的贴图效果,

    4.2 在init方法计划任务

    this->schedule(schedule_selector(GameScene::moveBackground),0.01);

    4.3 处理计划任务 

    void  GameScene::moveBackground(float t)

    {

    auto spbk=this->getChildByTag(10);

    auto spbk02=this->getChildByTag(11);

    spbk->setPositionY(spbk->getPositionY()-1);

    if(spbk->getPositionY()<-680)

    {

    spbk->setPositionY(0);

    }

    spbk02->setPositionY(spbk->getPositionY()+680);

    }

    这样就形成了循环贴图的效果,游戏游戏是横版的,还有将这样的循环贴图通过多层实现场景纵深效果(近处图层移动快,远处移动慢)

    当背景相对屏幕向下移动,飞机相对屏幕不懂,但飞机相对背景则向上飞行(好多游戏的主角事实上一直在屏幕中间)

    五、飞机动画和触摸控制

    5.1 在init方法创建飞机动画

    auto spPlane=Sprite::create();

    spPlane->setTag(110);

    spPlane->setPosition(Point(160,240));

    this->addChild(spPlane);

    Vector<SpriteFrame*> allframe;//保存动画的每一帧

    for(int i=0;i<4;i++)

    {

    SpriteFrame * sf=SpriteFrame::create("player.png",Rect(i*47,0,47,56));

    allframe.pushBack(sf);

    }

    Animation * ani=Animation::createWithSpriteFrames(allframe,0.1);

    spPlane->runAction(RepeatForever::create(Animate::create(ani)));

    5.2 通过触摸控制飞机移动

    EventListenerTouchOneByOne * event=EventListenerTouchOneByOne::create();

    event->setSwallowTouches(true);

    event->onTouchBegan=CC_CALLBACK_2(GameScene::onTouchBegan,this);

    event->onTouchMoved=CC_CALLBACK_2(GameScene::onTouchMoved,this);

    event->onTouchEnded=CC_CALLBACK_2(GameScene::onTouchEnded,this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(event,this);

    -------在GameScene中加入下面方法

    bool GameScene::onTouchBegan(Touch *touch, Event *unused_event){

    px=touch->getLocation().x;

    py=touch->getLocation().y;

    return true;

    }

    void GameScene::onTouchMoved(Touch *touch, Event *unused_event){

    int mx=(touch->getLocation().x-px);

     int my=(touch->getLocation().y-py);

    auto spPlane=this->getChildByTag(110);

    spPlane->runAction(MoveBy::create(0,Point(mx,my)));

    px=touch->getLocation().x;

    py=touch->getLocation().y;

    }

    void GameScene::onTouchEnded(Touch *touch, Event *unused_event){

    }

    这样就实现了在屏幕滑动改变飞机坐标。

    六、子弹发射

    6.1 在GameScene中加入成员Vector用来保存全部的子弹层

    Vector<Sprite *> allBullet;

    6.2 计划任务,定时产生子弹和移动子弹

    this->schedule(schedule_selector(GameScene::newBullet),0.5);

    this->schedule(schedule_selector(GameScene::moveBullet),0.01

    6.3 实现产生子弹的方法和移动子弹的方法

    void  GameScene::newBullet(float t){

    auto spPlane=this->getChildByTag(110);

    Sprite * bullet=Sprite::create("bullet3.png");

    bullet->setPosition(spPlane->getPosition());

    this->addChild(bullet);

    this->allBullet.pushBack(bullet);

    }

    void  GameScene::moveBullet(float t){

    for(int i=0;i<allBullet.size();i++)

    auto nowbullet=allBullet.at(i);

    nowbullet->setPositionY(nowbullet->getPositionY()+3);

    if(nowbullet->getPositionY()>Director::getInstance()->getWinSize().height)

    {  

    nowbullet->removeFromParent();

    allBullet.eraseObject(nowbullet);

    i--;

    }

    }

    }

    七、敌机实现

    敌机实现与子弹实现类似,仅仅只是一个是向上飞,一个是向下飞。

    7.1 在GameScene中加入成员Vector用来保存全部的子弹层

    Vector<Sprite *> allEnemy;

    7.2 加入产生敌机的任务

    this->schedule(schedule_selector(GameScene::newEnemy),0.5);

    this->schedule(schedule_selector(GameScene::moveEnemy),0.01)

    7.3 实现敌机任务方法,这里产生2种不同类型的敌机

    void  GameScene::newEnemy(float t){

    Sprite * enemy=nullptr;

    int num=rand()%10;//随机数0-9

    if(num>=3)

    {

        enemy=Sprite::create("aaa.png");

    enemy->setTag(1000);

    }

    else

    {   enemy=Sprite::create("ccc.png");

        enemy->setTag(2000);

    }

    enemy->setPosition(Point(rand()%300+10,500));

    this->addChild(enemy);

    this->allEnemy.pushBack(enemy);

    }

    void  GameScene::moveEnemy(float t){

    for(int i=0;i<allEnemy.size();i++)

    auto nowenemy=allEnemy.at(i);

    nowenemy->setPositionY(nowenemy->getPositionY()-3);

    if(nowenemy->getPositionY()<0)

    {  

    nowenemy->removeFromParent();

    allEnemy.eraseObject(nowenemy);

    i--;

    }

    }

    }

    八、碰撞检測和加分

    8.1 加入和引擎主线程fps一致的任务处理方法update

    this->scheduleUpdate();

    8.2 实现碰撞检測游戏逻辑

    void GameScene::update(float t){

    auto spPlane=this->getChildByTag(110);

    Rect rp(spPlane->getPositionX(),spPlane->getPositionY(),47,56);

    for(int i=0;i<allEnemy.size();i++)

    auto nowenemy=allEnemy.at(i);

        Rect er(nowenemy->getPositionX(),nowenemy->getPositionY(),40,50);

      if(rp.intersectsRect(er))

    { //爆炸

      newBomb(spPlane->getPositionX(),spPlane->getPositionY());

          //移除敌机

      nowenemy->removeFromParent();

      allEnemy.eraseObject(nowenemy);

      i--;

      //播放音乐

      SimpleAudioEngine::getInstance()->playEffect("explo.wav");

      //停止全部任务和动作

      //Director::getInstance()->getActionManager()->pauseAllRunningActions();

      this->pauseSchedulerAndActions();

      auto spover=Sprite::create("end.png");

      spover->setPosition(Point::ZERO);

      spover->setAnchorPoint(Point::ZERO);

      this->addChild(spover);

      auto act=Sequence::create(

      DelayTime::create(2), //等待2秒

      CallFunc::create(this,callfunc_selector(GameScene::jumpToMenu)),//运行跳转方法

      NULL

      );

      this->runAction(act);

    }

    //敌机和子弹碰撞检測

      for(int j=0;j<allBullet.size();j++)

      { auto nowbullet=allBullet.at(j);

          Rect br(nowbullet->getPositionX(),nowbullet->getPositionY(),20,20);

      if(er.intersectsRect(br))

      {//改动分数

      Label * labScore=(Label*)this->getChildByTag(120);

      score+=nowenemy->getTag();

        //爆炸效果

        newBomb(nowbullet->getPositionX(),nowbullet->getPositionY());

    //粒子效果

    auto ps=ParticleSystemQuad::create("bomb.plist");

    ps->setPosition(Point(nowbullet->getPositionX(),nowbullet->getPositionY()));

    this->addChild(ps);

      labScore->setString(String::createWithFormat("score:%d",score)->_string);

      //移除子弹层

      nowbullet->removeFromParent();

      allBullet.eraseObject(nowbullet);

      //移除敌机层

      nowenemy->removeFromParent();

      allEnemy.eraseObject(nowenemy);

      i--;

      //音效

      SimpleAudioEngine::getInstance()->playEffect("explo.wav");

      break;

       }

      }

    }

    }


    九、爆炸效果

    当碰撞检測到,在飞机位置产生一个新的爆炸效果层,播放动画。动画播放完毕自己主动删除自己。

    void GameScene::newBomb(int x,int  y)

    {

    Vector<SpriteFrame*> allframe;

    for(int i=0;i<7;i++)

    {

    SpriteFrame * sf=SpriteFrame::create("boom.png",Rect(i*44,0,44,47));

    allframe.pushBack(sf);

    }

    Animation * ani=Animation::createWithSpriteFrames(allframe,0.03);

    auto sprite=Sprite::create();

    Action * act=Sequence::create(

    Animate::create(ani),  //动画

    CCCallFuncN::create(sprite,callfuncN_selector(GameScene::killMe)),//调用自删除方法

    NULL);

    this->addChild(sprite);

    sprite->setPosition(Point(x,y));

    sprite->runAction(act);

    }

    void GameScene::killMe(Node * pSender)//自删除 pSender就是sprite这里是CallFunN,会传递节点过来

    {

    pSender->removeFromParentAndCleanup(true);

    }

    十、粒子特效和音乐播放

    10.1 首先使用粒子编辑器编辑粒子文件bomb.plist(详见源代码)

    10.2 加载粒子层

    auto ps=ParticleSystemQuad::create("bomb.plist");

    ps->setPosition(Point(nowbullet->getPositionX(),nowbullet->getPositionY()));

    this->addChild(ps);

    (假设粒子层也须要自删除。能够參考爆炸效果)

    10.3 播放音乐和音效

     首先要引入声音处理头文件和命名空间

    #include "SimpleAudioEngine.h"

    using namespace CocosDenshion;

    然后就能够使用1条语句来播放音乐和音效了

    SimpleAudioEngine::getInstance()->playBackgroundMusic("game.mp3",true); //播放背景音乐

    SimpleAudioEngine::getInstance()->playEffect("explo.wav"); //播放音效

    十一、判定死亡

    在Update方法中,眼下仅仅增加了当敌机和飞机碰撞则死亡,实际游戏中可能有多有条件

    如:敌机子弹和飞机碰撞、时间计数等,

    void GameScene::update(float t){

    auto spPlane=this->getChildByTag(110);

    Rect rp(spPlane->getPositionX(),spPlane->getPositionY(),47,56);

    for(int i=0;i<allEnemy.size();i++)

    {  auto nowenemy=allEnemy.at(i);

        Rect er(nowenemy->getPositionX(),nowenemy->getPositionY(),40,50);

      if(rp.intersectsRect(er))

    { //爆炸

      newBomb(spPlane->getPositionX(),spPlane->getPositionY());

          //移除敌机

      nowenemy->removeFromParent();

      allEnemy.eraseObject(nowenemy);

      i--;

      //播放音乐

      SimpleAudioEngine::getInstance()->playEffect("explo.wav");

      //停止全部任务和动作

      //Director::getInstance()->getActionManager()->pauseAllRunningActions();

      this->pauseSchedulerAndActions();

      auto spover=Sprite::create("end.png");

      spover->setPosition(Point::ZERO);

      spover->setAnchorPoint(Point::ZERO);

      this->addChild(spover);

      auto act=Sequence::create(

      DelayTime::create(2), //等待2秒

      CallFunc::create(this,callfunc_selector(GameScene::jumpToMenu)),//运行跳转方法

      NULL

      );

      this->runAction(act);

    }

    。。。

    。。。

    这里也使用了动作序列,先等待2秒,然后自己主动调用jumpToMenu方法跳转到主菜单

    void GameScene::jumpToMenu()//ï◊™µΩ÷˜≤Àµ•

    {

    SimpleAudioEngine::getInstance()->stopBackgroundMusic();

    Director::getInstance()->replaceScene(HelloWorld::createScene());

    }

    十二、移植到Android平台

    12.1 eclipse导入项目

    在VS2012中开发好项目之后,使用adt工具(ecplise)导入项目,import

    桌面game2014plangameproj.android

    12.2 改动jni/android.mk文件

    加入编译的文件

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := cocos2dcpp_shared
    LOCAL_MODULE_FILENAME := libcocos2dcpp
    LOCAL_SRC_FILES := hellocpp/main.cpp
                       ../../Classes/AppDelegate.cpp
                       ../../Classes/GameScene.cpp
                       ../../Classes/HelpScene.cpp
                       ../../Classes/HelloWorldScene.cpp
                       ../../Classes/AboutScene.cpp 

    LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes

    LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static
    LOCAL_WHOLE_STATIC_LIBRARIES += cocosdenshion_static
    LOCAL_WHOLE_STATIC_LIBRARIES += box2d_static

    include $(BUILD_SHARED_LIBRARY)

    $(call import-module,2d)
    $(call import-module,audio/android)
    $(call import-module,Box2D)

    12.3 进入cmd命令提示符

    进入项目文件夹 

    cd  c:/Users/Administrator/Desktop/game2014/planegame

    编译当前项目

    cocos compile -p android

    (等吧。

    。。最后会在c:/Users/Administrator/Desktop/game2014/planegame/proj.android/libs/armsabi/生成一个.so文件。 成功了!

    )

    12.4 拷贝Cocos2d-x android库文件

    到 c:/Users/Administrator/Desktop/game2014/planegame/cocos/2d/platform/android/java/src/ 拷贝org目录到

    c:/Users/Administrator/Desktop/game2014/planegame/proj.android/src 文件夹

    在adt中刷新项目(这时候项目的错误会消除)

    12.5 打包项目

    使用手机数据线连接电脑,开启调试模式

    能够直接通过run,来把该项目安装到手机。

    之后使用android打包向导打包生成apk。

    大功告成,大家最好还是投入半天时间做个《微信飞机大战》。下节课讲《2048》的开发。



  • 相关阅读:
    oracle客户端连接服务器基本教程
    java中字符串处理、串联和转换的几个常用方法,以及如果需要自己编程实现的具体实施步骤。
    面试相关
    java中byte是什么类型,和int有什么区别
    (华为机试大备战)java。多了解了解最常用的那个类库的方法对处理字符串的方法
    (华为)以下代码片段将创建一个仅保存大写字符的字段。
    (华为)以下代码片段将创建一个仅保存大写字符的字段。
    每个程序中只有一个public类,主类?
    我的第一个长程序,虽然是直接抄了书上,可是还是出现了两次拼写错误,最终还是找到异常的答案,改过来了。
    实践证明:当类想实现两个监听接口的时候,必须把两个都设置成内部类,不可能一个是外部类实现,一个是内部类实现。这样容易捕获错误,出现异常。
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/6788913.html
Copyright © 2011-2022 走看看