【COCOS2DX-BOX2D游戏开发之三】 读取tiledmap的tmx阻挡

做一款像素游戏项目,需要读取TMX文件中的阻挡区域,生成box2d的fixture,来做阻挡  使用cocos2dx版本: 2.2.2


1.在tmx文件中创建一个"Physics"的层,用来存放编辑器中生成的各种阻挡块

编辑器中主要有polygone, polyline,box和circle4种,其实box也属于polygone


【COCOS2DX-BOX2D游戏开发之三】 读取tiledmap的tmx阻挡_第1张图片

2.我的tiled map 版本Version 0.9.1

查看tmx文件,增加的层属于<Objectgroup>是不会被渲染的,所以添加多个层对效率也没有什么影响, 我们此处叫"Physics"

上图的4个图形,分别对应下图XML的4个<object></object>

【COCOS2DX-BOX2D游戏开发之三】 读取tiledmap的tmx阻挡_第2张图片

polyline读取一个初始点x,y,然后读取一些列相对于初始点的偏移值

box读取x,y,width,height

circle读取起始点x,y,外加直径width(height)足以,因为无法构造椭圆的b2Shape,所以我们构造圆

polygon读取起始点x,y,然后读取一些列相对于初始点的偏移值,类似于polyline




3.cocos2dx中解析此文件的时CCTMXXMLParser.cpp 大概在623行,但是代码中只解析了polygon,polyline和circle处解析代码为空,我们在此处添加完全


[cpp]  view plain copy
  1. else if (elementName == "polygon")   
  2.     {  
  3.         // find parent object's dict and add polygon-points to it  
  4.         ObjectGroup* objectGroup = (ObjectGroup*)m_pObjectGroups->lastObject();  
  5.         CCDictionary* dict = (CCDictionary*)objectGroup->getObjects()->lastObject();  
  6.   
  7.         // get points value string  
  8.         const char* value = valueForKey("points", attributeDict);  
  9.         if(value)  
  10.         {  
  11.             CCArray* pPointsArray = new CCArray;  
  12.   
  13.             // parse points string into a space-separated set of points  
  14.             stringstream pointsStream(value);  
  15.             string pointPair;  
  16.             while(std::getline(pointsStream, pointPair, ' '))  
  17.             {  
  18.                 // parse each point combo into a comma-separated x,y point  
  19.                 stringstream pointStream(pointPair);  
  20.                 string xStr,yStr;  
  21.                 char buffer[32] = {0};  
  22.                   
  23.                 CCDictionary* pPointDict = new CCDictionary;  
  24.   
  25.                 // set x  
  26.                 if(std::getline(pointStream, xStr, ','))  
  27.                 {  
  28.                     int x = atoi(xStr.c_str()) + (int)objectGroup->getPositionOffset().x;  
  29.                     sprintf(buffer, "%d", x);  
  30.                     CCString* pStr = new CCString(buffer);  
  31.                     pStr->autorelease();  
  32.                     pPointDict->setObject(pStr, "x");  
  33.                 }  
  34.   
  35.                 // set y  
  36.                 if(std::getline(pointStream, yStr, ','))  
  37.                 {  
  38.                     int y = atoi(yStr.c_str()) + (int)objectGroup->getPositionOffset().y;  
  39.                     sprintf(buffer, "%d", y);  
  40.                     CCString* pStr = new CCString(buffer);  
  41.                     pStr->autorelease();  
  42.                     pPointDict->setObject(pStr, "y");  
  43.                 }  
  44.                   
  45.                 // add to points array  
  46.                 pPointsArray->addObject(pPointDict);  
  47.                 pPointDict->release();  
  48.             }  
  49.               
  50.             dict->setObject(pPointsArray, "points");  
  51.             pPointsArray->release();  
  52.               
  53.             dict->setObject(dict->objectForKey("points"), "polygonPoints");  
  54.         }  
  55.     }   
  56.     else if (elementName == "polyline")  
  57.     {  
  58.         // find parent object's dict and add polyline-points to it  
  59.         // ObjectGroup* objectGroup = (ObjectGroup*)m_pObjectGroups->lastObject();  
  60.         // CCDictionary* dict = (CCDictionary*)objectGroup->getObjects()->lastObject();  
  61.         // TODO: dict->setObject:[attributeDict objectForKey:@"points"] forKey:@"polylinePoints"];  
  62.           
  63.         // ------Added by Teng.start  
  64.         // find parent object's dict and add polygon-points to it  
  65.         ObjectGroup* objectGroup = (ObjectGroup*)m_pObjectGroups->lastObject();  
  66.         CCDictionary* dict = (CCDictionary*)objectGroup->getObjects()->lastObject();  
  67.           
  68.         // get points value string  
  69.         const char* value = valueForKey("points", attributeDict);  
  70.         if(value)  
  71.         {  
  72.             CCArray* pPointsArray = new CCArray;  
  73.               
  74.             // parse points string into a space-separated set of points  
  75.             stringstream pointsStream(value);  
  76.             string pointPair;  
  77.             while(std::getline(pointsStream, pointPair, ' '))  
  78.             {  
  79.                 // parse each point combo into a comma-separated x,y point  
  80.                 stringstream pointStream(pointPair);  
  81.                 string xStr,yStr;  
  82.                 char buffer[32] = {0};  
  83.                   
  84.                 CCDictionary* pPointDict = new CCDictionary;  
  85.                   
  86.                 // set x  
  87.                 if(std::getline(pointStream, xStr, ','))  
  88.                 {  
  89.                     int x = atoi(xStr.c_str()) + (int)objectGroup->getPositionOffset().x;  
  90.                     sprintf(buffer, "%d", x);  
  91.                     CCString* pStr = new CCString(buffer);  
  92.                     pStr->autorelease();  
  93.                     pPointDict->setObject(pStr, "x");  
  94.                 }  
  95.                   
  96.                 // set y  
  97.                 if(std::getline(pointStream, yStr, ','))  
  98.                 {  
  99.                     int y = atoi(yStr.c_str()) + (int)objectGroup->getPositionOffset().y;  
  100.                     sprintf(buffer, "%d", y);  
  101.                     CCString* pStr = new CCString(buffer);  
  102.                     pStr->autorelease();  
  103.                     pPointDict->setObject(pStr, "y");  
  104.                 }  
  105.                   
  106.                 // add to points array  
  107.                 pPointsArray->addObject(pPointDict);  
  108.                 pPointDict->release();  
  109.             }  
  110.               
  111.             dict->setObject(pPointsArray, "points");  
  112.             pPointsArray->release();  
  113.               
  114.             dict->setObject(dict->objectForKey("points"), "polylinePoints");  
  115.         }  
  116.           
  117.         // ------Added by Teng.end  
  118.     }  
  119.     else if (elementName == "ellipse")  
  120.     {  
  121.         // ------Added by Teng.start  
  122.         // Do nothing...  
  123.         ObjectGroup* objectGroup = (ObjectGroup*)m_pObjectGroups->lastObject();  
  124.         CCDictionary* dict = (CCDictionary*)objectGroup->getObjects()->lastObject();  
  125.           
  126.         CCObject *obj = new CCObject;  
  127.         dict->setObject(obj, "ellipse");  
  128.         obj->release();  
  129.         // ------Added by Teng.end  
  130.     }  

