CCSprite 碰撞偵測


CCSprite 並沒有內建碰撞方法的設計,所以我們必須自己寫,不過大部分偵測碰撞的原理都是相同的,假設有 A, B 2個 Sprite 要偵測碰撞,首先取得 A Sprite 的 X,Y 座標,再根據 "錨點(AnchorPoint)" 計算 A Sprite 的面積, 接著也對 B Sprite 作相同的處理,最後再判斷這2個面積是否有重疊(碰撞),必須注意錨點的位置, CCSprite 預設錨點為中心位置, 如下紅點






所以當我們取得 X,Y 座標時,是以中心點開始計算,加上寬(width),高(height),位置會偏移,如下圖








有2個方法可以解決,第1個是開始建立 CCSprite 就設定新錨點,如下

   1:  CCSprite* tempspr = CCSprite::spriteWithFile("CloseNormal.png");
   2:  tempspr->setAnchorPoint(ccp(X,Y))

其中 ccp(X,Y) 為錨點比例,預設是0.5,0.5,你可以自由修改錨點,對齊左上或左下

第2個方法為計算 X,Y 座標時就先對錨點的偏移量作處理,如下

   1:  CCRect ccr1 = CCRect(spr1->getPositionX()-spr1->getContentSize().width/2,spr1->getPositionY()-spr1->getContentSize().height/2,spr1->getContentSize().width,spr1->getContentSize().height);

其中 -spr1->getContentSize().width/2 就是計算 實際位置的 X 座標,相同的 -spr1->getContentSize().height/2 為計算實際位置的 Y 座標,計算碰撞的方法如下


   1:  bool GameLogoScene::detectCollide(CCSprite* spr1,CCSprite* spr2)
   2:  {
   3:      bool iscollide = false;
   4:      
   5:      CCRect ccr1 = CCRect(spr1->getPositionX()-spr1->getContentSize().width/2,spr1->getPositionY()-spr1->getContentSize().height/2,spr1->getContentSize().width,spr1->getContentSize().height);
   6:   
   7:      CCRect ccr2 = CCRect(spr2->getPositionX()-spr2->getContentSize().width/2,spr2->getPositionY()-spr2->getContentSize().height/2,spr2->getContentSize().width,spr2->getContentSize().height);
   8:   
   9:      if(CCRect::CCRectIntersectsRect(ccr1,ccr2))
  10:      {
  11:          iscollide = true;
  12:      }
  13:   
  14:      CCLog("iscollide:%d",iscollide);
  15:   
  16:      return iscollide;
  17:  }

Admob 加入 Cocos2D-X 中

Admob 為 google 旗下的一個廣告平台(首頁),在 Android 或是 Ios 都有支援,可以直接和 google 帳號連結,使用方式在官網也有詳細解說,是相當方便的廣告平台

1.
首先你必須申請 Admob 帳號, 如果你有 google 帳號那就更方便了,可以直接連結

2.
在 Admob網站中建立應用程式廣告並取得發佈商 id ,這個發佈商 id 是一組15個英文數字組合,,正常情況是一個應用程式使用一組,也可以在多個應用程式中使用同一組

3.
下載 Admob SDK並解壓縮(目前版本為6.1.0),解壓縮之後為.jar檔,下個步驟我們會開始實作廣告


4.
我們以 HelloWorld 為範例( 注意: 這裡的範例為 Android 版本 )加入 admob,首先將 Admob SDK解壓縮後的.jar 檔加入專案中,加入方式為複製 GoogleAdMobAdsSdk-6.1.0.jar 並貼到專案目錄下的libs 資料夾中然後

開啟 Eclipse -> 要加入admob的專案點選右鍵 -> Properties -> Java Build Path -> Libraries -> Add External JARs... -> 選擇專案目錄中的libs資料夾的 GoogleAdMobAdsSdk-6.1.0.jar

