战争迷雾4

转载请注明,原文地址:http://www.benmutou.com/blog/archives/485

 

正文:

1. 复习一下

还记得我们的理论基础吗?点击某个瓦片后,修改瓦片的某个顶点值,同时修改附近瓦片的顶点值:

1)当前瓦片右下角的顶点值设为4

2)当前瓦片右边的瓦片的左下角顶点值设为8

3)当前瓦片下方的瓦片的右上角顶点值设为1

4)当前瓦片右下方的瓦片的左上角顶点值设为2

(小若:好混乱!但是我看懂了)

 

我们先来实现第一步,修改当然点击的瓦片的顶点值。看以下代码:

  1. bool HelloWorld::ccTouchBegan( CCTouch *pTouch, CCEvent *pEvent )  
  2. {  
  3.    
  4. CCPoint tiledMapPos = getMapTiledPos(  
  5.    
  6. cloudMap,  
  7.    
  8. CCDirector::sharedDirector()->convertToGL(pTouch->getLocationInView()));  
  9.    
  10. CCTMXLayer* cloudLayer = cloudMap->layerNamed("cloudLayer");  
  11.    
  12. changeCloudTiled4(cloudLayer, tiledMapPos);  
  13.    
  14. return true;  
  15.    
  16. }  

我们在触屏事件里添加了一步操作——切换当前点击瓦片的顶点值。

2. 当前点中的瓦片,修改瓦片右下角的顶点值为4

cloudLayercloudMap.txm地图的第一个图层,我把它命名为“cloudLayer”。

噢,对了,我们应该先介绍一个类:TiledFourCellTiledFourCell是代表瓦片4个顶点值数据的对象,很简单的:

  1. class TiledFourCell : public CCNode {  
  2. public:  
  3.    
  4. CREATE_FUNC(TiledFourCell);  
  5.    
  6. virtual bool init();  
  7.    
  8. void setiLeftTop(int _iLeftTop);  
  9.    
  10. int getiLeftTop();  
  11.    
  12. void setiLeftBottom(int _iLeftBottom);  
  13.    
  14. int getiLeftBottom();  
  15.    
  16. void setiRightTop(int _iRightTop);  
  17.    
  18. int getiRightTop();  
  19.    
  20. void setiRightBottom(int _iRightBottom);  
  21.    
  22. int getiRightBottom();  
  23.    
  24. /* 获取顶点值的总和 */  
  25.    
  26. int getiTotalNum();  
  27.    
  28. private:  
  29.    
  30. int iLeftTop; // 左上角   
  31.    
  32. int iLeftBottom;// 左下角   
  33.    
  34. int iRightTop; // 右上角   
  35.    
  36. int iRightBottom; // 右下角   
  37.    
  38. };  
 

总共四个属性分别代表瓦片的四个顶点值,以及一个用于获取顶点值总和的函数。

(小若:为什么一个瓦片又分成4个顶点?)

 

理论部分已经说过了,一个瓦片用哪个图素取决于这个瓦片四个顶点值的总和。话说,为什么我非得回答旁白的问题!

接着,我们看看changeCloudTiled4函数:

 

  1. void HelloWorld::changeCloudTiled4( CCTMXLayer* layer, CCPoint pos )  
  2. {  
  3.    
  4. TiledFourCell* cell = getCellByTiledPos(pos);  
  5.    
  6. /* 数值4,在瓦片右下角 */  
  7.    
  8. cell->setiRightBottom(4);  
  9.    
  10. /* 根据瓦片4个顶点之和设置地图瓦片的图片 */  
  11.    
  12. setGidByTotalNum(layer, pos, cell->getiTotalNum());  
  13.    
  14. }  


 

这个函数也不难,首先取得瓦片的顶点对象TiledFourCell,至于怎么取得的,我们先不管。

(小若:啊!我最恨吊胃口了。)

 

然后,我们把瓦片右下角的顶点值设为4

最后,最关键的一步,这有点复杂,大家打起精神来。根据瓦片4个顶点之和设置瓦片的图片,大家回忆一下最开始介绍的理论,我们取得瓦片顶点值之和,用这个值去寻找对应的瓦片图素ID,然后用这个图素替换瓦片的当前图素。