4.剩下的就是我们在程序中获取出这些阻挡区域了

[cpp]  view plain copy
  1. bool Map::createPhysical(b2World *world)  
  2. {  
  3.     b2BodyDef body_def;  
  4.     body_def.type = b2_staticBody;  
  5.     body_def.position.SetZero();  
  6.     mBody = world->CreateBody(&body_def);  
  7.       
  8.     // 找出阻挡区域所在的层  
  9.     ObjectGroup* group = mTiledMap->objectGroupNamed("Physics");  
  10.       
  11.     CCArray* array = group->getObjects();  
  12.     CCDictionary* dict;  
  13.     CCObject* pObj = NULL;  
  14.     CCARRAY_FOREACH(array, pObj)  
  15.     {  
  16.         dict = (CCDictionary*)pObj;  
  17.         if (!dict)  
  18.             continue;  
  19.           
  20.         b2FixtureDef fixture_def;  
  21.           
  22.         StaticBlockObject *sb_obj = new StaticBlockObject();  
  23.           
  24.         sb_obj->density = 1.0f;  
  25.         sb_obj->friction = 0.2f;  
  26.         sb_obj->restitution = 0.f;  
  27.           
  28.         // 读取所有形状的起始点  
  29.         float x = ((CCString*)dict->objectForKey("x"))->floatValue();  
  30.         float y = ((CCString*)dict->objectForKey("y"))->floatValue();  
  31.           
  32.         b2Shape* shape = NULL;  
  33.           
  34.         //多边形  
  35.         CCObject *polygon = dict->objectForKey("polygonPoints");  
  36.         if (polygon) {  
  37.             CCArray *polygon_points = (CCArray*)polygon;  
  38.               
  39.             std::vector<b2Vec2> points;  
  40.               
  41.             // 必须将所有读取的定点逆向,因为翻转y之后,三角形定点的顺序已经逆序了,构造b2PolygonShape会crash  
  42.             int c =polygon_points->count();  
  43.             points.resize(c);  
  44.             c--;  
  45.               
  46.             CCDictionary* pt_dict;  
  47.             CCObject* obj = NULL;  
  48.             CCARRAY_FOREACH(polygon_points, obj)  
  49.             {  
  50.                 pt_dict = (CCDictionary*)obj;  
  51.                   
  52.                 if (!pt_dict) {  
  53.                     continue;  
  54.                 }  
  55.                   
  56.                 // 相对于起始点的偏移  
  57.                 float offx = ((CCString*)pt_dict->objectForKey("x"))->floatValue();  
  58.                 float offy = ((CCString*)pt_dict->objectForKey("y"))->floatValue();  
  59.                   
  60.                 points[c--] = (b2Vec2((x + offx) / PTM_RATIO, (y-offy) / PTM_RATIO));  
  61.             }  
  62.               
  63.             b2PolygonShape *ps = new b2PolygonShape();  
  64.             ps->Set(&points[0], points.size());  
  65.             fixture_def.shape = ps;  
  66.               
  67.             shape = ps;  
  68.               
  69.             sb_obj->shape = StaticBlockObject::ST_POLYGON;  
  70.         } else if (polygon = dict->objectForKey("polylinePoints")){  
  71.             CCArray *polyline_points = (CCArray*)polygon;  
  72.               
  73.             std::vector<b2Vec2> points;  
  74.               
  75.             CCDictionary* pt_dict;  
  76.             CCObject* obj = NULL;  
  77.             CCARRAY_FOREACH(polyline_points, obj)  
  78.             {  
  79.                 pt_dict = (CCDictionary*)obj;  
  80.                   
  81.                 if (!pt_dict) {  
  82.                     continue;  
  83.                 }  
  84.                   
  85.                 float offx = ((CCString*)pt_dict->objectForKey("x"))->floatValue();  
  86.                 float offy = ((CCString*)pt_dict->objectForKey("y"))->floatValue();  
  87.                 points.push_back(b2Vec2((x + offx) / PTM_RATIO, (y-offy) / PTM_RATIO));  
  88.             }  
  89.               
  90.             b2ChainShape *ps = new b2ChainShape();  
  91.             ps->CreateChain(&points[0], points.size());  
  92.             fixture_def.shape = ps;  
  93.               
  94.             shape = ps;  
  95.               
  96.             sb_obj->shape = StaticBlockObject::ST_POLYGON;  
  97.         } else if (dict->objectForKey("ellipse")) {  
  98.             float width = ((CCString*)dict->objectForKey("width"))->floatValue();  
  99.             float height = ((CCString*)dict->objectForKey("height"))->floatValue();  
  100.               
  101.             b2CircleShape *ps = new b2CircleShape;  
  102.             ps->m_p.Set((x+width/2) / PTM_RATIO, ((y+height/2)) / PTM_RATIO);  
  103.             ps->m_radius = width/2/PTM_RATIO;  
  104.             fixture_def.shape = ps;  
  105.               
  106.             shape = ps;  
  107.               
  108.             sb_obj->shape = StaticBlockObject::ST_CIRCLE;  
  109.         } else {  
  110.             float width = ((CCString*)dict->objectForKey("width"))->floatValue();  
  111.             float height = ((CCString*)dict->objectForKey("height"))->floatValue();  
  112.               
  113.             b2PolygonShape *ps = new b2PolygonShape;  
  114.             ps->SetAsBox(width/2/PTM_RATIO, height/2/PTM_RATIO, b2Vec2((x+width/2)/PTM_RATIO, (y+height/2)/PTM_RATIO), 0);  
  115.             fixture_def.shape = ps;  
  116.               
  117.             shape = ps;  
  118.               
  119.             sb_obj->shape = StaticBlockObject::ST_POLYGON;  
  120.   
  121.         }  
  122.   
  123.         fixture_def.density = sb_obj->density;  
  124.         fixture_def.friction = sb_obj->friction;  
  125.         fixture_def.restitution = sb_obj->restitution;  
  126.   
  127.         b2Fixture *fixture = mBody->CreateFixture(&fixture_def);  
  128.         sb_obj->fixture = fixture;  
  129.           
  130.         if (shape) {  
  131.             delete shape;  
  132.             shape = NULL;  
  133.         }  
  134.           
  135.         // Storage the Static block object.  
  136.         mStaticBlockList.push_back(sb_obj);  
  137.     }  
  138.       
  139.     return true;  
  140. }  