5.
接著是程式碼實作部分,開啟 MainActivity.java

   1:  import com.google.ads.*;
   2:   
   3:  public class Test_Normal_1 extends Cocos2dxActivity{
   4:      private Cocos2dxGLSurfaceView mGLView;
   5:      
   6:      private AdView adView;
   7:      
   8:      protected void onCreate(Bundle savedInstanceState){
   9:          super.onCreate(savedInstanceState);
  10:          
  11:          // get the packageName,it's used to set the resource path
  12:          String packageName = getApplication().getPackageName();
  13:          super.setPackageName(packageName);
  14:          
  15:          setContentView(R.layout.game_demo);
  16:          mGLView = (Cocos2dxGLSurfaceView) findViewById(R.id.game_gl_surfaceview);
  17:          mGLView.setTextField((EditText)findViewById(R.id.textField));
  18:          
  19:          adView = new AdView(this, AdSize.BANNER, "a150189a3abeb1a");
  20:   
  21:          
  22:          LinearLayout layout = (LinearLayout)findViewById(R.id.AdLayout);
  23:          layout.addView(adView);
  24:   
  25:          adView.loadAd(new AdRequest());
  26:          
  27:      }

第 1 行把需要的套件 import 進來
第 6 行宣告 adview
第 19 行定義 adview ,分別設定廣告尺寸和發佈商 id
第 22 行建立版面設定 layout (R.id.AdLayout)
第 23 行設定 adview 的版面設定(layout)
第 25 行要求播放廣告

R.id.AdLayout

   1:  <LinearLayout 
   2:  android:id="@+id/AdLayout"
   3:  android:layout_width="wrap_content"
   4:  android:layout_height="wrap_content"
   5:  ></LinearLayout>


執行 HelloWorld,應該就會出現廣告囉

CCLabelTTF 應用在數字顯示

CCLabelTTF 在 HelloWorld 中應用在標題字體的顯示,當然想用在變化數字上也是相當方便,只需要做一些數值切換而已,我們在 GameLogoScene 中,首先在
GameLogoScene.h

   1:      int score;
   2:      
   3:      CCLabelTTF* ttf1;
   4:      void ttf1Load();
   5:      void ttf1Act();

第 1 行為分數,用來紀錄得分
第 3 行為 CCLabelTTF 指標 ,ttf1用來顯示分數
第 4 行宣告以及定義 ttf1 用
第 5 行用來位元及數值的變換

接著來看看.cpp的內容
GameLogoScene.cpp

   1:  void GameLogoScene::ttf1Load()
   2:  {
   3:      ttf1 = CCLabelTTF::labelWithString("", "Thonburi", 20);
   4:   
   5:      ttf1->setColor(ccc3(0,0,255));
   6:   
   7:      ttf1->setPosition(ccp(100,300));
   8:   
   9:      this->addChild(ttf1);
  10:  }
  11:   
  12:  void GameLogoScene::ttf1Act()
  13:  {
  14:      char tempc[20];
  15:   
  16:      std::string str1 = "";
  17:   
  18:      sprintf(tempc,"%d",score);
  19:   
  20:      str1 = tempc;
  21:   
  22:      ttf1->setString(str1.c_str());
  23:  }

第 3 行定義 ttf1 的內容,字型,以及字體大小
第 5 行設定顏色
第 7 行設定座標
第 9 行加入layer

第 14 行建立1個字元陣列轉換數值用
第 16 行建立 string 也是轉換用
第 18 行利用 sprintf方法把 score 轉換成字元陣列
第 20 行再把字元陣列內容給予 string
第 22 行設定 ttf1 的內容

最後我們一樣把 ttf1Load() 放在建構子中(16行)

   1:  GameLogoScene::GameLogoScene(void)
   2:  {
   3:      v1Load();
   4:   
   5:      //初始化各項數值
   6:      this->initWithColor(ccc4(255,255,255,255));
   7:      sleep = false;
   8:      gameFlow = 1;
   9:      GF1Time = 0;
  10:      logoSprActTime =0;
  11:      score =0;
  12:   
  13:   
  14:      logoSprLoad();
  15:   
  16:      ttf1Load();
  17:   
  18:      //加入schedule
  19:      this->schedule( schedule_selector(GameLogoScene::Run)); //加入schedule
  20:      
  21:  }

把 ttf1Act() 放到 Run() 迴圈中(10行)

   1:  void GameLogoScene::Run(float dt)
   2:  {
   3:   
   4:      if(gameFlow==GAMEFLOW1)
   5:      {
   6:          GF1Time++;
   7:          logoSprAct();
   8:          v1Add();
   9:   
  10:          ttf1Act();
  11:      }
  12:      
  13:  }

修改 ccTouchesEnded(),如果觸碰到了開關,分數 score 就會加 10 (25行)

   1:  void GameLogoScene::ccTouchesEnded(CCSet* touches, CCEvent* event)
   2:  {
   3:   
   4:      CCTouch* touch = (CCTouch*)( touches->anyObject() );
   5:   
   6:      CCPoint location = touch->locationInView();
   7:   
   8:      location = CCDirector::sharedDirector()->convertToGL(location);
   9:   
  10:      CCLog("convertToGLXY  x:%f, y:%f", location.x, location.y);
  11:      
  12:      std::vector<CCSprite*>::iterator iter;
  13:   
  14:      for(iter = v1->begin() ; iter != v1->end() ;)
  15:      {
  16:   
  17:          CCSprite *temp = *iter;
  18:              
  19:          if(location.x>temp->getPositionX()-temp->getContentSize().width/2 && location.x<temp->getPositionX()-temp->getContentSize().width/2+temp->getContentSize().width
  20:              && location.y>temp->getPositionY()-temp->getContentSize().height/2 && location.y<temp->getPositionY()-temp->getContentSize().height/2+temp->getContentSize().height)
  21:          {
  22:                  
  23:              iter = v1->erase(iter);
  24:                  
  25:              score+=10;
  26:          }
  27:          else
  28:          {
  29:              iter++;
  30:          }
  31:      }
  32:  }

最後在 visit() 方法中繪出 ttf1 (第20行)

   1:  void GameLogoScene::visit()
   2:  {
   3:      if(gameFlow==GAMEFLOW1)
   4:      {    
   5:          this->draw();
   6:   
   7:          logoSpr->visit();
   8:          
   9:          
  10:          std::vector<CCSprite*>::iterator iter;
  11:          for(iter = v1->begin() ; iter != v1->end() ; ++iter)
  12:          {
  13:   
  14:              CCSprite* temp = *iter;
  15:   
  16:              temp->visit();
  17:   
  18:          }
  19:          
  20:          ttf1->visit();
  21:   
  22:      }
  23:      
  24:  }
變化數字的功能已經完成,我們來加點小質感,在GameLogoScene.h新增一個tempscore變數(第2行)




   1:      int score;
   2:      int tempscore;
   3:   
   4:      CCLabelTTF* ttf1;
   5:      void ttf1Load();
   6:      void ttf1Act();

在ttf1Act()中加入計算(4~7行)





   1:  void GameLogoScene::ttf1Act()
   2:  {
   3:   
   4:      if(tempscore<score)
   5:      {
   6:          tempscore++;
   7:      }
   8:   
   9:   
  10:      char tempc[20];
  11:   
  12:      std::string str1 = "";
  13:   
  14:      sprintf(tempc,"%d",tempscore);
  15:   
  16:      str1 = tempc;
  17:   
  18:      ttf1->setString(str1.c_str());
  19:  }

這樣數字就會呈現不斷增加的情況,而不是直接變化




Cocos2D-X 使用粒子(系統)效果

Cocos2D-X 粒子系統相當方便,主要的類別是 CCParticleSystem ,它有許多子類別,凡是 CCParticle開頭的都是,如 CCParticleFire , CCParticleFlower , CCParticleRain等等, 而這些子類別是為了簡化使用,可以直接套用子類別的屬性設定,像是 粒子的生命周期,尺寸,顏色,當然你也可以自己設定這些數值,我們加入1個新的類別(Test_ParticleSystem)來練習

Test_ParticleSystem.h

   1:  #ifndef _TEST_PARTICLESYSTEM
   2:  #define _TEST_PARTICLESYSTEM
   3:  //create by zgh149 2012/07/27
   4:   
   5:  #include "cocos2d.h" //include 需要的標頭檔
   6:  #include "SimpleAudioEngine.h" //include 需要的標頭檔
   7:   
   8:  using namespace cocos2d; //使用名稱空間
   9:  using namespace CocosDenshion; //使用名稱空間
  10:   
  11:  class Test_ParticleSystem : public CCLayerColor // 繼承 CCLayerColor 方便使用畫布顏色
  12:  {
  13:   
  14:  public:
  15:   
  16:      Test_ParticleSystem(void); //建構子
  17:   
  18:      ~Test_ParticleSystem(void); //解構子
  19:   
  20:      virtual bool init(); //初始化
  21:   
  22:      static CCScene* scene(); //建立scene
  23:   
  24:      LAYER_NODE_FUNC(Test_ParticleSystem); //建立layer
  25:   
  26:      bool sleep; //控制此流程的進行
  27:   
  28:      int gameFlow; //此流程的迴圈控制
  29:   
  30:      const static int GAMEFLOW1 =1;
  31:   
  32:      void Run(float dt);//主要迴圈
  33:   
  34:      int GF1Time;//流程時間控制
  35:   
  36:      void visit();//繪圖
  37:   
  38:      void nextScene(CCNode* sender);//下一個流程
  39:   
  40:   
  41:      //測試粒子效果
  42:      CCParticleSystem* explosion;
  43:      void explosionLoad();
  44:      
  45:   
  46:  protected:
  47:   
  48:  private:
  49:   
  50:      void ccTouchesEnded(CCSet* touches, CCEvent* event);//控制觸碰
  51:   
  52:  };
  53:   
  54:  #endif
  55:   
  56:  //create by zgh149

Test_ParticleSystem 和 GameLogoScene 幾乎一模一樣, 去掉 logo 和 vector , 在 42 行加入粒子系統指標 explosion , 43 行為建立 explosion 的方法, 接著是 Test_ParticleSystem.cpp

Test_ParticleSystem.cpp

   1:  #include "Test_ParticleSystem.h"
   2:   
   3:  using namespace cocos2d;
   4:   
   5:  Test_ParticleSystem::Test_ParticleSystem(void)
   6:  {
   7:      
   8:      explosionLoad();
   9:   
  10:      //初始化各項數值
  11:      this->initWithColor(ccc4(0,0,0,255));
  12:      sleep = false;
  13:      gameFlow = 1;
  14:      GF1Time = 0;
  15:   
  16:      //加入schedule
  17:      this->schedule( schedule_selector(Test_ParticleSystem::Run),1); //加入schedule
  18:      
  19:      
  20:  }    
  21:   
  22:  Test_ParticleSystem::~Test_ParticleSystem(void)
  23:  {
  24:      
  25:      CCLog("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
  26:  }
  27:   
  28:   
  29:  CCScene* Test_ParticleSystem::scene()
  30:  {
  31:   
  32:      CCScene* scene = NULL;
  33:   
  34:      do 
  35:      {
  36:          
  37:          scene = CCScene::node();
  38:          CC_BREAK_IF(! scene);
  39:   
  40:          
  41:          Test_ParticleSystem *layer = Test_ParticleSystem::node();
  42:          CC_BREAK_IF(! layer);
  43:   
  44:         
  45:          scene->addChild(layer);
  46:      } while (0);
  47:   
  48:      return scene;
  49:  }
  50:   
  51:  void Test_ParticleSystem::Run(float dt)
  52:  {
  53:   
  54:      if(gameFlow==GAMEFLOW1)
  55:      {
  56:          GF1Time++;
  57:          
  58:          
  59:      }
  60:      
  61:  }
  62:   
  63:  void Test_ParticleSystem::ccTouchesEnded(CCSet* touches, CCEvent* event)
  64:  {
  65:   
  66:      CCTouch* touch = (CCTouch*)( touches->anyObject() );
  67:   
  68:      CCPoint location = touch->locationInView();
  69:   
  70:      location = CCDirector::sharedDirector()->convertToGL(location);
  71:   
  72:      CCLog("convertToGLXY  x:%f, y:%f", location.x, location.y);
  73:      
  74:      
  75:  }
  76:   
  77:   
  78:  bool Test_ParticleSystem::init()
  79:  {
  80:      bool bRet = false;
  81:      do 
  82:      {
  83:   
  84:          this->setTouchEnabled(true);
  85:      
  86:   
  87:          bRet = true;
  88:   
  89:      } while (0);
  90:   
  91:      return bRet;
  92:  }
  93:   
  94:  void Test_ParticleSystem::visit()
  95:  {
  96:      if(gameFlow==GAMEFLOW1)
  97:      {    
  98:          this->draw();    
  99:          explosion->visit();
 100:      }
 101:      
 102:  }
 103:   
 104:  void Test_ParticleSystem::nextScene(CCNode* sender)
 105:  {
 106:   
 107:      bool tempa = CocosDenshion::SimpleAudioEngine::sharedEngine()->isBackgroundMusicPlaying();
 108:   
 109:      if(tempa)
 110:      {
 111:          
 112:          CocosDenshion::SimpleAudioEngine::sharedEngine()->stopBackgroundMusic(true);
 113:      }
 114:      
 115:  }
 116:   
 117:   
 118:   
 119:  void Test_ParticleSystem::explosionLoad()
 120:  {
 121:      
 122:      explosion = CCParticleFireworks::create();
 123:      explosion->retain();
 124:      this->addChild(explosion, 10);
 125:      
 126:      explosion->setTexture( CCTextureCache::sharedTextureCache()->addImage("stars.png") );
 127:   
 128:          
 129:  }

第 119 行以上的都和 GameLogoScene 相當類似, 第 119 ~ 129 行為 explosion 的建立方法
第 122 行回傳 CCParticleFireworks 的指標
第 123 行保留 explosion,因為會不斷的使用
第 124 行加入此 layer 中
第 126 行設定材質資源,資源為圖片 stars.png, 當然你也必須在 resource 資料夾放入圖片, CCParticleFireworks會根據此圖片來建立單一粒子
第 8 行在建構子中放入 explosionLoad()方法

執行之後在畫面中央應該會有不斷噴出星星的粒子系統,如下圖













你可以試著修改 112 行的 CCParticleFireworks, 可改為其他粒子效果如 CCParticlerRain 等等,就會出現不同效果唷













以下列出比較常用的方法,如粒子系統的座標,尺寸等等

   1:  void Test_ParticleSystem::explosionLoad()
   2:  {
   3:      
   4:      explosion = CCParticleFireworks::create();
   5:      explosion->retain();
   6:      this->addChild(explosion, 10);
   7:      
   8:      explosion->setTexture( CCTextureCache::sharedTextureCache()->addImage("stars.png") );
   9:   
  10:      explosion->setStartSize(1);//開始尺寸
  11:   
  12:      explosion->setEndSize(20); //結束尺寸
  13:   
  14:      explosion->setDuration(3); //週期
  15:   
  16:      explosion->setStartColor(ccc4f(100,255,100,255));//開始顏色
  17:   
  18:      explosion->setEndColor(ccc4f(100,255,0,255));//結束顏色
  19:   
  20:      explosion->setPosition(ccp(480/2,0));//座標
  21:  }

在cocos2d-x 中 使用 Vector

在遊戲中,vector的使用程度遠高於array,主要因為其長度可以不必初始化宣告,之後可以動態增加, list 或 deque 也可以使用在 cocos2d-x 中,完全視需求而定,這邊以 GameLogoScene 為例, 加入 vector,
首先在 GameLogoScene.h 加入 vector 的宣告

GameLogoScene.h
   1:      std::vector<CCSprite*> *v1;
   2:      void v1Load();
   3:      void v1Add();
   4:      int v1AddTime;

第 1 行宣告 vector指標名稱為 v1 , 其中存放的物件為 CCSprite 指標
第 2 行為定義 v1 方法
第 3 行為 v1 加入物件方法
第 4 行為 v1 加入物件計時用

GameLogoScene.cpp
   1:  void GameLogoScene::v1Load()
   2:  {
   3:      v1 = new std::vector<CCSprite*>;
   4:   
   5:      v1AddTime =0;
   6:  }
   7:   
   8:  void GameLogoScene::v1Add()
   9:  {
  10:      v1AddTime++;
  11:      if(v1AddTime>=1)
  12:      {
  13:          v1AddTime =0;
  14:   
  15:          CCSprite* tempspr = CCSprite::spriteWithFile("CloseNormal.png");
  16:          
  17:          tempspr->setPosition(ccp(rand()%400,rand()%200));
  18:          
  19:          this->addChild(tempspr);
  20:   
  21:          v1->push_back(tempspr);
  22:      }
  23:   
  24:  }
第 1 ~ 6 行為定義 v1 的方法, 第3行定義 v1 存放物件為 CCSprite 指標,第5行初始化 v1addTime
第 8 ~ 24 行將 CCSprite指標加入 v1 方法, 第 10 ~ 11 行限制 v1 加入物件的速度,第 15 行設定加入的 CCSprite , 第 17 行設定座標,第 19 行將 CCSprtite 加入 layer ,第 21 行將 CCSprite 加入 v1,接著在建構子放入 v1Load()

   1:  GameLogoScene::GameLogoScene(void)
   2:  {
   3:      v1Load();
   4:   
   5:      //初始化各項數值
   6:      this->initWithColor(ccc4(255,255,255,255));
   7:      sleep = false;
   8:      gameFlow = 1;
   9:      GF1Time = 0;
  10:      logoSprActTime =0;
  11:   
  12:      logoSprLoad();
  13:   
  14:      //加入schedule
  15:      this->schedule( schedule_selector(GameLogoScene::Run),1); //加入schedule
  16:      
  17:      
  18:  }
在 Run 迴圈中放入 v1Add() 

   1:  void GameLogoScene::Run(float dt)
   2:  {
   3:   
   4:      if(gameFlow==GAMEFLOW1)
   5:      {
   6:          GF1Time++;
   7:          logoSprAct();
   8:          v1Add();
   9:      }
  10:      }
修改 visit()

   1:  void GameLogoScene::visit()
   2:  {
   3:      if(gameFlow==GAMEFLOW1)
   4:      {    
   5:          this->draw();
   6:   
   7:          logoSpr->visit();
   8:          
   9:          
  10:          std::vector<CCSprite*>::iterator iter;
  11:          for(iter = v1->begin() ; iter != v1->end() ; ++iter)
  12:          {
  13:   
  14:              CCSprite* temp = *iter;
  15:   
  16:              temp->visit();
  17:   
  18:          }
  19:   
  20:      }
  21:      
  22:  }
其中 visit() 在第 10 行使用迭代器搜尋所有在 v1 中的物件(第 11 行), 在第 16 行繪出 CCSprite,最後記得不要接續下一個流程(HelloWorld),執行吧,你應該會看到不少開關在螢幕上出現




到目前為止我們將物件加入 vector, 當然也可以刪除 vector 中的物件,在 ccTouchesEnded 中修改
   1:  void GameLogoScene::ccTouchesEnded(CCSet* touches, CCEvent* event)
   2:  {
   3:   
   4:      CCTouch* touch = (CCTouch*)( touches->anyObject() );
   5:   
   6:      CCPoint location = touch->locationInView();
   7:   
   8:      location = CCDirector::sharedDirector()->convertToGL(location);
   9:   
  10:      CCLog("convertToGLXY  x:%f, y:%f", location.x, location.y);
  11:      
  12:      std::vector<CCSprite*>::iterator iter;
  13:   
  14:      for(iter = v1->begin() ; iter != v1->end() ;)
  15:      {
  16:   
  17:          CCSprite *temp = *iter;
  18:              
  19:          if(location.x>temp->getPositionX() && location.x<temp->getPositionX()+temp->getContentSize().width
  20:              && location.y>temp->getPositionY() && location.y<temp->getPositionY()+temp->getContentSize().height)
  21:          {
  22:                  
  23:              iter = v1->erase(iter);
  24:                  
  25:          }
  26:          else
  27:          {
  28:              iter++;
  29:          }
  30:      }
  31:  }
第 12 行使用迭代器來搜尋 v1 所有的物件
第 19 ~ 20 行判斷觸碰的點(座標)是否在 CCSprite物件上,是的話就刪除(23行),否的話就搜尋下1個CCSprite,這篇介紹如何在 cocos2d-x 中使用 vector ,並加入和刪除物件




關於遊戲架構

這篇簡單介紹遊戲的架構,屬於個人的心得分享,也比較適合完全沒寫過遊戲或應用程式的朋友們,還記得我們在上一篇談到的遊戲流程嗎, 現在我們針對單個遊戲 "流程" 再分析
大部分的遊戲或應用程式的 "流程" 內容都脫離不了一個單純的架構,就是 while 的重複迴圈,而這個 while迴圈,裡面有3個方法分別處理不同部分的事件,第1個為 Input 方法, Input 主要處理使用者對裝置的輸入,以手機來舉例就是觸碰螢幕的處理,廣泛的說,手機上的相機鏡頭,麥克風,傳感器等等皆是,第2個為 DataProcess 方法, 通常排列在 Input 方法 之後,負責資料改變的處理,,第3個方法為 Paint 方法, 用來繪出所有的物件,以程式碼來表示,就像這樣

  while(isrun) //isrun 控制此流程的執行
  {
Input(); //輸入處理
DataProcess(); //資料處理
        Paint(); //繪圖處理
  }

相對的在cocos2d-x也可以使用相同的架構,現在教大家如何寫出自己的Logo流程,我們必須先準備一些材料,一張 PNG 圖片(這也是你的Logo), 一段 MP3 音樂檔(也可以不用,不過這樣就沒音樂囉),我們的目標是讓 Logo在全白的畫面(或全黑,你可以自由選擇)慢慢的淡入,接著停留2~3秒後再慢慢淡出,使用VC++開啟 HelloWorld 專案吧,在 Solution Explorer 視窗中的 include 資料夾點選右鍵 -> Add -> Class , 選擇 C++ Class , 接著只需要填入 Class Name 就好,我的Logo流程取名為 GameLogoScene, 完成之後在你的 include 和 source 資料夾 就會分別出現 GameLogoScene.cpp和.h檔,接著我們來看看GameLogoScene.h檔的內容吧

GameLogoScene.h


   1:  #ifndef _GAME_LOGO_SCENE
   2:  #define _GAME_LOGO_SCENE
   3:   
   4:  //create by zgh149 2012/07/22
   5:   
   6:  #include "cocos2d.h" //include 需要的標頭檔
   7:  #include "SimpleAudioEngine.h" //include 需要的標頭檔
   8:   
   9:  using namespace cocos2d; //使用名稱空間
  10:  using namespace CocosDenshion; //使用名稱空間
  11:   
  12:  class GameLogoScene : public CCLayerColor // 繼承 CCLayerColor 方便使用畫布顏色
  13:  {
  14:   
  15:  public:
  16:   
  17:      GameLogoScene(void); //建構子
  18:   
  19:      ~GameLogoScene(void); //解構子
  20:   
  21:      virtual bool init(); //初始化
  22:   
  23:      static CCScene* scene(); //建立scene
  24:   
  25:      LAYER_NODE_FUNC(GameLogoScene); //建立layer
  26:   
  27:      bool sleep; //控制此流程的進行
  28:   
  29:      int gameFlow; //此流程的分類
  30:   
  31:      const static int GAMEFLOW1 =1; //流程分類1
  32:   
  33:      void Run(float dt);//主要迴圈
  34:   
  35:      int GF1Time;//流程分類1的時間控制
  36:   
  37:      CCSprite* logoSpr; //使用Logo圖片的CCSprite
  38:   
  39:      void logoSprLoad();//logoSpr的資源讀取
  40:   
  41:      void logoSprAct();//logoSpr的動作    
  44:   
  45:      
  46:   
  47:      void nextScene(CCNode* sender);//下一個流程
  48:   
  49:      void visit();//繪圖
  50:   
  51:  protected:
  52:   
  53:  private:
  54:   
  55:      void ccTouchesEnded(CCSet* touches, CCEvent* event);//控制觸碰
  56:   
  57:  };
  58:   
  59:  #endif
  60:   
  61:  //create by zgh149

首先在6~7行 iclude 需要的標頭檔, cocos2d.h包含基本類別只要用到 CCLayer, CCSprite 之類的都要先 include 進來, SimpleAudioEngine.h 則是 cocos2d-x 提供的播放音效引擎,有使用到也是要 include
 9~10行使用名稱空間,這樣在後面就不用重複輸入前綴字
第12行定義 GameLogoScene類別繼承 CCLayerColor ,繼承 CCLayerColor 的原因是為了能直接設定畫布顏色
第17行為建構子,需要初始化的數值可以放在這裡
第19行為解構子,雖然 cocos2d-x 大部分的物件為 autorelease,不必特別去 delete, 但是使用自創類別的物件(動態記憶體配置),就必須手動 delete
第21行虛擬函式 virtual bool init() ,繼承CCLayerColor, 為了能直接設定畫布顏色
第23和25行都仿製 HelloWorld 的範例,建立 scene 和 layer
第27~35行之間為流程的控制變數, sleep控制流程的進行,true為停止流程,false為繼續流程, gameFlow用來控制流程的分類,甚麼是流程的分類呢? 以 Logo流程來說, 我們可以將 Logo的淡入視為分類1,而Logo的淡出視為分類2,整個流程的進行就是 分類1 -> 分類2 , 如此我們就能針對不同的分類做出處理,比如我們想讓玩家在分類1的時候觸碰螢幕,就跳到分類2,或是在分類1進行5秒之後的時候才播放音樂,而不是一開始進行分類1就播放,如此的分類控制,當然,分類的需要完全視需求而言,你可以自由定義流程的分類, GAMEFLOW1就是分類1,由於這個數值不需要變動,所以使用 const static , Run就是我們在前面提到的重複迴圈, GF1Time 是分類1的時間控制,如同剛剛的舉例,當我們想在分類1進行到某個時間,觸發某件事,所以通常會在每個分類都配置1個時間控制變數
第37~第45行之間就是 Logo 圖片的顯示動作,包含 CCSprite,CCAction的使用,都會在 GameLogoScene.cpp 說明
第47行為下一個流程的進行,如 Logo流程結束後通常都會接遊戲開頭選單,這邊我讓下一個流程為HelloWorld, 也介紹如何使用轉場
第49行為繪圖的方法,也是重覆迴圈的動作之一
第55行就是觸碰方面的控制,一樣在 GameLogoScene.cpp會有詳細的解釋!!

接著就是 GameLogoScene.cpp 的內容

GameLogoScene.cpp


   1:  #include "GameLogoScene.h"
   2:  #include "HelloWorldScene.h"
   3:   
   4:  using namespace cocos2d;
   5:   
   6:  GameLogoScene::GameLogoScene(void)
   7:  {
   8:      
   9:      //初始化各項數值
  10:      this->initWithColor(ccc4(255,255,255,255));
  11:      sleep = false;
  12:      gameFlow = 1;
  13:      GF1Time = 0;
  14:      
  15:   
  16:      logoSprLoad();
  17:   
  18:      //加入schedule
  19:      this->schedule( schedule_selector(GameLogoScene::Run),0.5); //加入schedule
  20:      
  21:      
  22:  }    
  23:   
  24:  GameLogoScene::~GameLogoScene(void)
  25:  {
  26:      
  27:  }
  28:   
  29:   
  30:  CCScene* GameLogoScene::scene()
  31:  {
  32:   
  33:      CCScene* scene = NULL;
  34:   
  35:      do 
  36:      {
  37:          
  38:          scene = CCScene::node();
  39:          CC_BREAK_IF(! scene);
  40:   
  41:          
  42:          GameLogoScene *layer = GameLogoScene::node();
  43:          CC_BREAK_IF(! layer);
  44:   
  45:         
  46:          scene->addChild(layer);
  47:      } while (0);
  48:   
  49:      return scene;
  50:  }
  51:   
  52:  void GameLogoScene::Run(float dt)
  53:  {
  54:   
  55:      if(gameFlow==GAMEFLOW1)
  56:      {
  57:          GF1Time++;
  58:          logoSprAct();
  59:   
  60:      }
  61:      
  62:  }
  63:   
  64:  void GameLogoScene::ccTouchesEnded(CCSet* touches, CCEvent* event)
  65:  {
  66:   
  67:      CCTouch* touch = (CCTouch*)( touches->anyObject() );
  68:   
  69:      CCPoint location = touch->locationInView();
  70:   
  71:      location = CCDirector::sharedDirector()->convertToGL(location);
  72:   
  73:      CCLog("convertToGLXY  x:%f, y:%f", location.x, location.y);
  74:   
  75:  }
  76:   
  77:   
  78:  bool GameLogoScene::init()
  79:  {
  80:      bool bRet = false;
  81:      do 
  82:      {
  83:   
  84:          this->setTouchEnabled(true);
  85:          
  86:          bRet = true;
  87:   
  88:      } while (0);
  89:   
  90:      return bRet;
  91:  }
  92:   
  93:   
  94:  void GameLogoScene::logoSprLoad()
  95:  {
  96:      logoSpr = CCSprite::spriteWithFile("newlogo.png");
  97:      
  98:      logoSpr->setPosition(ccp(480/2,320/2));
  99:   
 100:      this->addChild(logoSpr);
 101:      
 102:      logoSpr->setVisible(false);//這裡setIsVisible(false)是因為要讓logoSpr一開始能淡入
 103:  }
 104:   
 105:  void GameLogoScene::logoSprAct()
 106:  {
 107:      if(GF1Time==1)
 108:      {        
 109:          logoSpr->runAction(CCSequence::actions(CCShow::action(),CCFadeIn::actionWithDuration(2),NULL));        
 110:      CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("testlogo.mp3",false);}
 111:      else if(GF1Time==8)
 112:      {
 113:          logoSpr->runAction(CCSequence::actions(CCFadeOut::actionWithDuration(2),CCCallFuncN::actionWithTarget(this, callfuncN_selector(GameLogoScene::nextScene)),NULL));
 114:      }
 115:  }
 116:   
 117:   
 118:  void GameLogoScene::visit()
 119:  {
 120:      if(gameFlow==GAMEFLOW1)
 121:      {    
 122:          this->draw();
 123:   
 124:          logoSpr->visit();
 125:   
 126:      }
 127:      
 128:  }
 129:   
 130:  void GameLogoScene::nextScene(CCNode* sender)
 131:  {
 132:   
 133:      bool tempa = CocosDenshion::SimpleAudioEngine::sharedEngine()->isBackgroundMusicPlaying();
 134:   
 135:      if(tempa)
 136:      {
 137:          
 138:          CocosDenshion::SimpleAudioEngine::sharedEngine()->stopBackgroundMusic(true);
 139:      }
 140:      
 141:   
 142:      //到HelloWorld
 143:      CCScene* hw = HelloWorld::scene();
 144:      
 145:      CCScene* TranScene  = CCTransitionFade::transitionWithDuration(2, hw ,ccc3(0,0,0));
 146:   
 147:      if(TranScene)
 148:      {
 149:   
 150:          CCDirector::sharedDirector()->replaceScene(TranScene);
 151:      }
 152:      
 153:  }


OK~ 現在來看看GameLogoScene.cpp的內容吧
第1,2行把需要的標頭檔 include 進來, 在第2行中 #include "HelloWorldScene.h" 是為了下一個流程的進入
第4行使用名稱空間cocos2d,省了前綴字的麻煩
第6~22行為建構子內容
第10行設定畫布為白色底色, initWithColor(ccc4(255,255,255,255)); 其中  ccc4(255,255,255,255) 為ccColor4F類別,參數分別為 red,green,blue,alpha值,可以任意設定
第11行 sleep = false; 設定重複迴圈繼續
第12行 gameFlow = 1; 設定流程分類從分類1開始
第13行 GF1Time = 0; 設定分類1的時間控制從0開始
第16行 logoSprLoad(); 為 logoSpr 的資源(圖片)讀取函式,請跳到第94行,第94~103行為logoSpr的初始化,cocos2d-x中提供相當方便使用的CCSprite類別,你可以自由控制此sprite的圖片,位置,尺寸等等屬性,詳細用法請參考官網api
第96行 logoSpr = CCSprite::spriteWithFile("newlogo.png"); 將 newlogo.png 這張圖片設定為 logoSpr的材質, newlogo請改為你的圖片名稱
第98行 logoSpr->setPosition(ccp(480/2,320/2)); 設定 logoSpr的位置在螢幕的正中央,cocos2d-x的座標系統如下圖












X和Y座標的起點(原點 0,0 )在左下方,隨之遞增, width 為480, height為320, 所以我們 setPosition(ccp(480/2,320/2)); 就是把 logoSpr位置設定在螢幕的正中央
第100行 this->addChild(logoSpr); 把 logoSpr 加入 layer 中
第102行 logoSpr->setVisible(false); 因為 logoSpr 預設是顯示的,因為要做出淡入的效果,所以一開始必須先讓它消失
這邊就是 logoSprLoad() 的內容,接著我們再回去第19行吧
第19行 this->schedule( schedule_selector(GameLogoScene::Run),0.5); 首先 schedule() 方法的意思是 把傳入的參數( GameLogoScene::Run ) ,以0.5秒的速度,重複執行,也就是在架構中的重覆迴圈
第24 ~ 27行為解構子的內容,因為沒有使用動態記憶體配置,所以不需要 delete 物件
第30 ~ 50行就是仿製 HelloWorld的方法,建立 scene 和加入 layer
第52 ~ 62行為 Run函式的內容,第55行流程分類為分類1,分類的時間控制會遞增
第58行 為 logoSpr 的動作執行(淡入淡出),所以我們再跳到105行看看 logoSprAct()的內容吧
第107行為當分類1的時間控制為1的時候, logoSpr 將執行 runAction方法, CCSprite 可以藉由 runAction 方法 來執行一連串的動作, 在這裏我們傳入 CCSequence::actions(CCShow::action(),CCFadeIn::actionWithDuration(2),NULL) , CCSequence::actions() 代表一連串有順序的動作,第1個動作為  CCShow::action() ,顯示物件的意思,還記得我們在第102行設定 logoSpr 為消失,這裡我們就先讓它出現,接著第2個動作是 CCFadeIn::actionWithDuration(2) 代表 logoSpr將會淡入而過程為2秒, 第3個為 NULL ,為CCSequence規定最後1個動作,不可修改,所以整個 logoSpr 的連續動作就是

顯示 -> 淡入 -> 停止

接著第113行 logoSpr又執行一連串動作為 logoSpr->runAction(CCSequence::actions(CCFadeOut::actionWithDuration(2),CCCallFuncN::actionWithTarget(this, callfuncN_selector(GameLogoScene::nextScene)),NULL));
第1個動作是 CCFadeOut::actionWithDuration(2) 代表 logoSpr 淡出 過程為2秒, 第2個動作看起來似乎有點複雜,其實就是執行 GameLogoScene::nextScene 方法, 所以整個連續動作就是

淡出 -> 執行 GameLogoScene::nextScene 方法 -> 結束

第109行 CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("testlogo.mp3",false); 播放背景音樂 testlogo.mp3 當然這也是改成你自己的檔名

第64 ~ 75行為處理觸碰的方法, ccTouchesEnded(CCSet* touches, CCEvent* event) 是繼承 CCLayer 的方法,只要有觸碰的事件發生,此方法將會自動的被呼叫,所以即使在 Run 方法中並沒有呼叫,也不會影響觸碰的處理,第67 ~ 71行處理觸碰螢幕的X,Y座標, 第73行顯示觸碰的X,Y座標,你可以試著點選看看
第78 ~ 91行為繼承 CCLayer 的 init() 方法, 第84行開啟觸碰功能
第118 ~ 128行就是另一個重要部分,繪圖,第120行當分類為1,第122行畫出畫布(白色底色),第124行畫出 logoSpr ,記得這裡是有順序的,先畫出畫布再畫 logoSpr,所以 logoSpr 會疊在畫布上方, 交換這2行 logoSpr 會被畫布蓋住看不到了
最後第130 ~ 153行為 nextScene 方法,當 Logo 流程結束之後需要接到 HelloWorld 流程, 所以你必須將scene之間的轉換處理完成
第133行使用 CocosDenshion::SimpleAudioEngine::sharedEngine()->isBackgroundMusicPlaying(); 判斷是否有背景音樂的執行,有的話(135行)就停止背景音樂(138行),如果沒有做這邊的判斷,背景音樂將會延續到 HelloWorld 流程,第143行建立下一個流程的scene,也就是 HelloWorld , 接著第145行我使用 cocos2d-x 提供的轉場之一,讓整個scene之間的切換稍有質感, CCTransitionFade::transitionWithDuration(2, hw ,ccc3(0,0,0)); 第1個參數為轉場完成秒數,第2個參數為下一個接續的流程(HelloWorld), 第3個參數為轉場的顏色(黑色),事實上 cocos2d-x 提供了不下 20種轉場方便使用,只要是 CCTransitionXXX 就是轉場, 有興趣的話你可以修改試試效果

OK~ 這就是我使用的遊戲流程架構,分享給各位,當然架構是變化萬千,隨個人而異,這也是程式碼吸引人之處呀


實現 Cocos2D-X 跨平台功能 ( WINDOWS & MAC )

WINDOWS 平台
這篇就來介紹如何在PC上實現跨平台的功能吧,首先你也必須準備一些前置作業...

Android NDK部份
1. 需要先完整安裝 Android SDK ,版本必須1.5以上
2. 下載 Android NDK 目前使用版本為android-ndk-r6b-windows 直接到google下載
3. 解壓縮之後放到無中文路徑資料夾 如: D:\android-ndk-r6b


Cygwin部份
1. 下載cygwin,到 http://www.cygwin.com/ 下載setup.exe之後,選擇網路下載,安裝時選擇套件 Devel 安裝選項改為 Install 有1G多需要稍等一下
2. 安裝完成執行Cygwin 輸入make -v和gcc -v測試


轉code部份
1.先到cocos2d-1.0.0-x-0.9.0資料夾中, 編輯create-android-project.bat內容 ,修改以下3個路徑
(注意: 記得修改為自己的路徑)

set _CYGBIN=C:\cygwin\bin
The path of cygwin bin
set _ANDROIDTOOLS=D:\anroid\android-sdk-windows\tools
The path of android sdk tools
set _NDKROOT=D:\anroid\android-ndk-r5b
The root of ndk


2.執行create-android-project.bat ,執行之後依序輸入

package path:com.fox.test1  <-android package name
project name:test1 <-android project name
target id: <-android id

完成之後會在cocos2d-1.0.0-x-0.9.0資料夾中產生以剛剛輸入project name為名的資料夾
,裡面有3個資料夾,為android,Classes,Resource,

接下來
2-1.把cocos2dx專案裡的類別(.h和.cpp)全部放到Classes資料夾裡,
2-2.把cocos2dx專案裡的資源全部放到Resource資料夾裡,
2-3.編輯test1/android/jni/helloworld/Android.mk檔
2-4.打開Android.mk內容中有LOCAL_SRC_FILES, 把剛剛放到Classes裡的新類別填入,

如原本應該只有main.cpp \
../../../Classes/AppDelegate.cpp \
../../../Classes/HelloWorldScene.cpp

加入新的.cpp
main.cpp \
../../../Classes/AppDelegate.cpp \
../../../Classes/HelloWorldScene.cpp \
../../../Classes/xxx1.cpp \
../../../Classes/xxx2.cpp \
../../../Classes/xxx3.cpp


3.執行Cygwin,移動到cocos2d-1.0.0-x-0.9.0資料夾中的project name為名的資料夾裡的android資料夾中
(也就是cocos2d-1.0.0-x-0.9.0/test1/android)
移動指令為 cd d: <-移動到d槽  查詢資料夾指令 dir

4.輸入 ./build_native.sh

5.在轉code過程中 可以在輸出畫面看到過程,那裡出錯那裡失敗等等

6.成功轉code之後,android資料夾才真正可用

7.開啟eclipse, File -> New -> Project -> 選擇 androidProject -> Next -> 輸入project name

->在contents選擇 create project from existing source ->在location選擇剛剛產生的android資料夾(cocos2d-1.0.0-x-0.9.0/test1/android) -> Finish

轉code完成!!!

之後要再轉code只要從 "轉code部份" 的第2點開始就可以了


MAC 平台
在Mac上跨平台步驟相對的簡單,我們使用官網提供的範例 Cocos2dxSimpleGame 來做跨平台的示範,首先請到這裡下載,下載完畢後解壓縮吧

接著開啓終端機並使用命令列移動到cocos2d-1.0.1-x-0.10.0並執行create-android-project.sh,依據提示填入套件名稱,id,專案名稱等等,在專案名稱資料夾中,有三個子資料夾分別為android,Classes,Resources,第一個android資料夾主要是拿來放轉譯成功後的專案,第二個Classes資料夾是放置類別檔,當你在原專案有自創類別時就要把自創類別複製到Classes中並修改Android.mk,第三個資料夾Resources當然就是放置資源的地方,如圖形,音效等等

接著我們把Cocos2dxSimpleGame內的Classes資料夾中的所有類別複製到專案名稱資料夾的Classes中,開啟Classes中的Android.mk,修改


LOCAL_SRC_FILES := AppDelegate.cpp \
                   HelloWorldScene.cpp




LOCAL_SRC_FILES := AppDelegate.cpp \
                   HelloWorldScene.cpp \
                   GameOverScene.cpp

接下來把Cocos2dxSimpleGame內的Resources資料夾中的所有資源複製到專案名稱資料夾的Resources中,開啟終端機並使用命令列移動到專案名稱資料夾中的android資料夾,並執行build_native.sh,進行轉譯的動作,轉譯成功後,此時專案名稱資料夾中的android資料夾就可以拿來建立android專案囉,接著開啓Eclipse,File -> New -> Project -> Android Project,輸入Project Name,並選擇 Create Project from existing source,然後在Location欄位選擇剛剛的android資料夾路徑,接下來執行專案,編譯,運行完成!!!

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Affiliate Network Reviews