1. 程式人生 > >cocos2d-x+box2d+tiled 製作無規則碰撞地圖

cocos2d-x+box2d+tiled 製作無規則碰撞地圖

問題內容:1  利用tiled 物件層繪製碰撞的邊界 
                  2 通過cocos2d-x "CCTMXTiledMap"的物件 讀取地圖中的點資訊 
                  3 通過讀出的資訊繪製box2d “b2EdgeShape”剛體 

問題解決:
引擎版本:cocos2d2.1beta3 + box2d 2.2.1 

一 關於tiled

1.1 建立物件層

                    點選歷史上面的帶星號按鍵 有新增物件層選項 點選建立 建立好後 圖層中會顯示物件層 

 1.2 多邊形與折線

                  建立好物件層後 選擇 此時上面折線選項可以應用 

 選擇選項繪製不規則線 例如: 

1.3 tmx檔案內容

                    儲存繪製完成的地圖後 記事本開啟tmx檔案看程式碼 

                           objectgroup部分為物件層內容   name為物件層名字(最好是英文,中文可能會出現問題)
                           object部分為所需要的內容 其中X,Y 表示繪製的不規則圖形的起點位置 ployline 為以起點為初始點 連續繪製的座標點值

                      以上為地圖所需要內容

二 引擎部分修改 使之能夠完成地圖內容的讀取

                     2.1 讀取方式可以有兩種 

                           一是 利用cocos2d-x所提供的介面 CCTMXTiledMap讀取 (下文詳解)
二是 利用xml解析器 TinyXml讀取 (此部分略過 有興趣的話可以自己嘗試下)

2.2  修改引擎

                          其中CCTMXTiledMap 不支援讀取polyline 需要修改引擎程式碼  CCTMXXMLPaerser.cpp     中CCTMXMapInfo::startElement函式 下部 有elementName == "polyline" / elementName == "polygon" 

程式碼塊 內容被隱藏掉 需要修改其中內容 如下:

 然                    後重新生成cocos2d-x專案 將生成好的.lib .dll拷到自己的專案中


                   以上為引擎部分修改

                  三 建立box2d剛體
3.1  思路是這樣的讀取polyline 點的座標    利用座標繪製 b2EdgeShape 
                       涉及的問題: 1 點座標獲取 2 polyline點座標的拆分 3 建立剛體
3.2 點座標獲取
3.2.1 需要的變數
                       CCTMXTiledMap *m_pMap = CCTMXTiledMap::create(“地圖TMX檔案的名字”); 
                       CCTMXObjectGroup* m_pGroup = m_pMap->objectGroupNamed("tmx檔案中objectgroup的名字 上文有介紹");
                       CCArray* m_pObjects = m_pGroup->getObjects();
                       CCDictionary* m_pDict = NULL;       
                       CCObject* m_pObj = NULL;

3.2.2 查詢資料的方法
                       //迴圈查詢所有object
                               CCARRAY_FOREACH(m_pObjects, m_pObj)
                      {
                             m_pDict = (CCDictionary*)m_pObj;
                             //儲存值的時候需要同時儲存X Y 和polyline 的 points    XY為所畫多邊形的起始點
                             //查詢X Y 值 
                             const char* key = "x";
                             int x = ((CCString*)m_pDict->objectForKey(key))->intValue();
                             key = "y";
                             int y = ((CCString*)m_pDict->objectForKey(key))->intValue();

                            key = "polyline"
                             // 如果名字為 “polyline” 讀取並儲存points值
                            CCString* s_points = (CCString*)m_pDict->objectForKey(key);
                       }

                              此時即獲得所需要的資料