附带上StaticBlockObject代码,这个主要用来记录阻挡的类型、属性,以后用来做阻挡判断

[cpp]  view plain copy
  1. /** 
  2.     Storage fixture user data. 
  3.     use for b2Fixture user data. 
  4.  */  
  5. class iFixtureUserData  
  6. {  
  7. public:  
  8.     typedef uint BodyType;  
  9.     typedef uint FixtureType;  
  10.       
  11.     static const BodyType   BT_Avata = 0x000;  // no any use...  
  12.     static const FixtureType FT_None = 0x000;  
  13.       
  14.     static const BodyType BT_Map  = 0x1000;  
  15.     static const FixtureType FT_STATIC_OBJ = 0x1F01;  
  16.     static const FixtureType FT_DYNAMIC_OBJ = 0x1F02;  
  17.   
  18.     //  
  19.     static const BodyType BT_Role = 0x2000;  
  20.     static const BodyType BT_Bullet = 0x2100;  
  21.       
  22.     static const FixtureType FT_BODY = 0x2F01;  
  23.     static const FixtureType FT_FOOT = 0x2F02;  
  24.   
  25.       
  26. public:  
  27.     iFixtureUserData(BodyType body_type, FixtureType fixture_type):  
  28.         mBodyType(body_type), mFixtureType(fixture_type){  
  29.     }  
  30.       
  31.     virtual ~iFixtureUserData() {  
  32.           
  33.     }  
  34.   
  35.     inline BodyType getBodyType() { return mBodyType; }  
  36.     inline FixtureType getFixtureType() { return mFixtureType; }  
  37.       
  38. protected:  
  39.     BodyType mBodyType;  
  40.     FixtureType mFixtureType;  
  41. };  
  42.   
  43. /** 
  44.  Block object. 
  45.  specify a block area in physics engine. */  
  46. class StaticBlockObject : public iFixtureUserData {  
  47. public:  
  48.     StaticBlockObject():  
  49.     iFixtureUserData(BT_Map, FT_STATIC_OBJ),  
  50.     fixture(NULL),  
  51.     half_block(false)  
  52.     {  
  53.           
  54.     }  
  55.     enum ShapeType {  
  56.         ST_POLYGON = 0,  
  57.         ST_CIRCLE = 1,  
  58.         ST_EDGE = 2  
  59.     };  
  60.       
  61.     ShapeType shape;        // The shape type.  
  62.       
  63.     float density;  
  64.     float friction;  
  65.     float restitution;  
  66.       
  67.     b2Fixture *fixture;  
  68.       
  69.     bool half_block;  
  70. };  
  71.   
  72. typedef std::vector<StaticBlockObject *> StaticBlockList;  


