原文地址:http://www.benmutou.com/blog/archives/485
经过这么多铺垫,我们要来正式编写实现迷雾效果的代码了。
(小若:快点开始写,别唠叨了!)
笨木头花心贡献,啥?花心?不呢,是用心~
正文:
1. 复习一下
还记得我们的理论基础吗?点击某个瓦片后,修改瓦片的某个顶点值,同时修改附近瓦片的顶点值:
1)当前瓦片右下角的顶点值设为4
2)当前瓦片右边的瓦片的左下角顶点值设为8
3)当前瓦片下方的瓦片的右上角顶点值设为1
4)当前瓦片右下方的瓦片的左上角顶点值设为2
(小若:好混乱!但是我看懂了)
我们先来实现第一步,修改当然点击的瓦片的顶点值。看以下代码:
01 | bool HelloWorld::ccTouchBegan( CCTouch *pTouch, CCEvent *pEvent ) |
04 | CCPoint tiledMapPos = getMapTiledPos( |
08 | CCDirector::sharedDirector()->convertToGL(pTouch->getLocationInView())); |
10 | CCTMXLayer* cloudLayer = cloudMap->layerNamed( "cloudLayer" ); |
12 | changeCloudTiled4(cloudLayer, tiledMapPos); |
我们在触屏事件里添加了一步操作——切换当前点击瓦片的顶点值。
2. 当前点中的瓦片,修改瓦片右下角的顶点值为4
cloudLayer是cloudMap.txm地图的第一个图层,我把它命名为“cloudLayer”。
噢,对了,我们应该先介绍一个类:TiledFourCell。TiledFourCell是代表瓦片4个顶点值数据的对象,很简单的:
01 | class TiledFourCell : public CCNode { |
04 | CREATE_FUNC(TiledFourCell); |
08 | void setiLeftTop( int _iLeftTop); |
12 | void setiLeftBottom( int _iLeftBottom); |
16 | void setiRightTop( int _iRightTop); |
20 | void setiRightBottom( int _iRightBottom); |
22 | int getiRightBottom(); |
总共四个属性分别代表瓦片的四个顶点值,以及一个用于获取顶点值总和的函数。
(小若:为什么一个瓦片又分成4个顶点?)
理论部分已经说过了,一个瓦片用哪个图素取决于这个瓦片四个顶点值的总和。话说,为什么我非得回答旁白的问题!
接着,我们看看changeCloudTiled4函数:
01 | void HelloWorld::changeCloudTiled4( CCTMXLayer* layer, CCPoint pos ) |
04 | TiledFourCell* cell = getCellByTiledPos(pos); |
08 | cell->setiRightBottom(4); |
12 | setGidByTotalNum(layer, pos, cell->getiTotalNum()); |
这个函数也不难,首先取得瓦片的顶点对象TiledFourCell,至于怎么取得的,我们先不管。
(小若:啊!我最恨吊胃口了。)
然后,我们把瓦片右下角的顶点值设为4。
最后,最关键的一步,这有点复杂,大家打起精神来。根据瓦片4个顶点之和设置瓦片的图片,大家回忆一下最开始介绍的理论,我们取得瓦片顶点值之和,用这个值去寻找对应的瓦片图素ID,然后用这个图素替换瓦片的当前图素。
还是来看看setGidByTotalNum函数吧:
01 | void HelloWorld::setGidByTotalNum( CCTMXLayer* layer, CCPoint pos, int iTotalNum ) |
04 | CCSize mapSize = cloudMap->getMapSize(); |
08 | if (pos.x < 0 || pos. y < 0 |
10 | || pos.x >= mapSize.width || pos.y >= mapSize.height) { |
18 | CCInteger* gidInt = (CCInteger*)gidsArray->objectAtIndex(iTotalNum); |
22 | layer->setTileGID(gidInt->getValue(), pos); |
CCInteger* gidInt = (CCInteger*)gidsArray->objectAtIndex(iTotalNum);就是为了取得顶点值之和对应的图素ID。然后用setTileGID函数替换在pos坐标上的瓦片的图素。我们先忽略图素ID倒底是怎么取得的。
(小若:好乱好乱,我不学了!)
别激动,是有点乱,我们来理一下:
1)点击屏幕,获得瓦片坐标tiledMapPos
2)找到瓦片的顶点对象:TiledFourCell* cell = getCellByTiledPos(pos);
3)修改顶点对象的右下角顶点值为4:cell->setiRightBottom(4);
4)取得顶点对象四个顶点值的和iTotalNum:cell->getiTotalNum()
5)根据iTotalNum取得瓦片图素ID:
CCInteger* gidInt = (CCInteger*)gidsArray->objectAtIndex(iTotalNum);
6)用新的图素替换瓦片的图片:layer->setTileGID(gidInt->getValue(), pos);
(小若:那,那算了,我继续听。)
3. 看看如何取得瓦片的顶点对象
好,整个过程大家都清晰,那么,大家去泡杯咖啡吧,现在我们来慢慢看看getCellByTiledPos函数是怎么回事:
01 | TiledFourCell* HelloWorld::getCellByTiledPos( CCPoint pos ) |
04 | CCSize mapSize = cloudMap->getMapSize(); |
08 | int index = mapSize.width * pos.x + pos.y; |
12 | const char * keyCh = CCString::createWithFormat( "%d" , index)->getCString(); |
14 | CCObject* obj = tiledCellsDict->objectForKey(keyCh); |
16 | TiledFourCell* cell = NULL; |
22 | cell = TiledFourCell::create(); |
24 | tiledCellsDict->setObject(cell, keyCh); |
30 | cell = (TiledFourCell*)obj; |
哇,这看起来有点复杂,不过没关系,我们先不管,我们来看看我给HelloWordScene新增的成员变量:
1 | CCDictionary* tiledCellsDict; |
这已经很清楚了,我们利用瓦片坐标来索引瓦片的顶点对象。不过这个坐标有点特殊,它不是二维坐标,而是一维坐标。还记得我们的地图是10X10的规则吧,那么它总共有100个瓦片,一维坐标就是指瓦片在这100个中的索引。
二维坐标转换为一维坐标也很简单,公式如下:
2 | int index = mapSize.width * pos.x + pos.y; |
用文字描述为:地图横向瓦片的数量乘以x坐标 加上 y坐标
接着就是从字典tiledCellsDict中取值了,很简单,不多说了。
(小若:才怪啊,我不懂吖。)
那简单地说一下吧,先用瓦片的一维坐标从字典中查值,因为字典的key必须为字符串,所以我们要把整型的一维坐标转换为字符串格式:
1 | const char * keyCh = CCString::createWithFormat( "%d" , index)->getCString(); |
如果该瓦片还没有顶点对象,就创建一个,并且添加到字典里。够详细了,旁白别再吐槽了。
(小若:噗,我就要吐槽。)
4. 如何根据瓦片顶点总和获取图素ID
最后了,大家坚持住。还记得下面的代码吗?
3 | CCInteger* gidInt = (CCInteger*)gidsArray->objectAtIndex(iTotalNum); |
我们来解释一下图素ID是如何取得的,我又为HelloWorldScene添加了一个成员变量:
先看看在init函数里是怎么对它初始化的吧:
01 | bool HelloWorld::init() |
10 | CCLayerColor::initWithColor(ccc4(255, 255, 255, 255)); |
12 | cloudMap = CCTMXTiledMap::create( "cloudMap2.tmx" ); |
14 | this ->addChild(cloudMap); |
16 | this ->setTouchEnabled( true ); |
18 | gidsArray = CCArray::createWithCapacity(16); |
20 | tiledCellsDict = CCDictionary::create(); |
24 | tiledCellsDict->retain(); |
54 | gidsArray->addObject(CCInteger::create(1)); |
56 | gidsArray->addObject(CCInteger::create(5)); |
58 | gidsArray->addObject(CCInteger::create(9)); |
60 | gidsArray->addObject(CCInteger::create(13)); |
62 | gidsArray->addObject(CCInteger::create(2)); |
64 | gidsArray->addObject(CCInteger::create(6)); |
66 | gidsArray->addObject(CCInteger::create(10)); |
68 | gidsArray->addObject(CCInteger::create(14)); |
70 | gidsArray->addObject(CCInteger::create(3)); |
72 | gidsArray->addObject(CCInteger::create(7)); |
74 | gidsArray->addObject(CCInteger::create(11)); |
76 | gidsArray->addObject(CCInteger::create(15)); |
78 | gidsArray->addObject(CCInteger::create(4)); |
80 | gidsArray->addObject(CCInteger::create(8)); |
82 | gidsArray->addObject(CCInteger::create(12)); |
84 | gidsArray->addObject(CCInteger::create(16)); |
看起来挺麻烦的,其实很简单,以顶点值总和作为数组索引,取得图素ID。我相信注释已经很详细了。
(小若:噗,我一般不看注释,就等着你解释。)
不过话说回来,我这个获取图素的方法有点笨拙,不过效果很不错,大家可以试试用其它方式来实现,总之,就是处理瓦片顶点值总和和图素ID的对应关系罢了。
5. 要运行了!
现在,我们运行我们的代码吧!点击屏幕,我们会看到瓦片的变化!
![](http://benmutou.com/blog/wp-content/uploads/2013/04/blackCloud4/1.jpg)
(小若:好棒!)
但是还没完呢,我们只修改了当前点击的瓦片,还要修改它附近的瓦片呢:
01 | bool HelloWorld::ccTouchBegan( CCTouch *pTouch, CCEvent *pEvent ) |
05 | CCPoint tiledMapPos = getMapTiledPos( |
09 | CCDirector::sharedDirector()->convertToGL(pTouch->getLocationInView())); |
11 | CCLOG( "TiledMapPos x = %f, y = %f" , tiledMapPos.x, tiledMapPos.y); |
13 | CCTMXLayer* cloudLayer = cloudMap->layerNamed( "cloudLayer" ); |
17 | changeCloudTiled4(cloudLayer, tiledMapPos); |
19 | changeCloudTiled8(cloudLayer, ccp(tiledMapPos.x + 1, tiledMapPos.y)); |
21 | changeCloudTiled1(cloudLayer, ccp(tiledMapPos.x, tiledMapPos.y + 1)); |
23 | changeCloudTiled2(cloudLayer, ccp(tiledMapPos.x + 1, tiledMapPos.y + 1)); |
changeCloudTiled8、changeCloudTiled1、changeCloudTiled2函数的处理和changeCloudTiled4函数是一样的,只是修改的顶点不一样,具体大家看代码:
01 | void HelloWorld::changeCloudTiled8( CCTMXLayer* layer, CCPoint pos ) |
04 | TiledFourCell* cell = getCellByTiledPos(pos); |
08 | cell->setiLeftBottom(8); |
12 | setGidByTotalNum(layer, pos, cell->getiTotalNum()); |
16 | void HelloWorld::changeCloudTiled1( CCTMXLayer* layer, CCPoint pos ) |
20 | TiledFourCell* cell = getCellByTiledPos(pos); |
24 | cell->setiRightTop(1); |
28 | setGidByTotalNum(layer, pos, cell->getiTotalNum()); |
32 | void HelloWorld::changeCloudTiled2( CCTMXLayer* layer, CCPoint pos ) |
36 | TiledFourCell* cell = getCellByTiledPos(pos); |
44 | setGidByTotalNum(layer, pos, cell->getiTotalNum()); |
OK,再次运行项目,效果更明显了:
![](http://benmutou.com/blog/wp-content/uploads/2013/04/blackCloud4/2.jpg)
(小若:噗,有黑雾的感觉了!!但是为嘛有点不对劲?)
是的,我们的背景一开始就是白色的,没有迷雾的感觉,所以,我把图素图片修改了一下:
![](http://benmutou.com/blog/wp-content/uploads/2013/04/blackCloud4/3.png)
我把图素1变成黑色了,这样地图的初始状态就是黑色的。
(小若:对喔!地图是用图素1填满的。)
最后,我们再次运行项目,点击几下,战争迷雾终于出现了!
![](http://benmutou.com/blog/wp-content/uploads/2013/04/blackCloud4/4.jpg)
(小若:学得好累,终于结束了。)
虽然这个战争迷雾不算华丽,但是已经很不错了,可以帮助我们写出更有趣的游戏。战争迷雾的实现过程也许有点复杂,但其实它并不复杂,静下心思考一会,就能想通了。
好了,感谢大家阅读我的教程。