还是来看看setGidByTotalNum函数吧:

  1. void HelloWorld::setGidByTotalNum( CCTMXLayer* layer, CCPoint pos, int iTotalNum )  
  2. {  
  3.    
  4. CCSize mapSize = cloudMap->getMapSize();  
  5.    
  6. /* 避免超出范围 */  
  7.    
  8. if(pos.x < 0 || pos. y < 0  
  9.    
  10. || pos.x >= mapSize.width || pos.y >= mapSize.height) {  
  11.    
  12. return;  
  13.    
  14. }  
  15.    
  16. /* iTotalNum是瓦片的4个顶点数据值的总和,通过这个值获得对应的瓦片图素 */  
  17.    
  18. CCInteger* gidInt = (CCInteger*)gidsArray->objectAtIndex(iTotalNum);  
  19.    
  20. /* 用新的瓦片图素替换pos坐标上的瓦片图素 */  
  21.    
  22. layer->setTileGID(gidInt->getValue(), pos);  
  23.    
  24. }  
 

CCInteger* gidInt = (CCInteger*)gidsArray->objectAtIndex(iTotalNum);就是为了取得顶点值之和对应的图素ID。然后用setTileGID函数替换在pos坐标上的瓦片的图素。我们先忽略图素ID倒底是怎么取得的。

(小若:好乱好乱,我不学了!)

 

别激动,是有点乱,我们来理一下:

1)点击屏幕,获得瓦片坐标tiledMapPos

2)找到瓦片的顶点对象:TiledFourCell* cell = getCellByTiledPos(pos);

3)修改顶点对象的右下角顶点值为4cell->setiRightBottom(4);

4)取得顶点对象四个顶点值的和iTotalNum:cell->getiTotalNum()

5)根据iTotalNum取得瓦片图素ID

CCInteger* gidInt = (CCInteger*)gidsArray->objectAtIndex(iTotalNum);

6)用新的图素替换瓦片的图片:layer->setTileGID(gidInt->getValue(), pos);

(小若:那,那算了,我继续听。)

 

3. 看看如何取得瓦片的顶点对象

好,整个过程大家都清晰,那么,大家去泡杯咖啡吧,现在我们来慢慢看看getCellByTiledPos函数是怎么回事:

  1. TiledFourCell* HelloWorld::getCellByTiledPos( CCPoint pos )  
  2. {  
  3.    
  4. CCSize mapSize = cloudMap->getMapSize();  
  5.    
  6. /* 瓦片坐标原本是二维坐标,转换为一维数值 */  
  7.    
  8. int index = mapSize.width * pos.x + pos.y;  
  9.    
  10. /* 取得瓦片的顶点数据对象 */  
  11.    
  12. const char* keyCh = CCString::createWithFormat("%d", index)->getCString();  
  13.    
  14. CCObject* obj = tiledCellsDict->objectForKey(keyCh);  
  15.    
  16. TiledFourCell* cell = NULL;  
  17.    
  18. if(obj == NULL) {  
  19.    
  20. /* 如果该瓦片没有顶点数据对象,则创建一个,顶点数据值默认都为0 */  
  21.    
  22. cell = TiledFourCell::create();  
  23.    
  24. tiledCellsDict->setObject(cell, keyCh);  
  25.    
  26. }  
  27.    
  28. else {  
  29.    
  30. cell = (TiledFourCell*)obj;  
  31.    
  32. }  
  33.    
  34. return cell;  
  35.    
  36. }  
哇,这看起来有点复杂,不过没关系,我们先不管,我们来看看我给 HelloWordScene 新增的成员变量:
  1. CCDictionary* tiledCellsDict; // 瓦片坐标和瓦片顶点对象 键值对  


 

这已经很清楚了,我们利用瓦片坐标来索引瓦片的顶点对象。不过这个坐标有点特殊,它不是二维坐标,而是一维坐标。还记得我们的地图是10X10的规则吧,那么它总共有100个瓦片,一维坐标就是指瓦片在这100个中的索引。

二维坐标转换为一维坐标也很简单,公式如下:

 

  1. /* 瓦片坐标原本是二维坐标,转换为一维数值 */  
  2. int index = mapSize.width * pos.x + pos.y;  
     

用文字描述为:地图横向瓦片的数量乘以x坐标 加上 y坐标

接着就是从字典tiledCellsDict中取值了,很简单,不多说了。

(小若:才怪啊,我不懂吖。)

 