因为是从代码中直接复制的,但无非实现了下面几个功能:

1.在map中找到"Physics"层

2.遍历读取所有图形的起始点,并分析是何种图形,并读取相应的属性

3.用读取的每个图形来构造b2Shape,用地图的body来构造b2Fixture

其中有几个地方需要注意:

1.因为Box2d和游戏中使用的单位不同,分别是米和像素,所以要用PTM_RATIO宏来转换

2.polygon读取的所有点来构造b2PolygonShape,这个序列必须是读取的所有点的反向列表,否则会报一个计算center断言错误

产生这个的原因是因为:tmx中的顶点坐标系是地图左上角,游戏中是左下角,CCTMXXMLParser.cpp中解析起始点的时候,自动帮我们转为游戏中的坐标

于是tmx中的1,2,3,4的顶点序列,构成的区域是凸多边形的内部,转换后就成为了4,3,2,1,构成的是凸多边形的外部

polyline因为不是闭合的,无所谓逆向一说

[cpp]  view plain copy
  1. std::vector<b2Vec2> points;  
  2.             
  3.           // 必须将所有读取的定点逆向,因为翻转y之后,三角形定点的顺序已经逆序了,构造b2PolygonShape会crash  
  4.           int c =polygon_points->count();  
  5.           points.resize(c);  
  6.           c--;  
  7.             
  8.           CCDictionary* pt_dict;  
  9.           CCObject* obj = NULL;  
  10.           CCARRAY_FOREACH(polygon_points, obj)  
  11.           {  
  12.               pt_dict = (CCDictionary*)obj;  
  13.                 
  14.               if (!pt_dict) {  
  15.                   continue;  
  16.               }  
  17.                 
  18.               // 相对于起始点的偏移  
  19.               float offx = ((CCString*)pt_dict->objectForKey("x"))->floatValue();  
  20.               float offy = ((CCString*)pt_dict->objectForKey("y"))->floatValue();  
  21.                 
  22.               points[c--] = (b2Vec2((x + offx) / PTM_RATIO, (y-offy) / PTM_RATIO));  
  23.           }  
  24.             
  25.           b2PolygonShape *ps = new b2PolygonShape();  
  26.           ps->Set(&points[0], points.size());  
  27.           fixture_def.shape = ps;  


5.传个效果图:

【COCOS2DX-BOX2D游戏开发之三】 读取tiledmap的tmx阻挡_第3张图片

你可能感兴趣的:(【COCOS2DX-BOX2D游戏开发之三】 读取tiledmap的tmx阻挡)