• cocos2d-x游戏之2048


        学习游戏编程是一件非常有趣的事情,在cocos2dx官网找了几个简单的游戏试试手,感觉也不是那么难,首先来看看2048这款游戏吧,很火的原因之一是因为它简单而易操作。网上这位Legendof1991大神很早就写过了,我大部分代码都是根据他的文章来的,但是也有些细节的地方自己修改了,下面就正是进入游戏吧。

    先书写一下整个游戏的流程图:

    2048(1)

       图1  2048简单流程图

    一、主场景的创建
    

          首先还是新建一个工程,名字随便你怎么取,然后按照老规矩该添加图层就添加图层。先来看下它的头文件:

    class HelloWorld : public cocos2d::Layer
    {
    public:
       
     // there's no 'id' in cpp, so we recommend returning the class instance pointer
        static cocos2d::Scene* createScene();
    
        // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
        virtual bool init();  
        
        // a selector callback
        void menuCloseCallback(cocos2d::Ref* pSender);
        
        // implement the "static create()" method manually
        CREATE_FUNC(HelloWorld);
         
        virtual bool  onTouchBegan(Touch *touch, Event *unused_event);
        virtual void  onTouchEnded(Touch *touch, Event *unused_event);
        
        bool  doUp();
        bool  doDown();
        bool  doLeft();
        bool  doRight();
    
        //创建卡片函数
        void  createCard(Size size);
        void autoCreateCardNumber();
        //判断游戏是否能够继续
        void  doCheckGameOver();
    private:
        int firstX,firstY,endX,endY;
        CardSprite* cardArray[4][4];
        //游戏得分 
        int score;
        CCLabelTTF *labelTTFScoreName;
        CCLabelTTF* labelTTFScoreNumber;
    
    };
    
    #endif // __HELLOWORLD_SCENE_H__

           其中这两个函数virtual bool onTouchBegan(Touch *touch, Event *unused_event);virtual void onTouchEnded(Touch *touch, Event *unused_event);主要是用来判断触屏滑动的方向。

    void HelloWorld::onTouchEnded(Touch *touch, Event *unused_event)
    {
        Point touchPoint=touch->getLocation();
        //touch->getLocationInView();
        endX=firstX-touchPoint.x;
        endY=firstY-touchPoint.y;
        if (abs(endX)>abs(endY))
        {
            if(endX+5>0)  
            {  
                //向左  
                doLeft(); 
            }  
            else  
            {  
                //向右  
                doRight();  
            }  
        }
        else
        {
            if(endY+5>0)  
            {  
                //向下  
                doDown();  
            }  
            else  
            {  
                //向上  
                doUp();  
            }  
        }
       }

          这段代码通过世界坐标和窗口坐标之间的差值来判断的,经过测试可行。具体的原理他也没有说明。

    二、卡片的创建
    

         卡片我们需要新建一个卡片类来实现,类中我们需要设置一些初始化动作,我把他放在下面这个函数中。

    CardSprite* CardSprite::createCardSprite(int numbers,int width,int height,float CardSpriteX,float CardSpriteY)
    {
    //CardSprite *enemy=new CardSprite();
    CardSprite* enemy=CardSprite::create();
    //CardSprite* enemy=new CardSprite();
    
    if (enemy&&enemy->init())
    {
        //enemy->autorelease();    //自动释放,这种写法比较好,如果没有初始化好的话也可以安全删除
        enemy->enemyInit(numbers,width,height,CardSpriteX,CardSpriteY);
        enemy->retain();   //记得release
        return enemy;
    }
    CC_SAFE_DELETE(enemy);   //安全释放
    return NULL;
    }

         其中我们需要注意的地方是如果你用create函数之后,需要retain一次,不需要用了则要release,原文博客中用的是这两句:CardSprite *enemy=new CardSprite();enemy->autorelease();他这样处理的好处就是内存由内存池自动管理了。

    三、算法分析
    

         每个游戏核心都是算法处理。2048也不例外,它的核心思想是每行每列遇到相同的数字则一个乘以2,另一个卡片变成0,例如我们向左滑动的时候代码可以这样写,时间复杂度o(N3),其它三个方向的算法思路也是一样的。

    bool HelloWorld::doLeft()
    {
        bool isdo = false;
        for (int y = 0; y < 4; y++) 
        {
            for (int x = 0; x < 4; x++)
            {
    
                for (int x1 = x + 1; x1 < 4; x1++) 
                {
                    if (cardArray[x1][y]->getNumbers() > 0) 
                    {
                        if (cardArray[x][y]->getNumbers() <= 0) 
                        {
                            cardArray[x][y]->setNumbers(cardArray[x1][y]->getNumbers());
                            cardArray[x1][y]->setNumbers(0);
    
                            x--;   //这里不会发生溢出,原来已经x1++了。
                            isdo = true;
                        }else if(cardArray[x][y]->getNumbers() == cardArray[x1][y]->getNumbers())
                        {
                            cardArray[x][y]->setNumbers(cardArray[x][y]->getNumbers()*2);
                            cardArray[x1][y]->setNumbers(0);
                            score+=cardArray[x][y]->getNumbers();
                            labelTTFScoreNumber->setString(String::createWithFormat("%i",score)->getCString());
                            isdo = true;
                        }
                        break;
                    }
                }
            }
        }
        return isdo;
    }

          至于当你的卡片消除之后,主层中多于两个卡片空位时,你需要添加两张卡片填充空位,至于位置也是随机分布的,它采用递归的方式寻找没有数字的卡片。来看段代码:

    void HelloWorld::autoCreateCardNumber()
    {
        int i=CCRANDOM_0_1()*4;   //随机生成数字0-4
        int j=CCRANDOM_0_1()*4;    //随机生成数字
        if (cardArray[i][j]->getNumbers()>0)
        {   //有数字则递归调用直到有空白的地方为止
            autoCreateCardNumber();
        }
        else
        {
            cardArray[i][j]->setNumbers(CCRANDOM_0_1()*10<1?2:4);   //乘以10之后看是否小于1,出现2或者4,这里就是卡片出现的概率问题了
        }
        
    }
    四、总结
    

    整个游戏的难点大概就是这些吧,至于其他一些细节的地方大家可以看他的原文,这个游戏主要涉及到的难点就是卡片的消除及消除之后如何再把数字随机的添加到卡片上。下期任务是写一个简单版的打飞机游戏。望诸君共勉!

    作者:曹麒文

    出处:http://www.cnblogs.com/master-image/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面

  • 相关阅读:
    【Flask教程02】路由基本定义
    Ubuntu16.04下设置静态IP
    实例讲解虚拟机3种网络模式(桥接、nat、Host-only)
    greenplum单机安装
    GreenPlum 基础操作 入门教程
    repo
    RAW nand clear NAND eMMC
    #运算符、不同的指针类型、数组和指针、指针运算、堆、栈、静态区、只读区、下标VS指针
    LDPC知识点
    宏表达式与函数、#undef、条件编译、
  • 原文地址:https://www.cnblogs.com/master-image/p/3890835.html
Copyright © 2020-2023  润新知