一个简单的跑酷游戏《萝莉快跑》的消化(附下载)

《喵汪大战》原作者:bill_man  http://my.csdn.net/bill_man 


昨晚那个游戏已经成功运行在了手机上,接下来,好好看看代码。



AppDelegate.cpp类

常规的东西,设置FPS,设置是否显示FPS,生成菜单场景类Scene,然后让导演类加载第一个场景。

GameMenuScene.cpp类


大部分工作都在init里,设置背景:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. CCSize size = CCDirector::sharedDirector()->getWinSize();      
  2.    //菜单背景  
  3.    CCSprite* bg = CCSprite::create("MainMenu.png");  
  4.    bg->setScale(0.5);  
  5.    bg->setPosition( ccp(size.width/2, size.height/2) );  
  6.    this->addChild(bg, 0,0);  
加载几个按钮,到CCMenu上,然后将CCMenu加载到Scene中

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //按钮  
  2.   CCMenuItemImage *newGameItem = CCMenuItemImage::create("newGameA.png""newGameB.png",this,menu_selector(GameMenu::menuNewGameCallback));  
  3.   newGameItem->setScale(0.5);  
  4.   newGameItem->setPosition(ccp(size.width / 2 + 40,size.height / 2 - 20));  
  5.   newGameItem->setEnabled(false);  
  6.   CCMenuItemImage *continueItem = CCMenuItemImage::create("continueA.png""continueB.png",this,menu_selector(GameMenu::menuContinueCallback));  
  7.   continueItem->setScale(0.5);  
  8.   continueItem->setPosition(ccp(size.width / 2 + 40,size.height / 2 - 60));  
  9.   continueItem->setEnabled(false);  
  10.   CCMenuItemImage *aboutItem = CCMenuItemImage::create("aboutA.png""aboutB.png",this,menu_selector(GameMenu::menuAboutCallback));  
  11.   aboutItem->setScale(0.5);  
  12.   aboutItem->setPosition(ccp(size.width / 2 + 40,size.height / 2 - 100));  
  13.   aboutItem->setEnabled(false);  
  14.   soundItem = CCMenuItemImage::create("sound-on-A.png""sound-on-B.png",this,menu_selector(GameMenu::menuSoundCallback));  
  15.   soundItem->setScale(0.5);  
  16.   soundItem->setEnabled(false);  
  17.   soundItem->setPosition(ccp(40,40));  
  18.   CCMenu* mainmenu = CCMenu::create(newGameItem,continueItem,aboutItem,soundItem,NULL);  
  19.   mainmenu->setPosition(ccp(0,0));  
  20.   this->addChild(mainmenu,1,3);  
初始化背景音乐:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //初始化声音  
  2.     SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic("background.mp3");  
  3.     SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(0.5);  
  4.     SimpleAudioEngine::sharedEngine()->playBackgroundMusic("background.mp3");  

然后在onEnter方法里,让菜单按钮从无放大至正常大小

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void GameMenu::onEnter(){  
  2.     CCLayer::onEnter();  
  3.     CCSize size = CCDirector::sharedDirector()->getWinSize();  
  4.     CCNode* mainmenu = this->getChildByTag(3);  
  5.     mainmenu->setScale(0);  
  6.     mainmenu->runAction(CCSequence::create(CCScaleTo::create(0.5,1),CCCallFunc::create(this, callfunc_selector(GameMenu::menuEnter)),NULL));  
  7. }  
使用getChildByTag()方法取得mainNenu。这也正是为何addChild()时,要加入tag参数了。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. CCSequence::create()里,CCScaleTo动作执行完之后,调用GameMenu类的menuEnter方法  

然后通过mainmenu->getChildren();方法获取按钮组,遍历一下,设置为可用。

