• 基于Cocos2d-x-1.0.1的飞机大战游戏开发实例(中)


    接《基于Cocos2d-x-1.0.1的飞机大战游戏开发实例(上)》

    三、代码分析

    1.界面初始化

     1 bool PlaneWarGame::init()
     2 {
     3     bool bRet = false;
     4     do 
     5     {
     6         CC_BREAK_IF(! CCLayer::init());
     7 
     8         _size = CCDirector::sharedDirector()->getWinSize();
     9 
    10         // 设置触摸可用
    11         this->setIsTouchEnabled(true);
    12         // 从窗口中取消息
    13         CCDirector::sharedDirector()->getOpenGLView()->SetWin32KeyLayer( this );
    14 
    15         // 初始化游戏背景
    16         CC_BREAK_IF(!initBackground());
    17         // 菜单1(暂停/声音/炸弹)
    18         CC_BREAK_IF(!initMenu1());
    19         // 菜单2(继续/重新开始/返回)
    20         CC_BREAK_IF(!initMenu2());
    21         // 菜单3(重新开始/返回)
    22         CC_BREAK_IF(!initMenu3());
    23 
    24         // 创建玩家飞机
    25         _player = new PlaySprite;
    26         _player->setPosition(CCPointZero);
    27         addChild(_player);
    28 
    29         // 调度器:定时调用自定义的回扫函数
    30         this->schedule( schedule_selector(PlaneWarGame::gameLoop));
    31         this->schedule( schedule_selector(PlaneWarGame::shoot), 0.1 );
    32         this->schedule( schedule_selector(PlaneWarGame::addEnemy), 0.3 );
    33         this->schedule( schedule_selector(PlaneWarGame::addProp), 5 );
    34 
    35         // 初始化两个数组
    36         _enemys = CCArray::array();
    37         _enemys->retain();
    38         _bullets = CCArray::array();
    39         _bullets->retain();
    40         
    41         // 背景音乐
    42         CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic(
    43             "planewar/game_music.wav", true);
    44 
    45         bRet = true;
    46     } while (0);
    47 
    48     return bRet;
    49 }

    初始化的工作包括:

    • 设置触摸可用
    •   设置可自定义处理消息
    •       初始化游戏背景
    •   初始化三个菜单(界面菜单/游戏暂停时的菜单/游戏结束时的菜单)
    •   初始化两个CCArray类型的数组,分别用来存储有效的敌机精灵对象和子弹对象
    •       设置需要定时调用函数的调度器
    •       设置背景音乐
    •       添加玩家角色

    2.初始化游戏背景

     1 bool PlaneWarGame::initBackground()
     2 {
     3     // 显示分数
     4     _label = CCLabelBMFont::labelWithString("0123456789","planewar/font.fnt");
     5     _label->setPosition(ccp(220,_size.height-20));
     6     _label->setColor(ccc3(0,0,0));
     7     this->addChild(_label, 1);
     8 
     9     // 显示炸弹数量
    10     CCLabelBMFont* bombcount = CCLabelBMFont::labelWithString("X09","planewar/font.fnt");
    11     bombcount->setAnchorPoint(ccp(0,0));
    12     bombcount->setPosition(ccp(73,10));
    13     bombcount->setColor(ccc3(0,0,0));
    14     addChild(bombcount, 10,111);
    15 
    16     // 添加背景
    17     CCSprite* bg1 = CCSprite::spriteWithFile("planewar/bg1.png");
    18     if (!bg1)return false;
    19     bg1->setAnchorPoint(ccp(0,1));
    20     bg1->setPosition( ccp(0, _size.height) );
    21     this->addChild(bg1, 0, 11);
    22 
    23     bg1->runAction(
    24         CCSequence::actions(
    25         CCMoveBy::actionWithDuration(10,ccp(0,-720)),
    26         CCCallFunc::actionWithTarget(this, callfunc_selector(PlaneWarGame::bg1roll)),NULL));
    27 
    28     CCSprite* bg2 = CCSprite::spriteWithFile("planewar/bg2.png");
    29     if (!bg1)return false;
    30     bg2->setAnchorPoint(ccp(0,1));
    31     bg2->setPosition( ccp(0, _size.height*2) );
    32     this->addChild(bg2, 0, 12);
    33 
    34     bg2->runAction(
    35         CCSequence::actions(
    36         CCMoveBy::actionWithDuration(20,ccp(0,-1440)),
    37         CCCallFunc::actionWithTarget(this, callfunc_selector(PlaneWarGame::bg2roll)),NULL));
    38     return true;
    39 }
    40 
    41 void PlaneWarGame::bg1roll(){
    42     //运动出屏幕重设位置,运动
    43     CCSprite * bg = (CCSprite *)getChildByTag(11);
    44     bg->setPosition(ccp(0,1440));
    45     CCAction* seq = CCSequence::actions(
    46         CCMoveBy::actionWithDuration(20,ccp(0,-1440)),
    47         CCCallFunc::actionWithTarget(this, callfunc_selector(PlaneWarGame::bg1roll)),NULL);
    48     bg->runAction(seq);
    49 }
    50 
    51 void PlaneWarGame::bg2roll(){
    52     //运动出屏幕重设位置,运动
    53     CCSprite * bg = (CCSprite *)getChildByTag(12);
    54     bg->setPosition(ccp(0,1440));
    55     CCAction* seq = CCSequence::actions(
    56         CCMoveBy::actionWithDuration(20,ccp(0,-1440)),
    57         CCCallFunc::actionWithTarget(this, callfunc_selector(PlaneWarGame::bg2roll)),NULL);
    58     bg->runAction(seq);
    59 }

     初始化游戏背景时,需要注意的是:要做出飞机向上走的视觉效果,就要把背景向下滚动,这里使用两个背景bg1和bg2,大小分别为屏幕大小。初始bg1在屏幕内,bg2在屏幕上方,两个同事向下运动;当bg1刚刚运动到屏幕外,bg2刚好盖住屏幕,bg1挪到屏幕上方向下运动...循环操作,就能做出循环滚动的背景效果了。

    3.子弹的处理

     1 // 确定子弹类型
     2 void PlaneWarGame::shoot(float dt)
     3 {
     4     if (_isOver)return;
     5     if(_player==NULL) return;
     6 
     7     if(_player->_bulletKind == BK_SINGLE)
     8     {
     9         CCSprite *bullet = CCSprite::spriteWithFile(
    10             "planewar/plane.png", CCRectMake(112,2,9,17));
    11         addBullet(bullet,_player->getPlayerPt());
    12     }else if (_player->_bulletKind == BK_DOUBLE)
    13     {
    14         CCSprite *bullet = CCSprite::spriteWithFile(
    15             "planewar/plane.png", CCRectMake(66,238,8,15));
    16         addBullet(bullet,ccp(_player->getPlayerPt().x-20,_player->getPlayerPt().y));
    17         CCSprite *bullet1 = CCSprite::spriteWithFile(
    18             "planewar/plane.png", CCRectMake(66,238,8,15));
    19         addBullet(bullet1,ccp(_player->getPlayerPt().x+20,_player->getPlayerPt().y));
    20     }
    21 }
    22 // 添加子弹
    23 void PlaneWarGame::addBullet(CCSprite* bullet, CCPoint pt)
    24 {    
    25     bullet->setPosition(pt);
    26     this->addChild(bullet);
    27 
    28     bullet->runAction( CCSequence::actions(
    29         CCMoveTo::actionWithDuration(0.5, ccp(pt.x,_size.height+bullet->getContentSize().height/2)),
    30         CCCallFuncN::actionWithTarget(this,callfuncN_selector(PlaneWarGame::spriteMoveFinished)), 
    31         NULL) );
    32 
    33     bullet->setTag(1);
    34     _bullets->addObject(bullet);
    35 }
    36 // 回收
    37 void PlaneWarGame::spriteMoveFinished(CCNode* sender)
    38 {
    39     CCSprite *sprite = (CCSprite *)sender;
    40     this->removeChild(sprite, true);
    41     if (sprite->getTag()==1)//子弹
    42         _bullets->removeObject(sprite);
    43 }

     吃道具可改变子弹类型。子弹的回收方式:从飞机坐标处出发,运动到屏幕外被回收。道具的处理类似。

    4.设置触摸/鼠标点击控制玩家角色的移动

     1 bool PlaneWarGame::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
     2 {
     3     if(_player==NULL) return false;
     4 
     5     CCPoint pt = CCDirector::sharedDirector()->convertToGL(
     6         pTouch->locationInView(pTouch->view()));
     7 
     8     CCRect rt = _player->getRect();
     9     if (CCRect::CCRectContainsPoint(rt,pt))
    10         _player->_isDragEnabled = true;
    11     return true;
    12 }
    13 
    14 void PlaneWarGame::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
    15 {
    16     if(_player==NULL) return;
    17 
    18     CCSize size = CCDirector::sharedDirector()->getWinSize();
    19     CCPoint pt = CCDirector::sharedDirector()->convertToGL(
    20         pTouch->locationInView(pTouch->view()));
    21     
    22     CCRect rt = CCRect(0,0,size.width,size.height);
    23 
    24     if (_player->_isDragEnabled && CCRect::CCRectContainsPoint(rt,pt))
    25         _player->setPlayerPt(pt);
    26 }
    27 
    28 void PlaneWarGame::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
    29 {
    30     _player->_isDragEnabled = false;
    31 }

    这里重写3个虚函数。移动角色的方式:触摸/鼠标按下时,如果点击位置在角色上,将角色可被拖拽状态置为true;触摸/鼠标移动时,根据角色的可被拖拽状态和屏幕范围确定是否移动;触摸/鼠标弹起时,将角色的可被拖拽状态恢复为false。

    5.碰撞检测

      1 void PlaneWarGame::updateGame(float dt)
      2 {
      3     CCArray *bulletsToDelete = CCArray::array();
      4     CCObject* it = NULL;
      5     CCObject* jt = NULL;
      6     
      7     if(_player==NULL) return;
      8 
      9     CCRect playerRect = _player->getRect();
     10     // 己方飞机和敌方飞机的碰撞
     11     CCARRAY_FOREACH(_enemys, jt)
     12     {
     13         EnemySprite *enemy = dynamic_cast<EnemySprite*>(jt);
     14         if (!enemy->isNull() && CCRect::CCRectIntersectsRect(playerRect, enemy->getRect()))
     15         {
     16             _player->die();
     17             gameover(false);
     18             return;
     19         }
     20     }
     21 
     22     //////////////////////////////////////////////////////////////////////////
     23         // 道具和角色的碰撞检测
     24     CCSprite* prop = (CCSprite*)getChildByTag(8);//子弹
     25     if (NULL != prop)
     26     {
     27         CCRect propRect = CCRectMake(
     28             prop->getPosition().x - (prop->getContentSize().width/2),
     29             prop->getPosition().y - (prop->getContentSize().height/2),
     30             prop->getContentSize().width,
     31             prop->getContentSize().height);
     32         if (CCRect::CCRectIntersectsRect(playerRect, propRect))
     33         {
     34             _player->_bulletKind = BK_DOUBLE;
     35             removeChild(prop,true);
     36             CocosDenshion::SimpleAudioEngine::sharedEngine()->
     37                 playEffect("planewar/get_double_laser.wav",false);
     38         }
     39     }
     40     CCSprite* prop1 = (CCSprite*)getChildByTag(9);//炸弹
     41     if (NULL != prop1)
     42     {
     43         CCRect prop1Rect = CCRectMake(
     44             prop1->getPosition().x - (prop1->getContentSize().width/2),
     45             prop1->getPosition().y - (prop1->getContentSize().height/2),
     46             prop1->getContentSize().width,
     47             prop1->getContentSize().height);
     48         if (CCRect::CCRectIntersectsRect(playerRect, prop1Rect))
     49         {
     50             _player->_bombCount++;
     51             removeChild(prop1,true);
     52             CocosDenshion::SimpleAudioEngine::sharedEngine()->
     53                 playEffect("planewar/get_bomb.wav",false);
     54         }
     55     }
     56 
     57     //////////////////////////////////////////////////////////////////////////
     58         // 子弹和敌机的碰撞检测
     59     CCARRAY_FOREACH(_bullets, it)
     60     for (int i=0; i<_bullets->count(); i++)
     61     {
     62         CCSprite *bullet = dynamic_cast<CCSprite*>(_bullets->objectAtIndex(i));
     63         CCRect bulletRect = CCRectMake(
     64             bullet->getPosition().x - (bullet->getContentSize().width/2),
     65             bullet->getPosition().y - (bullet->getContentSize().height/2),
     66             bullet->getContentSize().width,
     67             bullet->getContentSize().height);
     68 
     69         CCArray* enemysToDelete =CCArray::array();
     70 
     71         CCARRAY_FOREACH(_enemys, jt)
     72         {
     73             EnemySprite *enemy = dynamic_cast<EnemySprite*>(jt);
     74             if (!enemy->_die && CCRect::CCRectIntersectsRect(bulletRect, enemy->getRect()))
     75             {
     76                 if (enemy->_hp>0)
     77                 {
     78                     enemy->_hp--;
     79                     //bulletsToDelete->addObject(bullet);
     80                     _bullets->removeObject(bullet);
     81                     removeChild(bullet,true);
     82                 }
     83                 else if (enemy->_hp<=0)
     84                 {
     85                     //bulletsToDelete->addObject(bullet);
     86                     _bullets->removeObject(bullet);
     87                     removeChild(bullet,true);
     88                     enemysToDelete->addObject(enemy);
     89                 }
     90             }
     91         }
     92 
     93         CCARRAY_FOREACH(enemysToDelete, jt)
     94         {
     95             EnemySprite *enemy = dynamic_cast<EnemySprite*>(jt);
     96             enemy->_die = true;
     97             enemy->die();
     98             _bulletsDestroyed++;
     99         }
    100 
    101         enemysToDelete->release();
    102     }
    103 
    104     // 释放已死亡的子弹
    105     CCARRAY_FOREACH(bulletsToDelete, it)
    106     {
    107         CCSprite* projectile = dynamic_cast<CCSprite*>(it);
    108         _bullets->removeObject(projectile);
    109         this->removeChild(projectile, true);
    110     }
    111     bulletsToDelete->release();
    112 }          

     碰撞检测使用最简单的矩形检测。

    6.玩家角色的添加和操作

      1 void PlaySprite::onEnter()
      2 {
      3     CCNode::onEnter();
      4 
      5     CCSize size = CCDirector::sharedDirector()->getWinSize();
      6     _sprite = CCSprite::spriteWithFile("planewar/hero1.png");
      7     _sprite->setContentSize(CCSize(62,68));
      8     _sprite->setPosition( ccp(size.width/2,_sprite->getContentSize().height/2) );
      9     addChild(_sprite,10);
     10 
     11     CCAnimation * animation = CCAnimation::animation();
     12     animation->addFrameWithFileName("planewar/hero1.png");
     13     animation->addFrameWithFileName("planewar/hero2.png");
     14     CCAction* action = CCRepeatForever::actionWithAction(
     15         CCAnimate::actionWithDuration(0.1f, animation, false));
     16     action->setTag(11);
     17     _sprite->runAction(action);
     18 
     19     // 调度器
     20     schedule( schedule_selector(PlaySprite::move), 0.002 );
     21 }
     22 
     23 CCRect PlaySprite::getRect()
     24 {
     25     if (_sprite!=NULL)
     26         return CCRect(
     27         _sprite->getPosition().x - (_sprite->getContentSize().width/2),
     28         _sprite->getPosition().y - (_sprite->getContentSize().height/2),
     29         _sprite->getContentSize().width,
     30         _sprite->getContentSize().height);
     31 }    
     32 
     33 CCPoint PlaySprite::getPlayerPt()
     34 {
     35     if (_sprite!=NULL)
     36         return _sprite->getPosition();
     37 }
     38 
     39 void PlaySprite::setPlayerPt(CCPoint pt)
     40 {
     41     if (_sprite!=NULL)
     42         return _sprite->setPosition(pt);
     43 }
     44 
     45 void  PlaySprite::setMoveMode( UINT  message, WPARAM  wParam) 
     46 {
     47     if (message == WM_KEYDOWN)
     48     {// 控制飞机移动
     49         if (wParam == VK_UP)
     50             _mode = MM_UP;
     51         else if (wParam == VK_DOWN)
     52             _mode = MM_DOWN;
     53         else if (wParam == VK_LEFT)
     54             _mode = MM_LEFT;
     55         else if (wParam == VK_RIGHT)
     56             _mode = MM_RIGHT;
     57     }else if (message == WM_KEYUP)
     58     {
     59         if (wParam == VK_UP || wParam == VK_DOWN ||wParam == VK_LEFT||wParam == VK_RIGHT)
     60             _mode = MM_NONE;
     61     }
     62 }
     63 
     64 void PlaySprite::move(float dt)
     65 {
     66     CCSize winSize = CCDirector::sharedDirector()->getWinSize();
     67 
     68     switch(_mode)
     69     {
     70     case MM_NONE:
     71         break;
     72     case MM_UP:
     73         if (getPlayerPt().y<winSize.height)
     74             setPlayerPt(ccp(getPlayerPt().x,getPlayerPt().y+1));
     75         break;
     76     case MM_DOWN:
     77         if (getPlayerPt().y>0)
     78             setPlayerPt(ccp(getPlayerPt().x,getPlayerPt().y-1));
     79         break;
     80     case MM_LEFT:
     81         if (getPlayerPt().x>0)
     82             setPlayerPt(ccp(getPlayerPt().x-1,getPlayerPt().y));
     83         break;
     84     case  MM_RIGHT:
     85         if (getPlayerPt().x<winSize.width)
     86             setPlayerPt(ccp(getPlayerPt().x+1,getPlayerPt().y));
     87         break;
     88     }
     89 }
     90 
     91 void PlaySprite::die()
     92 {
     93     if (_sprite==NULL)return;
     94 
     95     _sprite->stopActionByTag(11);
     96     CCAnimation * animation = CCAnimation::animation();
     97     animation->addFrameWithFileName("planewar/hero_blowup_n1.png");
     98     animation->addFrameWithFileName("planewar/hero_blowup_n2.png");
     99     animation->addFrameWithFileName("planewar/hero_blowup_n3.png");
    100     animation->addFrameWithFileName("planewar/hero_blowup_n4.png");
    101     _sprite->runAction(CCSequence::actions(
    102         CCAnimate::actionWithDuration(0.1f, animation, false),
    103         CCCallFunc::actionWithTarget(this, callfunc_selector(PlaySprite::destroy)),NULL));
    104 }
    105 
    106 void PlaySprite::destroy()
    107 {
    108     if (_sprite==NULL)return;
    109     
    110     removeChild(_sprite,true);
    111     _sprite = NULL;
    112 }
    • 玩家角色类是派生自CCNode类,用组合的方式包含一个CCSprite类的成员对象,在玩家角色类内部封装对精灵的操作。
    • 重写onEnter函数初始化_sprite成员,设置位置、大小、图片和帧动画等。
    • 玩家的方向键控制移动和鼠标拖拽移动方式类似,在操作时设置移动状态,在schedule定时调用的函数move中根据移动状态来移动玩家角色。
    • 关于碰撞之后的动画和销毁,参照上面的die和destroy函数,对象被碰撞时调用die函数运行爆炸的帧动画,动画结束后自动调用destroy函数销毁对象。
    • 敌机对象的操作和玩家角色类似。

    四、完成效果图

    经过不到一周的时间,从什么都没有,到游戏正常运行、功能完整,体会了cocos2dx的调用机制。

    下面是运行效果图:

    如果实现中有什么问题,欢迎大家在评论中指教!

  • 相关阅读:
    Evaluation
    Version
    bzoj4184-shallot
    jQuery 获取并设置 CSS 类
    jQuery 删除元素
    jQuery 添加元素
    jQuery 设置内容和属性
    jQuery
    jQuery Chaining
    CI 框架增加公用函数-如何使用Helper辅助函数
  • 原文地址:https://www.cnblogs.com/Ray1024/p/5131617.html
Copyright © 2020-2023  润新知