cocos2d-x之SimpleGame分析

本文原创版权归 博客园 Terry_龙 所有,转载请注明原创作者及出处,以示尊重!

作者:Terry_龙
原文:http://www.cnblogs.com/TerryBlog/archive/2012/10/30/2746150.html

作为cocos2d-x的标配DEMO,SimpleGame可算是给入门学cocos2d-x的俺们这些新手门学习的对象了,那么来分析分析,把几个关键的代码记录下来。

 

设置游戏读取资源的目录

复制代码
CCFileUtils::sharedFileUtils()->setResourceDirectory( " sd ");
复制代码

 默认路径是Resource目录

 

设置游戏的分辨率大小

复制代码
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(480, 320, kResolutionNoBorder);
复制代码

也可以不设置,自动去获取

 

创建精灵

复制代码
CCSprite *player=CCSprite::create( " Player.png ",CCRectMake( 0, 0, 27, 40));
        player->setPosition(ccp(
            origin.x+player->getContentSize().width/ 2, //X轴为:0+游戏角色的宽度/2
            winVisibleSize.height/ 2 + player->getContentSize().height/ 2) //y轴为:指定的屏幕分辨率/2+游戏角色的高/2
            );
         this->addChild(player);
复制代码

 如果你希望精灵在图层初始化的时候就产生,那么精灵的创建最好把它放在于图层(CCLayer)的init函数中。

 创建精灵时,需要为精灵指定位置(position),可接收的参数是CCPoint类型,CCPoint 是一个存放x、y轴数据的对象

 

使用时间选择器刷新游戏

复制代码
this->schedule(schedule_selector(HelloWorld::gameLogic), 1.0);
复制代码

 调用CCNodes的schedule(SEL_SCHEDULE selector, float interval)方法,这是一个自定义的时间选择器,以秒为单位。这里以秒为单位,每秒产生一个怪物

 

设置可接受触摸(Touch)事件

在场景初始化时,设置可接受触摸

复制代码

 this->setTouchEnabled(true);

复制代码

 然后,注册一个触摸事件的分发函数

复制代码
void HelloWorld::registerWithTouchDispatcher(){
    CCDirector::sharedDirector()->getTouchDispatcher()->addStandardDelegate( this, 0);
}
复制代码

 通过以上的做法,游戏即可在ccTouchesEnded回调函数里面得到触摸反馈。

 

移动精灵

上面每一秒钟刷新游戏,产生一个游戏怪物,方法位于HelloWold::addTarget,移动精灵的代码是

复制代码
CCFiniteTimeAction* actionMove = CCMoveTo::create( ( float)actualDuration,
                                            ccp( 0 - target->getContentSize().width/ 2, actualY) );
    CCFiniteTimeAction* actionMoveDone = CCCallFuncN::create(  this
                                            callfuncN_selector(HelloWorld::spriteMoveFinished));
    target->runAction( CCSequence::create(actionMove, actionMoveDone, NULL) );
复制代码

 

 actionMove:设置精灵移动方向和移动速度

actionMoveDone:精灵移动完成后执行可执行的回调函数为 spriteMoveFinished,当游戏移动到屏幕边界外,即回收该对象

 然后通过精灵的runAction设置,精灵移动

 

上面代码,怪物的产生位置定在x的最右边,move方向从右到左到消息,需要设置x轴的最小值并且完全可以将怪物移动出屏幕即可,故x轴这里设置移动代码: (0 - target->getContentSize().width/2 ),y轴不变,随机从出来那一刻起一直不变做横向移动。

 

 控制游戏角色发射飞镖

上面有提到设置图层可授受触摸 ,接下来我们要通过触摸让游戏主角发射飞镖消灭不断产生的怪物。代码见下方

得到触摸点坐标 

复制代码
CCTouch* touch = (CCTouch*)( touches->anyObject() );
    CCPoint location = touch->getLocation();
复制代码

 每接受触摸一次产生一个飞镖

复制代码

 CCSize winSize = CCDirector::sharedDirector()->getVisibleSize();

    CCSprite *projectile = CCSprite::create( " Projectile.png ", CCRectMake( 002020));
    projectile->setPosition( ccp( 20, winSize.height/2+projectile->getContentSize().height/2) );//角色x轴为0+角色的宽度,故飞镖的x轴应和角色有交接,使用户看起来飞镖是从角色身上发出来的
复制代码

飞镖移动方向

复制代码
float offX=location.x - projectile->getPosition().x;
     float offY=location.y - projectile->getPosition().y;

     if(offX <= 0) return;

     this->addChild(projectile);
     
     float realX=winSize.width+(projectile->getContentSize().width/ 2);//飞镖的横向方向跟游戏怪物一样,也是要移动出游戏界面并回收的(如果碰到怪物也要回收)
     float ratio = offY/offX;//偏移量
     float realY=(realX*ratio)+projectile->getPosition().y;//飞镖的竖向方向应是飞镖的横向方向*触摸点的偏移量每帧偏移的数值+当前飞镖所在位置的y轴
    CCPoint realDest =ccp(realX,realY);
复制代码

控制飞镖移动

复制代码
projectile->runAction(CCSequence::create(
        CCMoveTo::create( 1.0,realDest),//参数1.移动速度。参数2.移动的x、y轴
        CCCallFuncN::create( this,callfuncN_selector(HelloWorld::spriteMoveFinished)),//设置回调函数
        NULL
    ));
复制代码

 

飞镖与怪物的碰撞检测

这里需要在图层初始化的时候设置一个时间选择器,监听每帧的游戏变化

复制代码

   this->schedule(schedule_selector(HelloWorld::updateGame));

复制代码

