關於遊戲架構

這篇簡單介紹遊戲的架構,屬於個人的心得分享,也比較適合完全沒寫過遊戲或應用程式的朋友們,還記得我們在上一篇談到的遊戲流程嗎, 現在我們針對單個遊戲 "流程" 再分析
大部分的遊戲或應用程式的 "流程" 內容都脫離不了一個單純的架構,就是 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~ 這就是我使用的遊戲流程架構,分享給各位,當然架構是變化萬千,隨個人而異,這也是程式碼吸引人之處呀


標籤: ,