zoukankan      html  css  js  c++  java
  • Cocos2dx系列笔记7:一个简单的跑酷游戏《萝莉快跑》的消化(附下载)

    懒骨头(http://blog.csdn.com/iamlazybone

    或许有天

    我们羡慕和崇拜的人

    因为我们的努力

    也会来了解我们

    说不定

    还会成为好友

    骨头喜欢这样与哲哲共勉


    多少个夜晚

    一张长长的书桌上

    哲哲在左边认真的做着可爱的独特的手作

    骨头在右边噼里啪啦敲着自己真正喜欢的代码

    中间的pad放部电影 || 放首歌 || 放段《一席》

    这就是我们刚认识时憧憬的日子

    所以现在是很幸福了


    继续!


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



    AppDelegate.cpp类

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

    GameMenuScene.cpp类


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

     CCSize size = CCDirector::sharedDirector()->getWinSize();    
        //菜单背景
        CCSprite* bg = CCSprite::create("MainMenu.png");
        bg->setScale(0.5);
        bg->setPosition( ccp(size.width/2, size.height/2) );
        this->addChild(bg, 0,0);

    加载几个按钮,到CCMenu上,然后将CCMenu加载到Scene中

      //按钮
        CCMenuItemImage *newGameItem = CCMenuItemImage::create("newGameA.png", "newGameB.png",this,menu_selector(GameMenu::menuNewGameCallback));
        newGameItem->setScale(0.5);
        newGameItem->setPosition(ccp(size.width / 2 + 40,size.height / 2 - 20));
        newGameItem->setEnabled(false);
        CCMenuItemImage *continueItem = CCMenuItemImage::create("continueA.png", "continueB.png",this,menu_selector(GameMenu::menuContinueCallback));
        continueItem->setScale(0.5);
        continueItem->setPosition(ccp(size.width / 2 + 40,size.height / 2 - 60));
        continueItem->setEnabled(false);
        CCMenuItemImage *aboutItem = CCMenuItemImage::create("aboutA.png", "aboutB.png",this,menu_selector(GameMenu::menuAboutCallback));
        aboutItem->setScale(0.5);
        aboutItem->setPosition(ccp(size.width / 2 + 40,size.height / 2 - 100));
        aboutItem->setEnabled(false);
        soundItem = CCMenuItemImage::create("sound-on-A.png", "sound-on-B.png",this,menu_selector(GameMenu::menuSoundCallback));
        soundItem->setScale(0.5);
        soundItem->setEnabled(false);
        soundItem->setPosition(ccp(40,40));
        CCMenu* mainmenu = CCMenu::create(newGameItem,continueItem,aboutItem,soundItem,NULL);
        mainmenu->setPosition(ccp(0,0));
        this->addChild(mainmenu,1,3);

    初始化背景音乐:

    //初始化声音
        SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic("background.mp3");
        SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(0.5);
        SimpleAudioEngine::sharedEngine()->playBackgroundMusic("background.mp3");

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

    void GameMenu::onEnter(){
        CCLayer::onEnter();
        CCSize size = CCDirector::sharedDirector()->getWinSize();
        CCNode* mainmenu = this->getChildByTag(3);
        mainmenu->setScale(0);
        mainmenu->runAction(CCSequence::create(CCScaleTo::create(0.5,1),CCCallFunc::create(this, callfunc_selector(GameMenu::menuEnter)),NULL));
    }

    使用getChildByTag()方法取得mainNenu。这也正是为何addChild()时,要加入tag参数了。

    CCSequence::create()里,CCScaleTo动作执行完之后,调用GameMenu类的menuEnter方法

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

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

    void GameMenu::menuNewGameCallback(CCObject* pSender)
    {
        CCDirector::sharedDirector()->setDepthTest(true);
        CCDirector::sharedDirector()->replaceScene(CCTransitionPageTurn::create(0.5,GameMain::scene(), false));
    }

    最后是控制背景音乐的方法

    void GameMenu::menuSoundCallback(CCObject* pSender)
    {
        //设置声音
        if(! issound){
            soundItem->setNormalImage(CCSprite::create("sound-on-A.png"));
            soundItem->setDisabledImage(CCSprite::create("sound-on-B.png"));
            SimpleAudioEngine::sharedEngine()->playBackgroundMusic("background.mp3", true);
           issound = true;
        }else{
            soundItem->setNormalImage(CCSprite::create("sound-off-A.png"));
            soundItem->setDisabledImage(CCSprite::create("sound-off-B.png"));
            SimpleAudioEngine::sharedEngine()->stopBackgroundMusic();
           issound = false;
        }
    }


    再来看看关于界GameAboutScene.cpp


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

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


    GameObjStar.cpp星星类


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

    void GameObjStar::set_visable(bool var){
        _visable = var;
        this->setVisible(var);
    }

    在onEnter方法中初始化一下:缩放至一半大小,设置是否可见,设置尺寸。

    void GameObjStar::onEnter(){
        CCNode::onEnter();
        //星星初始化
        this->setContentSize(CCSizeMake(36.5,35.5));
        CCSprite* star = CCSprite::create("star.png");
        star->setScale(0.5);
        _visable = true;
        addChild(star,1);
    }


    GameMark.cpp分数类


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

    void GameMark::onEnter()
    {
        CCNode::onEnter();
        CCSize size = CCDirector::sharedDirector()->getWinSize(); 
        this->setContentSize(size);
       // bits = CCArray::create(5);
    	bits=new CCArray(5);
    	CCSprite *title= CCSprite::create("score.png");
        title->setPosition(ccp(size.width/2 + 120,size.height - 15));
        title->setScale(0.5);
        addChild(title);
        for(int i = 0;i < 5;i ++){
            CCSprite * shu = CCSprite::create("shu.png");
            ui = shu->getTexture();
            shu->setScale(0.5);
            shu->setTextureRect(CCRectMake(234,0,26,31));
            shu->setPosition(ccp(size.width - 15 - i * 15,size.height - 15));
            bits->addObject(shu);
            addChild(shu);
        }
        bits->retain();
        mark = 0;
    }

    bits->retain();// 应该是防止数组被回收

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

    setTextureRect(CCRectMake((temp - 1) * 26,0,26,31));
    void GameMark::addnumber(int var){
        //按位设置数字
        mark += var;
        int temp = mark % 10;
        if(temp > 0){
            ((CCSprite *)bits->objectAtIndex(0))->setTexture(ui);
            ((CCSprite *)bits->objectAtIndex(0))->setTextureRect(CCRectMake((temp - 1) * 26,0,26,31)); 
        }else{
            ((CCSprite *)bits->objectAtIndex(0))->setTexture(ui);
            ((CCSprite *)bits->objectAtIndex(0))->setTextureRect(CCRectMake(234,0,26,31)); 
        }
        temp = (mark % 100) / 10;
        if(temp > 0){
            ((CCSprite *)bits->objectAtIndex(0))->setTexture(ui);
            ((CCSprite *)bits->objectAtIndex(1))->setTextureRect(CCRectMake((temp - 1) * 26,0,26,31));  
     
        }else{
            ((CCSprite *)bits->objectAtIndex(0))->setTexture(ui);
            ((CCSprite *)bits->objectAtIndex(1))->setTextureRect(CCRectMake(234,0,26,31)); 
        }
        temp = (mark % 1000) / 100;



    再来看下萝莉类GameObjHero.cpp


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

    接受触摸:

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

    mainsprite = CCSprite::create("s_1.png");
        //动画
        CCAnimation * animation = CCAnimation::create();
        animation->addSpriteFrameWithFileName("s_1.png");
        animation->addSpriteFrameWithFileName("s_2.png");
        animation->addSpriteFrameWithFileName("s_3.png");
        animation->addSpriteFrameWithFileName("s_4.png");
        animation->addSpriteFrameWithFileName("s_5.png");
        animation->addSpriteFrameWithFileName("s_6.png");
        animation->setDelayPerUnit(0.1f);
        animation->setRestoreOriginalFrame(true);
        //运行奔跑动画
        mainsprite->runAction(CCRepeatForever::create(CCAnimate::create(animation)));

    上面这种添加动画的方式,是骨头第一次遇到。

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

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

    里面有个设置状态方法:

     switch(state){
            case 1://跳跃
                this->stopAllActions();
                mainsprite->stopAllActions();
                mainsprite->setTexture(jump);
                this->runAction(CCSequence::create(CCJumpBy::create(2.5,ccp(0,0),100,1),CCCallFunc::create(this, callfunc_selector(GameObjHero::jumpend)),NULL));
                break;
            case 2://受伤
                this->stopAllActions();
                mainsprite->stopAllActions();
                mainsprite->setTexture(hurt);
                this->runAction(CCSequence::create(CCBlink::create(3, 10),CCCallFunc::create(this, callfunc_selector(GameObjHero::hurtend)),NULL));
                ((GameMain *)this->getParent())->setover();
                break;


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

    然后是GameObjMap.cpp地图类


     void GameObjMap::bg1change(){
        //运动出屏幕重设位置,运动
        CCSprite * bg = (CCSprite *)this->getChildByTag(0);
        bg->setPosition(ccp(480,320));
        bg->runAction(CCSequence::create(CCMoveBy::create(4,ccp(-960,0)),CCCallFunc::create(this, callfunc_selector(GameObjMap::bg1change)),NULL));
        for(int i = 0;i < 5;i ++){
            ((GameObjStar *)stars1->objectAtIndex(i))->set_visable(true);
        }
    }
    void GameObjMap::bg2change(){
        //运动出屏幕重设位置,运动
        CCSprite * bg = (CCSprite *)this->getChildByTag(1);
        bg->setPosition(ccp(480,320));
        bg->runAction(CCSequence::create(CCMoveBy::create(4,ccp(-960,0)),CCCallFunc::create(this, callfunc_selector(GameObjMap::bg2change)),NULL));
        for(int i = 0;i < 5;i ++){
            ((GameObjStar *)stars2->objectAtIndex(i))->set_visable(true);
        }
    }

    楼上两个方法作用是背景循环,callfunc_selector方法调用的是对方,一对好机油!

     bg1->runAction(CCSequence::create(CCMoveBy::create(2,ccp(-480,0)),CCCallFunc::create(this, callfunc_selector(GameObjMap::bg1change)),NULL));
     bg2->runAction(CCSequence::create(CCMoveBy::create(4,ccp(-960,0)),CCCallFunc::create(this, callfunc_selector(GameObjMap::bg2change)),NULL));
       

    其实整个背景地图的宽度是两倍的可视屏幕宽度,即480*2=960,这样的地图有两套,两套地图的动画在第一次运行时正好差半个周期,而且是互相调用,呈现出来的效果就是一直循环下去。恩,懂了。
    然后在两套地图上绘制植物、草地等元素。


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

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

    最主要的循环方法:

    scheduleUpdate();
    void GameMain::update(float time){}

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

    bool GameMain::isCollion(CCPoint p1,CCPoint p2,int w1,int h1,int w2,int h2){
        if(abs(p1.x - p2.x) < w1 + w2 && abs(p1.y - p2.y) < h1 + h2){
            return true;
        }
        return false;
    };


    判断是否是掉落状态:

    void GameMain::isherodrop(){
    	CCPoint p1 = (map->getChildByTag(0))->getPosition();
    	CCPoint p2 = (map->getChildByTag(1))->getPosition();    
        int temp;
        if(p1.x <= 100 && (p1.x + 480) >= 100){
            temp = (100 - p1.x) / 64;
            if(bg1shu[temp] == -1){
               hero->setState(2);
            }
        }else{
            temp = (100 - p2.x) / 64;
            if(bg2shu[temp] == -1){
                hero->setState(2);
            } 
        }
    }

    上面方法比较难懂,大意就是根据当前地图的x值位置,来取得主角所站的位置有没有石头,没有的话即掉落。

    temp = (100 - p1.x) / 64;

    100是因为萝莉站在x=100的位置,所以要得到萝莉所在位置是否为空,即

    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时为空。


    本篇完结

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

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

    大家晚安。


  • 相关阅读:
    ABP 前端 组件之间传递参数的几种方式
    angular Form 自定义验证
    Docker 启用失败 failed to start docker Application container Engin
    C# 委托与事件
    c# Application.DoEvents()
    c# 泛型
    Ubuntu如何挂载U盘
    jdk1.8 List根据时间字段倒序排序
    yarn安装模块报错:check python checking for Python executable "python2" in the PATH
    yarn : 无法加载文件 C:\Users\Administrator\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本。
  • 原文地址:https://www.cnblogs.com/riasky/p/3455283.html
Copyright © 2011-2022 走看看