那简单地说一下吧,先用瓦片的一维坐标从字典中查值,因为字典的key必须为字符串,所以我们要把整型的一维坐标转换为字符串格式:

  1. const char* keyCh = CCString::createWithFormat("%d", index)->getCString();  



如果该瓦片还没有顶点对象,就创建一个,并且添加到字典里。

够详细了,旁白别再吐槽了。 (小若:噗,我就要吐槽。)

 

4. 如何根据瓦片顶点总和获取图素ID

最后了,大家坚持住。还记得下面的代码吗?

 

  1. /* iTotalNum是瓦片的4个顶点数据值的总和,通过这个值获得对应的瓦片图素 */  
  2.    
  3. CCInteger* gidInt = (CCInteger*)gidsArray->objectAtIndex(iTotalNum);  


我们来解释一下图素ID是如何取得的,我又为HelloWorldScene添加了一个成员变量:

  1. bool HelloWorld::init()  
  2. {  
  3.    
  4. bool bRet = false;  
  5.    
  6. do  
  7.    
  8. {  
  9.    
  10. CCLayerColor::initWithColor(ccc4(255, 255, 255, 255));  
  11.    
  12. cloudMap = CCTMXTiledMap::create("cloudMap2.tmx");  
  13.    
  14. this->addChild(cloudMap);  
  15.    
  16. this->setTouchEnabled(true);  
  17.    
  18. gidsArray = CCArray::createWithCapacity(16);  
  19.    
  20. tiledCellsDict = CCDictionary::create();  
  21.    
  22. gidsArray->retain();  
  23.    
  24. tiledCellsDict->retain();  
  25.    
  26. /* 瓦片地图使用4X4的图片,每张小图片对应的数值如下: 
  27.   
  28. 0 4 8 12 
  29.   
  30. 1 5 9 13 
  31.   
  32. 2 6 10 14 
  33.   
  34. 3 7 11 15 
  35.   
  36. 以上数值为瓦片格子的数据值。 
  37.   
  38. 瓦片图素从1开始计数,如下: 
  39.   
  40. 1 2 3 4 
  41.   
  42. 5 6 7 8 
  43.   
  44. 9 10 11 12 
  45.   
  46. 13 14 15 16 
  47.   
  48. 比如数据值4,对应的瓦片图素是2;数据值10,对应的瓦片图素是11。 
  49.   
  50. */  
  51.    
  52. /* 以数据值为索引,可以取得瓦片图素 */  
  53.    
  54. gidsArray->addObject(CCInteger::create(1));//顶点总和0,对应图素ID为1   
  55.    
  56. gidsArray->addObject(CCInteger::create(5));// 顶点总和1,对应图素ID为5   
  57.    
  58. gidsArray->addObject(CCInteger::create(9));// 顶点总和2,对应图素ID为9   
  59.    
  60. gidsArray->addObject(CCInteger::create(13));// 顶点总和3,对应图素ID为13   
  61.    
  62. gidsArray->addObject(CCInteger::create(2));// 顶点总和4,对应图素ID为2   
  63.    
  64. gidsArray->addObject(CCInteger::create(6));// 顶点总和5,对应图素ID为6   
  65.    
  66. gidsArray->addObject(CCInteger::create(10));  
  67.    
  68. gidsArray->addObject(CCInteger::create(14));  
  69.    
  70. gidsArray->addObject(CCInteger::create(3));  
  71.    
  72. gidsArray->addObject(CCInteger::create(7));  
  73.    
  74. gidsArray->addObject(CCInteger::create(11));  
  75.    
  76. gidsArray->addObject(CCInteger::create(15));  
  77.    
  78. gidsArray->addObject(CCInteger::create(4));  
  79.    
  80. gidsArray->addObject(CCInteger::create(8));  
  81.    
  82. gidsArray->addObject(CCInteger::create(12));  
  83.    
  84. gidsArray->addObject(CCInteger::create(16));  
  85.    
  86. bRet = true;  
  87.    
  88. while (0);  
  89.    
  90. return bRet;  
  91.    
  92. }  

看起来挺麻烦的,其实很简单,以顶点值总和作为数组索引,取得图素ID。我相信注释已经很详细了。

(小若:噗,我一般不看注释,就等着你解释。)

 

不过话说回来,我这个获取图素的方法有点笨拙,不过效果很不错,大家可以试试用其它方式来实现,总之,就是处理瓦片顶点值总和和图素ID的对应关系罢了。

 

5. 要运行了!

现在,我们运行我们的代码吧!点击屏幕,我们会看到瓦片的变化!

(小若:好棒!)

但是还没完呢,我们只修改了当前点击的瓦片,还要修改它附近的瓦片呢:

  1. bool HelloWorld::ccTouchBegan( CCTouch *pTouch, CCEvent *pEvent )  
  2.    
  3. {  
  4.    
  5. CCPoint tiledMapPos = getMapTiledPos(  
  6.    
  7. cloudMap,  
  8.    
  9. CCDirector::sharedDirector()->convertToGL(pTouch->getLocationInView()));  
  10.    
  11. CCLOG("TiledMapPos x = %f, y = %f", tiledMapPos.x, tiledMapPos.y);  
  12.    
  13. CCTMXLayer* cloudLayer = cloudMap->layerNamed("cloudLayer");  
  14.    
  15. /* 设置当前瓦片的数据值,以及设置旁白瓦片的数据值 */  
  16.    
  17. changeCloudTiled4(cloudLayer, tiledMapPos);  
  18.    
  19. changeCloudTiled8(cloudLayer, ccp(tiledMapPos.x + 1, tiledMapPos.y));  
  20.    
  21. changeCloudTiled1(cloudLayer, ccp(tiledMapPos.x, tiledMapPos.y + 1));  
  22.    
  23. changeCloudTiled2(cloudLayer, ccp(tiledMapPos.x + 1, tiledMapPos.y + 1));  
  24.    
  25. return true;  
  26.    
  27. }  
changeCloudTiled8 changeCloudTiled1 changeCloudTiled2 函数的处理和 changeCloudTiled4 函数是一样的,只是修改的顶点不一样,具体大家看代码:
  1. void HelloWorld::changeCloudTiled8( CCTMXLayer* layer, CCPoint pos )  
  2. {  
  3.    
  4. TiledFourCell* cell = getCellByTiledPos(pos);  
  5.    
  6. /* 数值8,在瓦片左下角 */  
  7.    
  8. cell->setiLeftBottom(8);  
  9.    
  10. /* 根据瓦片4个顶点之和设置地图瓦片的图片 */  
  11.    
  12. setGidByTotalNum(layer, pos, cell->getiTotalNum());  
  13.    
  14. }  
  15.    
  16. void HelloWorld::changeCloudTiled1( CCTMXLayer* layer, CCPoint pos )  
  17.    
  18. {  
  19.    
  20. TiledFourCell* cell = getCellByTiledPos(pos);  
  21.    
  22. /* 数值1,在瓦片右上角 */  
  23.    
  24. cell->setiRightTop(1);  
  25.    
  26. /* 根据瓦片4个顶点之和设置地图瓦片的图片 */  
  27.    
  28. setGidByTotalNum(layer, pos, cell->getiTotalNum());  
  29.    
  30. }  
  31.    
  32. void HelloWorld::changeCloudTiled2( CCTMXLayer* layer, CCPoint pos )  
  33.    
  34. {  
  35.    
  36. TiledFourCell* cell = getCellByTiledPos(pos);  
  37.    
  38. /* 数值2,在瓦片左上角 */  
  39.    
  40. cell->setiLeftTop(2);  
  41.    
  42. /* 根据瓦片4个顶点之和设置地图瓦片的图片 */  
  43.    
  44. setGidByTotalNum(layer, pos, cell->getiTotalNum());  
  45.    
  46. }  
OK ,再次运行项目,效果更明显了:

(小若:噗,有黑雾的感觉了!!但是为嘛有点不对劲?)

是的,我们的背景一开始就是白色的,没有迷雾的感觉,所以,我把图素图片修改了一下:

我把图素1变成黑色了,这样地图的初始状态就是黑色的。

(小若:对喔!地图是用图素1填满的。)

 

最后,我们再次运行项目,点击几下,战争迷雾终于出现了!

 

(小若:学得好累,终于结束了。)

虽然这个战争迷雾不算华丽,但是已经很不错了,可以帮助我们写出更有趣的游戏。战争迷雾的实现过程也许有点复杂,但其实它并不复杂,静下心思考一会,就能想通了。

好了,感谢大家阅读我的教程。

 

源码下载地址请查看第05章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值