cocos2dx 3.1從零學習(一)——入門篇(一天學會打飛機)

  我們有C++基礎,學習引擎總是急於求成,想立馬做出一款簡單的遊戲給朋友玩。但是我們往往看了很多資料卻一直不知道如何下手去寫,有時候只要能走出第一步我們就會遊刃有餘,但是眼高手低的我們不是大神,需要有人指引一下。這裏我就寫一下我是如何入門學習cocos2dx3.1的,給大家參考一下。

    如果你想第一天就寫出微信打飛機,請耐心去閱讀。我也是一個菜鳥,博客難免粗糙和出錯,請大家諒解。加油吧!


    我們創建工程後總會自帶一個HelloWorld類,短短的幾行代碼就出來了一個遊戲的雛形,請問我們真的理解它了嗎?如果我們能早一點弄明白這幾行代碼,我們或許會比現在走得更遠。


理解HelloWorld

    HelloWorld去掉退出按鈕只有下面三個函數。

  1. static cocos2d::Scene* createScene();  
  2.  virtual bool init();   
  3.  CREATE_FUNC(HelloWorld);//一定要自己看源碼  

    學習之前我要強調一遍,這三個方法一定要做到透徹理解和重寫因爲所有的遊戲場景都需要這三個函數


    創建遊戲HelloWorld場景的時候,只需要在AppDelegate寫一句: Helloworld::createScene();

請看它的實現

  1. // 'scene' is an autorelease object  
  2. auto scene = Scene::create();  
  3.   
  4. // 'layer' is an autorelease object  
  5. auto layer = HelloWorld::create();  
  6.   
  7. // add layer as a child to scene  
  8. scene->addChild(layer);  
  9.   
  10. // return the scene  
  11. return scene;  

    首先新建一個新場景,然後create一個HelloWorld層並添加到新創建的場景,最後返回這個場景給AppDelegate

    一個場景的創建和展示就是這麼簡單,但是你有沒有發現HelloWorld中沒有create呢?並且沒有調用init()方法呢?我們在HelloWorld中沒有在任何地方看到調用init初始化那麼這是在哪裏調用的呢

    請看CREATE_FUNC的宏定義:

  1. #defineCREATE_FUNC(__TYPE__) \  
  2. static __TYPE__*create() \  
  3. { \  
  4.     __TYPE__ *pRet = new __TYPE__(); \  
  5.     if (pRet && pRet->init()) \  
  6.     { \  
  7.         pRet->autorelease(); \  
  8.         return pRet; \  
  9.     } \  
  10.     else \  
  11.     { \  
  12.         delete pRet; \  
  13.         pRet = NULL; \  
  14.         return NULL; \  
  15.     } \  
  16. }  

\表示換行因爲宏定義代碼如果放同一行閱讀不方便

    可以看到CREATE_FUNC宏其實是定義了一個靜態成員函數,這個函數可以new一個新的對象,並對其進行init()操作。

    這樣整個Helloworld的邏輯就清晰了:

          外部類調用static cocos2d::Scene* createScene();來創建一個新的場景。  CREATE_FUNC(HelloWorld);是定義一個靜態的create類成員函數,並在這個函數中調用virtual bool init();初始化這個場景。 

            以後每當我們新建一個場景的時候,按照這個格式即可。好了,下面該動手了。

    現在請刪除Helloworld新建一個叫MainScene的場景,完善該類,修改AppDelegate.cpp中的場景創建爲auto scene = MainScene::createScene();運行展示新的場景。(這個時候整個屏幕還是黑暗的,,因爲沒有任何元素顯示)。


    注意:如果是新手我建議你再看一下Helloworld裏面這三句代碼的定義你肯定會出錯

    比如:static方法裏面 auto layer =***(這裏是當前類名,不是Layer::create();

    init()方法最好加virtual修飾也可以這樣寫   virtual bool init() override;override表示繼承來的,對它重載)。init裏面一定先初始化父類Layer::init()

 

            到這裏應該會自己重寫一個空白的場景了,如果你以前不明白這裏,並且剛纔沒有動手寫的話,那麼再請你刪除CREATE_FUNC這句,自己重寫一個create函數吧。如果你記不起怎麼寫,我不建議你再繼續往下閱讀,學習就到此爲止吧。

動手重寫CREATE_FUNC宏定義

  1. MainScene *MainScene::create1(){  
  2.      auto mainS = new MainScene;  
  3.      if ( mainS && mainS->init())  
  4.      {  
  5.          mainS->autorelease();  
  6.          return mainS;  
  7.      }  
  8.      else  
  9.      {  
  10.          delete mainS;  
  11.          mainS = NULL;  
  12.          return nullptr;  
  13.      }  
  14. }  

    修改AppDegate.cpp裏面的HelloWorld::createScene();改    auto scene = MainScene::createScene();