这时委托函数updateGame就启到每帧监听的作用了,代码如下

复制代码
void HelloWorld::updateGame( float dt){
    CCArray *projectileToDelete = new CCArray;
    CCObject* it=NULL;
    CCObject* jt = NULL;
    CCARRAY_FOREACH(_projectiles,it){ //循环每一个飞镖
        CCSprite *projectile=dynamic_cast<CCSprite*>(it);
        CCRect projectileRect =CCRectMake(
            projectile->getPosition().x - (projectile->getContentSize().width/ 2),
            projectile->getPosition().y-(projectile->getContentSize().height/ 2),
            projectile->getContentSize().width,
            projectile->getContentSize().height);

        CCArray* targetsToDelete= new CCArray;
        
        CCARRAY_FOREACH(_targets, jt)//循环每一个敌人
        {
            CCSprite *target = dynamic_cast<CCSprite*>(jt);
            
            CCRect targetRect = CCRectMake(
            target->getPosition().x - (target->getContentSize().width/ 2),
            target->getPosition().y - (target->getContentSize().height/ 2),
            target->getContentSize().width,
            target->getContentSize().height);
             if (projectileRect.intersectsRect(targetRect)) //这里做矩形交集检测,如果两者有产生交集则将需要删除的敌人存放于数组里面
            {
                targetsToDelete->addObject(target);
            }
        }

         CCARRAY_FOREACH(targetsToDelete, jt)//循环需要删除的敌人
         {
             CCSprite *target = dynamic_cast<CCSprite*>(jt);
             _targets->removeObject(target);  //将敌人从产生的敌人数组里面删除
              this->removeChild(target,  true);//从图层删除

              _projectilesDestroyed++;  //累积
               if (_projectilesDestroyed >=  10) //当满足消灭一定数量的敌人时,则执行游戏场景切换或者什么的blablabla
              {
                 ///
              }
          }
          if(targetsToDelete->count() >  0){ //如果有存在需要删除的敌人,则同时将与敌人交集的飞镖存入需要删除的飞镖数组
            projectileToDelete->addObject(projectile);
         }
         targetsToDelete->release();
    }

     // remove projectile
    CCARRAY_FOREACH(projectileToDelete,it){ //最后循环飞镖将飞镖删除掉
        CCSprite *projectile =dynamic_cast<CCSprite*>(it);
        _projectiles->removeObject(projectile);
         this->removeChild(projectile, true);
    }
    projectileToDelete->release();
     // end
 }
复制代码

 根据上面,逻辑并不复杂,注意一下当前飞镖与敌人数组之间的关系即可。

 

播放游戏音效

无论是游戏背景音效也好,忍者发射飞镖那一刹那时的声音也好,好的游戏都要加上特殊的音效效果才能使游戏更加引人入胜。 cocos2d-x游戏引擎当然也有相关的音乐播放接口。从网上找到的一段话:

cocos2d-iphone里包含cocosDenshion库,里面从底到高提供三层接口:CDSoundEngine->CDAudioManager->SimpleAudioEngine,但整个库完全依赖于OpenAL来实现。关于OpenAL,它不是Khronos Group的标准,而是Creative公司的一个开源库,可以软实现或硬件实现。目前硬件实现了OpenAL的好像就只有苹果的产品,因此在其他平台上,我们无法提供cocosDenshion底层的支持,但我们是支持顶层的,它是开发者最常用到的一层。

 

 那么如何添加音效呢?

添加背景音

复制代码
 CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic( " background-music-aac.wav ", true);//设置背景音循环
复制代码

 添加发射飞镖音效

复制代码
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect( " pew-pew-lei.wav ", false);
复制代码

 这里发射飞镖音效需要注意一个问题就是,如果这样写在Touch里面,第一次让音效播放是无效的,原因是播放音效需要预加载,不然第一次点击是不会出声音的,之后才会。不知道这是引擎硬性要求的么?解决办法是在图层初始化的时候让音效预加载一次

复制代码
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect( " pew-pew-lei.wav ");
复制代码

 

关于场景切换 

 觉得这方面没什么要说的,该说的网上都有,尤其是nowpaper 这篇文章 ,讲得很形象,可以去看看。

SimpleGame里面的场景切换类,对应是GameOverScene.cpp,该文件是判断游戏结束时,切换到该界面提示用户失败或者成功。这里有一点需要特点注意的是 

复制代码

  GameOverScene *gameOverScene=GameOverScene::create();

   gameOverScene->getLayer()->getLabel()->setString( " you win!!! ");
  CCDirector::sharedDirector()->replaceScene(gameOverScene);
复制代码

 

 上面红色代码这块,比较难懂,这句话可以这么理解:通过GameOverScene头文件中的CC_SYNTHESIZE_READONLY宏定义生成getLayer方法,该方法返回GameOverLayer类型,然后再通过CC_SYNTHESIZE_READONLY 宏定义生成getLabel方法,返回cocos2d::CCLabelTTF类型,最后调用CCLabelTTF成员里面的setString ,该_label在初始化GameOverLayer的时候就己经this->addChild(_label) ,会不会这样就相当于传参数到另一个CCScene呢?呵呵!

宏定义代码如下:

#define CC_SYNTHESIZE_READONLY(varType, varName, funName)\
protected: varType varName;\

public: virtual varType get##funName(void) const { return varName; }   //这里组合得到方法名和返回类型

 

洋洋洒洒的说了那么一堆,也算是研究出来了,希望大家能够少走点弯路。

 

源码可以在cocos2d-x sample 目录上找到,或者https://github.com/terryyhl/SimpleGame.git 。 

你可能感兴趣的:(cocos2d-x之SimpleGame分析)