然后紧跟着一些callBack方法,也就是按钮事件

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void GameMenu::menuNewGameCallback(CCObject* pSender)  
  2. {  
  3.     CCDirector::sharedDirector()->setDepthTest(true);  
  4.     CCDirector::sharedDirector()->replaceScene(CCTransitionPageTurn::create(0.5,GameMain::scene(), false));  
  5. }  
最后是控制背景音乐的方法

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void GameMenu::menuSoundCallback(CCObject* pSender)  
  2. {  
  3.     //设置声音  
  4.     if(! issound){  
  5.         soundItem->setNormalImage(CCSprite::create("sound-on-A.png"));  
  6.         soundItem->setDisabledImage(CCSprite::create("sound-on-B.png"));  
  7.         SimpleAudioEngine::sharedEngine()->playBackgroundMusic("background.mp3"true);  
  8.        issound = true;  
  9.     }else{  
  10.         soundItem->setNormalImage(CCSprite::create("sound-off-A.png"));  
  11.         soundItem->setDisabledImage(CCSprite::create("sound-off-B.png"));  
  12.         SimpleAudioEngine::sharedEngine()->stopBackgroundMusic();  
  13.        issound = false;  
  14.     }  
  15. }  

再来看看关于界GameAboutScene.cpp


【插一句:setDepthTest:设置深度测试——可以用于自动处理遮挡关系

这个类里,在init方法中贴几个CCSprite,加一个CCMenuItemImage及点击事件即可。没什么特别的。


GameObjStar.cpp星星类


本身继承自CCNode,有一个设置是否可见的方法

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void GameObjStar::set_visable(bool var){  
  2.     _visable = var;  
  3.     this->setVisible(var);  
  4. }  
在onEnter方法中初始化一下:缩放至一半大小,设置是否可见,设置尺寸。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void GameObjStar::onEnter(){  
  2.     CCNode::onEnter();  
  3.     //星星初始化  
  4.     this->setContentSize(CCSizeMake(36.5,35.5));  
  5.     CCSprite* star = CCSprite::create("star.png");  
  6.     star->setScale(0.5);  
  7.     _visable = true;  
  8.     addChild(star,1);  
  9. }  


GameMark.cpp分数类


也是继承自CCNode,同样在onEnter方法里初始化,前面的socre是一个固定的CCSprite,后面是5个CCSprite,根据每个位数来动态修改图片

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void GameMark::onEnter()  
  2. {  
  3.     CCNode::onEnter();  
  4.     CCSize size = CCDirector::sharedDirector()->getWinSize();   
  5.     this->setContentSize(size);  
  6.    // bits = CCArray::create(5);  
  7.     bits=new CCArray(5);  
  8.     CCSprite *title= CCSprite::create("score.png");  
  9.     title->setPosition(ccp(size.width/2 + 120,size.height - 15));  
  10.     title->setScale(0.5);  
  11.     addChild(title);  
  12.     for(int i = 0;i < 5;i ++){  
  13.         CCSprite * shu = CCSprite::create("shu.png");  
  14.         ui = shu->getTexture();  
  15.         shu->setScale(0.5);  
  16.         shu->setTextureRect(CCRectMake(234,0,26,31));  
  17.         shu->setPosition(ccp(size.width - 15 - i * 15,size.height - 15));  
  18.         bits->addObject(shu);  
  19.         addChild(shu);  
  20.     }  
  21.     bits->retain();  
  22.     mark = 0;  
  23. }  
bits->retain();// 应该是防止数组被回收