再使用一下自己寫的create1:  auto layer=MainScene::create1();

   

    這樣我們就完全掌握了遊戲場景的創建和它的原理,其實更重要的是我們認識到了應該去怎麼學,cocos2dx引擎使用了大量的宏定義,我們一定不能只追求表面的用法,而應該深入下去學習宏實現了那些東西。特別是到後面的內存管理更是如此。


    總結一下也再重複一遍所有遊戲場景的基礎都是這三句代碼

  1. static cocos2d::Scene* createScene();  
  2. virtual bool init();   
  3. CREATE_FUNC(HelloWorld);  

    我在後面每一天新加場景都要手寫一遍一定要搞把這三句代碼以及實現牢記於心後面的學習中還會遇到大量的宏我們要學會跟蹤宏定義仔細閱讀每一段代碼這樣我們才能學懂而不是簡單的學會

 

    好後面接下來完善我們的第一個遊戲場景現在新建的場景是空白的什麼都沒有我們要嘗試往場景中添加各種展示元素

    這裏有三個定義要搞清楚場景(scene)、(layer)、精靈(sprite)。(原諒我表達能力有限大家最好先閱讀一本cocos2dx的書籍把一些理論知識弄明白)

    場景可以包含多個層層可以包含多個精靈。 精靈可以是我們在遊戲看到的所有的元素比如按鈕血量條人物等比如酷跑中,可以看到近處的背景精靈移動快遠處的背景精靈移動慢我們可以添加兩個層到場景中一個層中循環快速的精靈背景一個循環慢速的精靈背景。這樣就容易理解它們的概念了吧

 

進入正題

Sprite精靈創建

    如果你是新手,一定要把下面的代碼自己敲一下。磨刀不誤砍柴工,現在擔心敲代碼耽誤時間,以後只會耽誤更多的時間。

    首先在resource文件夾下放一張圖片00191880.jpg華爲入職時的照片工號191880從來沒改過^_^),

然後創建精靈,展示這張圖片。

    三句很簡單,添加到init()函數中吧!

  1. auto sprite =Sprite::create("00191880.jpg");//auto是C++11的自動推斷變量類型  
  2. sprite->setPosition(200, 200);//設置這個精靈在屏幕的位置  
  3. this->addChild(sprite);//把這個精靈添加到當前層中。  

    我沒學過OC,我以前做windows客戶端感覺這種寫法很不習慣入鄉隨俗吧如果你連this也不知道是什麼意思的話建議你看一下C++Primer,這本書很重要

    下面是我練習的代碼,自己嘗試一下吧!

  1. auto spriteA =Sprite::create("1.png");  
  2. auto spriteB =Sprite::create("2.png");     
  3. auto spriteC =Sprite::create("3.png");  
  4. this->addChild(spriteA);  
  5. this->addChild(spriteB);     
  6. this->addChild(spriteC);  
  7. Size visibleSize =Director::getInstance()->getVisibleSize();  
  8. spriteA->setPosition(visibleSize.width /4-30, visibleSize.height / 2);  
  9. spriteB->setPosition(visibleSize.width /4 * 3+32, visibleSize.height / 2);  
  10. spriteC->setPosition(visibleSize.width /2, visibleSize.height / 8 * 7+50);  
  11. spriteA->setScale(1.5);  
  12. spriteB->setScale(1.5);  
  13. spriteC->setScale(1.5);  

    如果你閱讀過cocos2dx的書籍或者百度一下的話,相信你上面的代碼一定看得懂。Director::getInstance()是一個單例,獲取整個遊戲的導演類,我不會告訴你後面的getVisibleSize()getWinSize()還有setScale()是什麼意思的,希望你能養成自己動手去查的習慣。


    下面寫一點小菜吧添加的精靈不會動是不是很沒意思?下面就讓圖片動起來:

init裏面添加

  1. this->schedule(schedule_selector(Second::myupdate));  

    這是一個定時器每隔一段時間會執行myupdate函數。

    myupdate的定義如下我不會告訴你schedule後面還可以再加一個參數表示隔多久執行一次的自己查去吧):

  1. voidSecond::myupdate(float f){//注意有一個float f 形參  
  2.     auto sp = this->getChildren();//獲取這個層中所有的孩子,也就是所有的精靈,看不懂?別逗我了,點進去看源碼吧,注意它的返回值類型。  
  3.     for (auto a: sp)  
  4.     {  
  5.        a->setPosition(a->getPosition().x, a->getPosition().y - 2);  
  6.     }  
  7. }  

    上面這段代碼就是移動剛纔你添加的所有的精靈

           for (auto  a: sp)看不懂好吧這個類似於迭代器的遍歷你可以改成for(auto a= sp.begin();a!=sp.end();a++){}(原諒我手打代碼沒有在編譯器寫因爲我是重新整理的)。

    再運行一下你的程序讓它們動起來吧如果你夠厲害的吧肯定會有辦法讓它們在屏幕中怎麼都停不下來

    補充:Sprite->setTexture()這個可以修改精靈的材質。

*****************************************************************************************

    到這裏,你是不是已經猜到微信打飛機飛機是怎麼移動的了?只要再加一個碰撞檢測就我們就可以實現了碰撞檢測?咱們不整這麼高端了其實就是for循環獲取這子彈和飛機的位置查看子彈精靈是否在飛機精靈的位置啦