3.3 polyline點座標的拆分

                      此部分內容大家隨意 就是字串的拆分與儲存 簡單介紹下我的方法 

                      c++中提供了strtok方法拆分字串 但個人認為有些小問題 所以用了其他方法:

                     typedef basic_string<char>::size_type S_T1;
                     static const S_T1 npos = -1;
                   //字串分割
                   //src 原字串 tok分隔字元(可多個) trim是否保留空串(預設保留) null_subst 空標記
                   //輸出 分隔結果向量
                  vector<string> myTokenize(const string& src, string tok, bool trim=false, string null_subst="")
                 {
                    if (src.empty() || tok.empty())
                     {
                           throw "emptystring/0";
                       }

                    vector<string> v;
                    S_T1 pre_index = 0, index = 0, len = 0;
                    while ((index = src.find_first_of(tok,pre_index)) != npos)
                   {
                        if ((len = index-pre_index) != 0)
                   {
                        v.push_back(src.substr(pre_index,len));
                   }
                   else if (trim == false)
                  {
                        v.push_back(null_subst);
                  }
                  pre_index = index + 1;
                  }
                  string endstr = src.substr(pre_index);
                  if (trim == false)
                 {
                       v.push_back(endstr.empty()?null_subst:endstr);
                 } 
                  else if(!endstr.empty())
                 {
                     v.push_back(endstr);
                  }
                    return v;
                  }

                當然用這個涉及一點string / const char* 的轉換 略過

                  string tok=" ,";     //分隔標誌 為 空格 和 逗號
                 //用vector儲存提取出來的所有點座標
                  typedef vector<string> V;
                  V v;
                  v = myTokenize(points,tok,true);
                  V::iterator it = v.begin();

    這樣 就獲得了 polyline 的points 所有的座標

 3.4 3 建立剛體

       float StringToFloat(string* str)
      {
           int len = str->length();
           char* c = new char[len+1];
           strcpy(c,str->c_str());
           float f = (float)atof(c);
           return f;
        }

           //遍歷儲存XY座標
           //畫圖方式 V1->V2 V2-V3 V3-V4..
           //第二條線起始點為第一條線終點

           b2Vec2 vs[2];

           for (; it!=v.end(); )
           {  
                //分別儲存第一點第二點XY
                float x1,y1,x2,y2;
                //定義剛體 剛體型別為b2EdgeShape
                b2BodyDef bd;
                b2Body* ground = m_World->CreateBody(&bd);
                b2EdgeShape shape;

                x1 = StringToFloat(&(*it));
                ++it;
                y1 = StringToFloat(&(*it));
                ++it;

                //只第一次不做 第一條線起始點為首先讀取點值
                if (!m_bIsSaveV2)
                {
                        vs[0].Set((posx+x2)/PTM_RATIO, (posy-y2)/PTM_RATIO);
                        vs[1].Set((posx+x1)/PTM_RATIO, (posy-y1)/PTM_RATIO);
                        shape.Set(vs[0], vs[1]);
                        ground->CreateFixture(&shape, 0.0f);

                }
                //控制第一次不做 這個變數在初始賦值時為true 等迴圈建立完剛體後 再次變為true
                m_bIsSaveV2 = false;

                //防止點的數量為單數 it無值
                if(it==v.end())
                {
                        break;
                }
                x2 = StringToFloat(&(*it));
                ++it;
                y2 = StringToFloat(&(*it));
                ++it;
                //設定點位置 cocos2d座標系與tmx座標系Y方向相反 得出的Y值需相減
                //posx posy 為之前儲存的起始點座標X Y
                vs[0].Set((posx+x1)/PTM_RATIO, (posy-y1)/PTM_RATIO);
                vs[1].Set((posx+x2)/PTM_RATIO, (posy-y2)/PTM_RATIO);
                shape.Set(vs[0], vs[1]);
                ground->CreateFixture(&shape, 0.0f);
        }


         剛體建立完成


   else if (elementName == "polygon") 
    {
                CCTMXObjectGroup* objectGroup = (CCTMXObjectGroup*)pTMXMapInfo->getObjectGroups()->lastObject();
                CCDictionary* dict = (CCDictionary*)objectGroup->getObjects()->lastObject();
                const char* val = valueForKey("points", attributeDict);
                CCString* obj = new CCString(val);
                dict->setObject(obj, "polygon");
                obj->release();
    }
    else if (elementName == "polyline")
    {
                CCTMXObjectGroup* objectGroup = (CCTMXObjectGroup*)pTMXMapInfo->getObjectGroups()->lastObject();
                CCDictionary* dict = (CCDictionary*)objectGroup->getObjects()->lastObject();
                const char* val = valueForKey("points", attributeDict);
                CCString* obj = new CCString(val);
                dict->setObject(obj, "polyline");
                obj->release();
    }