下面是按位设置数字,这种自定义rect的舒适化CCSprite的方式是第一次出现,其他的没什么了。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. setTextureRect(CCRectMake((temp - 1) * 26,0,26,31));  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void GameMark::addnumber(int var){  
  2.     //按位设置数字  
  3.     mark += var;  
  4.     int temp = mark % 10;  
  5.     if(temp > 0){  
  6.         ((CCSprite *)bits->objectAtIndex(0))->setTexture(ui);  
  7.         ((CCSprite *)bits->objectAtIndex(0))->setTextureRect(CCRectMake((temp - 1) * 26,0,26,31));   
  8.     }else{  
  9.         ((CCSprite *)bits->objectAtIndex(0))->setTexture(ui);  
  10.         ((CCSprite *)bits->objectAtIndex(0))->setTextureRect(CCRectMake(234,0,26,31));   
  11.     }  
  12.     temp = (mark % 100) / 10;  
  13.     if(temp > 0){  
  14.         ((CCSprite *)bits->objectAtIndex(0))->setTexture(ui);  
  15.         ((CCSprite *)bits->objectAtIndex(1))->setTextureRect(CCRectMake((temp - 1) * 26,0,26,31));    
  16.    
  17.     }else{  
  18.         ((CCSprite *)bits->objectAtIndex(0))->setTexture(ui);  
  19.         ((CCSprite *)bits->objectAtIndex(1))->setTextureRect(CCRectMake(234,0,26,31));   
  20.     }  
  21.     temp = (mark % 1000) / 100;  


再来看下萝莉类GameObjHero.cpp


首先在void GameObjHero::onEnter()方法里初始化。

接受触摸:

 pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
初始化萝莉:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. mainsprite = CCSprite::create("s_1.png");  
  2.     //动画  
  3.     CCAnimation * animation = CCAnimation::create();  
  4.     animation->addSpriteFrameWithFileName("s_1.png");  
  5.     animation->addSpriteFrameWithFileName("s_2.png");  
  6.     animation->addSpriteFrameWithFileName("s_3.png");  
  7.     animation->addSpriteFrameWithFileName("s_4.png");  
  8.     animation->addSpriteFrameWithFileName("s_5.png");  
  9.     animation->addSpriteFrameWithFileName("s_6.png");  
  10.     animation->setDelayPerUnit(0.1f);  
  11.     animation->setRestoreOriginalFrame(true);  
  12.     //运行奔跑动画  
  13.     mainsprite->runAction(CCRepeatForever::create(CCAnimate::create(animation)));  
上面这种添加动画的方式,是骨头第一次遇到。

首先创建CCAnimation,然后设置每一帧的图片,然后设置每帧的延迟时间,设置播放完动画后是否回到第一帧。

然后是构造一个循环播放的CCRepeatForever动画,添加到精灵上。

里面有个设置状态方法:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. switch(state){  
  2.        case 1://跳跃  
  3.            this->stopAllActions();  
  4.            mainsprite->stopAllActions();  
  5.            mainsprite->setTexture(jump);  
  6.            this->runAction(CCSequence::create(CCJumpBy::create(2.5,ccp(0,0),100,1),CCCallFunc::create(this, callfunc_selector(GameObjHero::jumpend)),NULL));  
  7.            break;  
  8.        case 2://受伤  
  9.            this->stopAllActions();  
  10.            mainsprite->stopAllActions();  
  11.            mainsprite->setTexture(hurt);  
  12.            this->runAction(CCSequence::create(CCBlink::create(3, 10),CCCallFunc::create(this, callfunc_selector(GameObjHero::hurtend)),NULL));  
  13.            ((GameMain *)this->getParent())->setover();  
  14.            break;  

跳跃,则运行CCJumpBy动画,受伤则运行CCBlink动画,就是一闪一闪的,并且在动画结束分别调用相应的方法。

然后是GameObjMap.cpp地图类


[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.  void GameObjMap::bg1change(){  
  2.     //运动出屏幕重设位置,运动  
  3.     CCSprite * bg = (CCSprite *)this->getChildByTag(0);  
  4.     bg->setPosition(ccp(480,320));  
  5.     bg->runAction(CCSequence::create(CCMoveBy::create(4,ccp(-960,0)),CCCallFunc::create(this, callfunc_selector(GameObjMap::bg1change)),NULL));  
  6.     for(int i = 0;i < 5;i ++){  
  7.         ((GameObjStar *)stars1->objectAtIndex(i))->set_visable(true);  
  8.     }  
  9. }  
  10. void GameObjMap::bg2change(){  
  11.     //运动出屏幕重设位置,运动  
  12.     CCSprite * bg = (CCSprite *)this->getChildByTag(1);  
  13.     bg->setPosition(ccp(480,320));  
  14.     bg->runAction(CCSequence::create(CCMoveBy::create(4,ccp(-960,0)),CCCallFunc::create(this, callfunc_selector(GameObjMap::bg2change)),NULL));  
  15.     for(int i = 0;i < 5;i ++){  
  16.         ((GameObjStar *)stars2->objectAtIndex(i))->set_visable(true);  
  17.     }  
  18. }  
楼上两个方法作用是背景循环,callfunc_selector方法调用的是对方,一对好机油!

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bg1->runAction(CCSequence::create(CCMoveBy::create(2,ccp(-480,0)),CCCallFunc::create(this, callfunc_selector(GameObjMap::bg1change)),NULL));  
  2. bg2->runAction(CCSequence::create(CCMoveBy::create(4,ccp(-960,0)),CCCallFunc::create(this, callfunc_selector(GameObjMap::bg2change)),NULL));  
  3.     
其实整个背景地图的宽度是两倍的可视屏幕宽度,即480*2=960,这样的地图有两套,两套地图的动画在第一次运行时正好差半个周期,而且是互相调用,呈现出来的效果就是一直循环下去。恩,懂了。
然后在两套地图上绘制植物、草地等元素。


最后是核心逻辑类:GameMainScene.cpp

首先在初始化方法里,把游戏的所有元素都实例化一下,比如萝莉,比如地图,比如分数星星等。

最主要的循环方法:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. scheduleUpdate();  
  2. void GameMain::update(float time){}  

在update里面判断是否跟星星相撞,

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool GameMain::isCollion(CCPoint p1,CCPoint p2,int w1,int h1,int w2,int h2){  
  2.     if(abs(p1.x - p2.x) < w1 + w2 && abs(p1.y - p2.y) < h1 + h2){  
  3.         return true;  
  4.     }  
  5.     return false;  
  6. };  

判断是否是掉落状态:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void GameMain::isherodrop(){  
  2.     CCPoint p1 = (map->getChildByTag(0))->getPosition();  
  3.     CCPoint p2 = (map->getChildByTag(1))->getPosition();      
  4.     int temp;  
  5.     if(p1.x <= 100 && (p1.x + 480) >= 100){  
  6.         temp = (100 - p1.x) / 64;  
  7.         if(bg1shu[temp] == -1){  
  8.            hero->setState(2);  
  9.         }  
  10.     }else{  
  11.         temp = (100 - p2.x) / 64;  
  12.         if(bg2shu[temp] == -1){  
  13.             hero->setState(2);  
  14.         }   
  15.     }  
  16. }  
上面方法比较难懂,大意就是根据当前地图的x值位置,来取得主角所站的位置有没有石头,没有的话即掉落。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. temp = (100 - p1.x) / 64;  
100是因为萝莉站在x=100的位置,所以要得到萝莉所在位置是否为空,即
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bg2shu[temp] == -1  

地图是这样添加的

  //星星,植物等大图素的添加
    for(int i = 0;i < 7;i ++){   //960/8=120

也就是说在960像素宽度的背景图上,可以放8个宽度等于120的元素。

static const short bg1shu[] = {-1,1,0,2,-1,1,2,3};
static const short bg2shu[] = {3,3,-1,3,-1,3,3,-1};

然后根据 数组的值来放置元素,-1时为空。


本篇完结   点此下载

http://download.csdn.net/detail/iamlazybone/6632923 


好了,《萝莉快跑》的例子到此算是消化了大部分了。

看3遍不如自己动手敲1遍,赶紧去噼里啪啦一下吧:)

大家晚安。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值