咱們不急下面學一下標籤(Label),它讓我們可以展示打飛機的分數等。

Label的創建有很多種方法,下面簡單介紹三種

Label::create

Label::createWithTTF

Label::createWithBMFont

學過了Sprite肯定能看懂下面代碼再說一遍一定要自己去嘗試一下

  1. std::string words = "三翻四復";//windows下是不支持中文的,xcode支持。  
  2.  auto label = Label::create(words,"STHeiti", 30);//黑體,三十號  
  3.  this->addChild(label);  
  4.   
  5.  TTFConfigconfig("fonts/barnacle.ttf", 25);//TTF字體  
  6.  auto labelTTF =Label::createWithTTF(config, "Hello ");  
  7.  labelTTF->setPosition(333,333);  
  8.  labelTTF->setColor(Color3B(255, 0, 0));  
  9.  this->addChild(labelTTF);  
  10.   
  11.  auto labelTTF1 =Label::createWithTTF("ABCDEFG""fonts/16.ttf", 44);  
  12.  labelTTF1->setTextColor(Color4B(255, 0,0, 255));  
  13.  labelTTF1->setPosition(444, 444);  
  14.  labelTTF1->enableShadow(Color4B::BLUE,Size(10,-10));  
  15.  labelTTF1->enableOutline(Color4B::GREEN,3);  
  16.  labelTTF1->enableGlow(Color4B::BLACK);  
  17.  this->addChild(labelTTF1);  

//下面的代碼是我學習了前面的時候自己去寫的,多加嘗試

  1. std::vector<std::string> names ={"AAAAAAAA","BBBBBBBBBB","CCCCCCCCCCC","DDDDDDDDDDDD"};  
  2.  for (auto str : names){  
  3.      auto tmpLabel = Label::create(str,"STHeiti", 40);  
  4.     tmpLabel->setPosition(visibleSize.width / 2,visibleSize.height-i*45);  
  5.      tmpLabel->setColor(Color3B(rand() /255, rand() / 255, rand() / 255));  
  6.      tmpLabel->setRotation(rand()/180);  
  7.      this->addChild(tmpLabel);  
  8.  }  

    還有一個Label::createWithBMFont("fonts/futura-48.fnt","只能是英文或者數字"),futura-48.fnt字體很漂亮,有需要的我再上傳吧。

 

    上面的代碼還有很多我沒提過的知識點因爲這些都是我學習的筆記最近我時間不是很多所以不會寫得太細有忽略掉的就自己動手去查吧相信你的自學能力不會比我差

setColor是設置顏色enableShadow加陰影。enableOutline加描邊還有很多特性大家有興趣都自己嘗試一下


*********************************************************************************************

開始打飛機

    到這裏一個場景典型的元素就介紹完了,Spritelable,下面來個硬菜吧打飛機

    利用微信打飛機的素材實現打飛機的基本功能

有幾個小細節說一下

1:使用鼠標拖動飛機

    因爲還沒學習事件響應這裏提前學習一下單點觸控

    在init添加下面這段代碼屏幕就可以響應單擊事件了

  1.  auto listen =EventListenerTouchOneByOne::create();  
  2.  listen->onTouchBegan =CC_CALLBACK_2(GameScene::onTouchBegan, this);  
  3.  listen->onTouchMoved =CC_CALLBACK_2(GameScene::onTouchMoved, this);  
  4.  listen->setSwallowTouches(true);  
  5. Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listen,this);  

          CC_CALLBACK_2表示回調函數接收兩個參數

          onTouchBeganonTouchMoved等在頭文件聲明這幾個函數是繼承自基類Layer如果你不確定跟基類的函數名是否一致請在聲明的時候加override,表示重載注意onTouchBegan是返回值類型是bool,其他的是void。

    bool onTouchBegan (Touch*, Event*)override;

    void onTouchMoved(Touch*, Event*)override;

  1. voidGameScene::onTouchMoved(Touch* pTouch, Event* pEvent){  
  2.     Point touch =pTouch->getLocation();//返回點擊的位置  
  3.     Rect rectPlayer =spPlayer->getBoundingBox();//看返回值類型,應該知道這個是飛機所佔矩形區域的大小  
  4.    
  5.     if(rectPlayer.containsPoint(touch)){//如果點擊的點在這個矩形區域內就可以對飛機進行拖動  
  6.         Point temp = pTouch->getDelta();  
  7.        spPlayer->setPosition(spPlayer->getPosition() + temp);  
  8.     }  
  9.    
  10. }  
  11.    

 getBoundingBox()是獲取精靈所佔的矩形大小,containsPoint()查看點是否在矩形內。知道了如果響應單點觸控這樣就可以完全實現飛機的拖拽了

 

2.如何判斷子彈是否命中飛機

    我前面提到過定時器每幀執行回調函數。可以把敵機存到一個數組裏每次遍歷敵機數組判斷子彈的點是否在敵機中如果是的話就表示命中,然後在數組中刪除敵機元素在層中刪除敵機精靈

對精靈執行  sp->removeFromParentAndCleanup(true);可以在層中消除自身


到這裏應該可以寫出簡單的打飛機了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章