先貼個圖:(精靈左下角,中間的那段不和諧的是所謂的牆,那白色字型的是菜單,是不是很醜。。)
在精靈移動的時候如果碰到了牆或者是什麼別的我們不想讓它過的物體,我們就要阻止它了。。這就是所謂的碰撞。。本質上來說,就是一個演算法問題。。我先貼下My Code,再來解釋吧。。
typedef enum{PASS=0,MAPLIMIT=1,WALL=2}COLLISIONTYPE;
typedef enum{DOWN=0,UP=3,LEFT=1,RIGHT=2}DIRECTIONS;COLLISIONTYPE HelloWorld::canPass(cocos2d::CCPoint position){if(position.x<=32||position.y<=32||position.x>=mapSize.width*mapTileSize.width-32||position.y>=mapSize.height*mapTileSize.height)return MAPLIMIT;CCPoint nextPostionForTileCoord=positionToTileCoord(position);int tileGIDUP=map->layerNamed("wall")->tileGIDAt(CCPoint((int)nextPostionForTileCoord.x,(int)(nextPostionForTileCoord.y-1.0)));int tileGIDDOWN=map->layerNamed("wall")->tileGIDAt(CCPoint((int)nextPostionForTileCoord.x,(int)(nextPostionForTileCoord.y+1.0)));int tileGIDRIGHT=map->layerNamed("wall")->tileGIDAt(CCPoint((int)(nextPostionForTileCoord.x+1.0),(int)nextPostionForTileCoord.y));int tileGIDLEFT=map->layerNamed("wall")->tileGIDAt(CCPoint((int)(nextPostionForTileCoord.x-1.0),(int)nextPostionForTileCoord.y));int tileGIDUPLEFT=map->layerNamed("wall")->tileGIDAt(CCPoint((int)(nextPostionForTileCoord.x-1),(int)(nextPostionForTileCoord.y-1.0)));int tileGIDUPRIGHT=map->layerNamed("wall")->tileGIDAt(CCPoint((int)(nextPostionForTileCoord.x+1),(int)(nextPostionForTileCoord.y-1.0)));int tileGIDDOWNRIGHT=map->layerNamed("wall")->tileGIDAt(CCPoint((int)(nextPostionForTileCoord.x+1),(int)(nextPostionForTileCoord.y+1)));int tileGIDDOWNLEFT=map->layerNamed("wall")->tileGIDAt(CCPoint((int)(nextPostionForTileCoord.x-1),(int)(nextPostionForTileCoord.y+1)));if(tileGIDUP||tileGIDDOWN||tileGIDRIGHT||tileGIDLEFT||tileGIDUPLEFT||tileGIDUPRIGHT||tileGIDDOWNRIGHT||tileGIDDOWNLEFT){return WALL;}return PASS;}
COLLISIONTYPE是我定義的精靈行走的類型,如果碰到了不能過的物體,那麼現在的狀態就是WALL,如果是因為地圖大小的限制,那麼就是MAPLIMIT,正常情況下是PASS。
碰撞監測肯定是隨時都發生的吧。這個肯定是毫無疑問的,那麼我們就可以把這個判斷加在update()方法中:
if(isWalking){COLLISIONTYPE pass_type=canPass(heroPosition);if(pass_type==WALL){heroSprite->stopAllActions();if(directionCurrent==UP)heroSprite->setPosition(CCPoint(heroPosition.x,heroPosition.y-0.1));else if (directionCurrent==DOWN){heroSprite->setPosition(CCPoint(heroPosition.x-0.1,heroPosition.y+0.5));}else if (directionCurrent==LEFT){heroSprite->setPosition(CCPoint(heroPosition.x+0.1,heroPosition.y));}elseheroSprite->setPosition(CCPoint(heroPosition.x-0.1,heroPosition.y));isWalking=false;}
這些變數估計不用解釋了吧。。看名字就懂了。。目前我們關注這句:
COLLISIONTYPE pass_type=canPass(heroPosition);
只要在update()中加這句就Ok了,就可以監測碰撞了。。。那麼碰撞之後,我們要幹嘛呢???毫無疑問,我們就要把精靈給停止下來。。停止精靈目前在進行的所以的action。所以就有了這句: heroSprite->stopAllActions();
先暫時不看接下來的代碼。我們返回到 COLLISIONTYPE HelloWorld::canPass(cocos2d::CCPoint position)
這個函數,我是怎麼判斷它什麼時候是WALL,什麼時候是MAPLIMIT的呢?
首先,我們知道精靈也是有大小的,(我這裡是寬高均為64像素的正方形),錨點肯定在(0.5,0.5)這個位置,即精靈的中心。
接下來,我們來判斷什麼時候會為MAPLIMIT,相信大家肯定知道了,只要精靈的左邊界和map的左邊界相同時;或:右邊界和右邊界相同;(上邊界和下邊界一樣的)。
if(position.x<=32||position.y<=32||position.x>=mapSize.width*mapTileSize.width-32||position.y>=mapSize.height*mapTileSize.height)return MAPLIMIT;
這段代碼應該沒問題了吧。。。注意這裡是32,因為就是左邊界到中心的距離呀。。(關於這個座標的問題,我還不是很懂,新手上路中。。),我猜測position是指錨點的位置(至少我這裡測試的是這樣的,但是網上和書上說的錨點和postion是不同的,position是左下角的位置)。。先放放這裡。。大家可以自己測試一下。。
然後到了和牆碰撞的這一部分了,我的演算法是:計算這個精靈的左上方,正上方,右上方,正右方,右下角,正下方,左下角,正左方這8個位置所在的那個Tile是不是在有牆的圖層中,大家肯定會調用
map->layerNamed("wall")->tileGIDAt(....)這個函數吧。。這就是判斷這個Tile是不是在“wall”圖層中,如果是,那麼返回一個int 類型的GID(唯一的吧。。反正不是0。。)如果不是那就返回0。這裡面的參數是CCPoint類型的,而且還是TileMap的座標點,而我們一般用的都是cocos2d-x的座標(和opengl的座標一樣),那麼這就涉及到兩種座標系的轉換:
CCPoint HelloWorld::positionToTileCoord(CCPoint position){float x=(float)(position.x)/mapTileSize.width;float y=(float)(mapSize.height*mapTileSize.height-position.y)/mapTileSize.height;return ccp(x,y);}
這個很簡單吧。。我就不解釋了。。
在canPass這個函數中的那個變數nextPostionForTileCoord,我名字取的不是很好,因為本來我的方法不是這樣的考慮的,變數名也沒改過來,大家將就著看吧。。回到正題:只要有一個位置是在屬於"wall"圖層的Tile中on個,那麼我們就停止精靈的所有action。
大家肯定還會對
if(directionCurrent==UP)heroSprite->setPosition(CCPoint(heroPosition.x,heroPosition.y-0.1));
類似的代碼有點疑問。其實這也是我調試中出現了問題才加上去的。。大家仔細想想我們之前的做法是不是有什麼漏洞??當有監測到有碰撞時,精靈就馬上停了下來,而且邊緣和與之碰撞的物體相接觸,融合,即肯定存在一個我們之前的那8個角中的一個在物體上(仔細考慮考慮)。如果我們再想讓精靈運動起來,那麼同時也會開始監測是不是有碰撞產生,(插一句:在物體沒有運動的時候不會,我們添加了一個isWalking的變數,記錄下物體的運動情況),而那個與物體接觸的角就會使判斷一直為:Wall,我們的精靈就一直不能移動。為瞭解決這個問題,我在他們運動的方向上減去了0.1或者加了0.1,大家看代碼應該能懂什麼時候該減,什麼時候該加。。還是舉個例子吧:如果精靈向上(cocos2d-x的y的正方向,TileMap的負方向)運動,那麼肯定是和上面的某個物體碰撞,所以我們的y值減去了一個0.1,而x就可以保持不變,取0.1是為了讓玩家看不出來變化。
精靈碰撞的問題就可以解決了,效果
這是我自己編的一個遊戲的一部分,在此將其記錄下來,和大家分享一下。大家有什麼問題或意見可以向我提出來,一起交流交流。後面繼續寫我遊戲的其他一部分。目前只是實現了情境的移動,精靈的移動和碰撞監測。開始我的遊戲之旅。