本文主要對 cocos2d官網上的内容進行了補充注解。http://www.cocos2d-x.org/docs/manual/framework/native/graphic/coordinate-system/zh
Cocos2d坐标系
不同坐标系簡介
笛卡爾坐标系
你可能上學的時候就已經知道“笛卡爾坐标系”了,它在幾何課本裡經常用到。如果你已經忘得差不多了,下面這些圖檔可以很快喚起你的記憶:
在移動遊戲開發過程中,有三種類型的坐标系你可能遇到:
UI坐标系
iOS/Android/Windows SDK中的通用UI坐标系:
- 起點坐标(x=0, y=0)位于左上角
- X軸從螢幕最左邊開始,由左向右漸增
- Y軸坐标從螢幕最上方開始,由上向下漸增
詳見下圖
Direct3D坐标系
DirectX 使用Left-handed Cartesian Coordinate
OpenGL和Cocos2d坐标系
Cocos2d-x/-html5/-iphone使用的坐标系和OpenGL的坐标系一樣,名為“Right-handed Cartesian Coordinate Syste”。
在2D世界中,我們僅會使用x軸和y軸。是以在你的cocos2d遊戲中:
起點坐标(x=0, y=0)位于左下角,這意味着螢幕位于
X軸從螢幕最左邊開始,由左向右漸增
Y軸坐标從螢幕最下方開始,由下向上漸增
下面這張圖檔有助于更好的闡述Cocos2d-x坐标:
一定要注意:通用UI坐标系和DirectX坐/attachments/1559/parent.jpeg)标系是不一樣的。
Parent and Childrens
由于每個類都繼承自CCNode(cocos2d-x的最頂層類),是以每個類都會預設有anchorPoint屬性。 當我們在一個位置畫一個的對象的時候,cocos2d-x會合并屬性位置和anchorPoint。當然,當旋轉一個對象時,cocos2d-x會圍繞繞anchorPoint旋轉的。
我們建立一個灰色父對象和一個藍色子對象。設定父對象位置是ccp(100,100),子對象的anchor point位于圓心。
CCSprite* parent = CCSprite::create("parent.png");
parent->setAnchorPoint(ccp(0, 0));// Anchor Point
parent->setPosition(ccp(100, 100));
parent->setAnchorPoint(ccp(0, 0));
addChild(parent);
//create child
CCSprite* child = CCSprite::create("child.png");
child->setAnchorPoint(ccp(0.5, 0.5));
child->setPosition(ccp(0, 0));
parent->addChild(child);//add child sprite into parent sprite.
由于我們設定子對象的位置是ccp(0,0),父對象位置是ccp(100,100)。是以,子對象位置是:
看到這裡,我們對 錨點還是不太清晰。畢竟文檔隻是一片介紹性的文檔,還需要消化一番。對于一塊圖像而已,cos2d總是用一塊矩形來描述它的,既然如此,那麼必然有四個頂點,左下(0,0),右下(1,0),左上(0,1),右上(1,1),如下圖所示:
按照了解來看,錨點并不是一個絕對值,隻是一個相對值,如果選擇(1,1) 就是選擇右上角作為錨點,如果選擇(0.5,0.5)則表示選擇圖檔的中心作為錨點。那麼錨點有什麼用呢?此處不太好描述,我們用例子來說明,還是上面的代碼,如下:
CCSprite* parent = CCSprite::create("parent.png");
parent->setAnchorPoint(ccp(1, 0));// Anchor Point
parent->setPosition(ccp(100, 100));
CCSprite* parent = CCSprite::create("parent.png");
parent->setAnchorPoint(ccp(0, 0));// Anchor Point
parent->setPosition(ccp(100, 100));
CCSprite* parent = CCSprite::create("parent.png");
parent->setAnchorPoint(ccp(0, 1));// Anchor Point
parent->setPosition(ccp(100, 100));
CCSprite* parent = CCSprite::create("parent.png");
parent->setAnchorPoint(ccp(1, 1));// Anchor Point
parent->setPosition(ccp(100, 100));
上面四種代碼都是從(100,100)開始畫圖,不同的隻是錨點,畫出的圖像對于下圖中的四種矩形:
如此說來,通俗的說,錨點就是決定圖像如何擺放,setPosition(100,100) 隻是告訴cos2d我們要畫一個圖像,至于怎麼畫,畫的位置如何,完全是由錨點來決定的。可以想象一下,如果錨點是(.0.5,0.5),那畫出的矩形正好以上圖中的(100,100)為中心,将4個矩形串起來。
再看下面官方給出的兩個例子,更加證明了這個道理。
錨點
作為例子,下面這個精靈有的錨點位于 ccp(0,0),位置位于ccp(0,0)。
這個矩形精靈将被放到它的父對象(layer)的左下角。
示例:
// create sprite
CCSprite* sprite = CCSprite::create("bottomleft.png");
sprite->setAnchorPoint(ccp(0, 0));// Anchor Point
sprite->setPosition(ccp(0,0));
addChild(sprite);
在另一個例子中,我們會擺放一個坐标為ccp(0.5,0.5)的anchorPoint,以便您更好的了解錨點的相對值。
// create sprite
CCSprite* sprite = CCSprite::create("center.png");
sprite->setAnchorPoint(ccp(0.5, 0.5));// Anchor Point
sprite->setPosition(ccp(0,0));
addChild(sprite);
正如你從圖中看出的,錨點取的不是像素值,此值的X和Y是相對于此節點的大小的。
擷取可視區域大小, 擷取可視區域起點 vs 擷取視窗大小
- getVisibleSize
- getVisibleOrigin
- getWinSize
VisibleSize(可視區域大小)會傳回此點的OpenGL視圖的可視區域大小。如果沒有調用
CCEGLView::setDesignResolutionSize()
的話,此值等于getWinSize的大小。 getVisibleOrigin(擷取可視區域起點)會傳回此點的OpenGL視圖的可視區域起點。請移步Multi resolution support檢視詳情。
如何轉換坐标
convertToNodeSpace:
舉例,convertToNodeSpace用于tile-based的遊戲,即有一個大地圖。convertToNodeSpace會轉換openGL觸摸點轉成.tmx 地圖或者其他近似的坐标。
例子:
下面的圖檔會展現,node1的錨點(0,0),node2的錨點是(1,1)。
我們會調用CCPoint point = node1->convertToNodeSpace(node2->getPosition()); 轉換node2的螢幕坐标為node1的位置。結果是,node2的位置是(-25,-60).
說到這裡,其實文檔有遺忘了一個概念,那就是“本地坐标”的概念。本地坐标永遠都是指矩形的左下(0,0)點,這個概念很重要。上圖中node1的錨點是(0,0),正好與node1的本地坐标重合,我們假設node1的長是 10,高是4,此時node1的本地坐标在上圖中的絕對坐标上是(20,40)。當然,此時node2的錨點是(1,1),其中上圖中的絕對坐标是(-5,-20)。(-5,-20)到點(20,40)之間的偏移向量是(25,60),是以反過來,以(20,40)點而言,node2(-5,-20)到它的向量偏移就是(-25,-60)。另外,絕對坐标也稱世界坐标。
convertToWorldSpace:
convertToWorldSpace(常量 CCPoint& nodePoint) 轉換node坐标為SCREEN坐标。convertToWorldSpace會經常傳回你的精靈的SCREEN位置,如果你想捕獲精靈的taps而且需要移動/縮放layer的時候,這可能非常有幫助。
上面的代碼會轉換node2坐标為node2在螢幕上對應的坐标。
這個地方翻譯顯得有點意猶未盡啊,最後一句應該是"上面的代碼會轉換node2坐标為node2在螢幕上對應于node1本地坐标的坐标"。哈哈,node2的錨點絕對坐标是(-5,-20),上圖說過,node1的本地坐标是它坐下頂點的坐标,其絕對坐标是(20,40),顯然如果以(20,40)作為相對的原點(0,0),那麼(-5,-20)的相對坐标就是(15,20)。
convertToWorldSpaceAR
convertToWorldSpaceAR傳回相對錨點的位置:是以如果你的場景 – 根layer有一個錨點位于ccp(0.5f, 0.5f)。- 預設的,convertToNodeSpaceAR應傳回相對于螢幕中心的位置。
convertToNodeSpaceAR – 和convertToWorldSpaceAR是一樣的邏輯。
示例代碼:
CCSprite *sprite1 = CCSprite::create("CloseNormal.png");
sprite1->setPosition(ccp(20,40));
sprite1->setAnchorPoint(ccp(0,0));
this->addChild(sprite1);
CCSprite *sprite2 = CCSprite::cteate("CloseNormal.png");
sprite2->setPosition(ccp(-5,-20));
sprite2->setAnchorPoint(ccp(1,1));
this->addChild(sprite2);
CCPoint point1 = sprite1->convertToNodeSpace(sprite2->getPosition());
CCPoint point2 = sprite1->convertToWorldSpace(sprite2->getPosition());
CCPoint point3 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());
CCPoint point4 = sprite1->convertToWorldSpaceAR(sprite2->getPosition());
CCLog("position = (%f,%f)",point1.x,point1.y);
CCLog("position = (%f,%f)",point2.x,point2.y);
CCLog("position = (%f,%f)",point3.x,point3.y);
CCLog("position = (%f,%f)",point4.x,point4.y);
結果:
position = (-25.000000,-60.000000)
position = (15.000000,20.000000)
position = (-25.000000,-60.000000)
position = (15.000000,20.000000)
上面例子中的兩組結果是一樣的,這樣不足以說明後面那兩個函數convertToNodeSpaceAR converToWorldSpaceAR的作用。其原因在于,例子中node1的錨點是(0,0),正好和它的本地坐标(左下點)是重複的。如果我們選擇node1錨點是(0.5,0.5),再來一次。
這裡在将sprite1的錨點設定成(0.5,0.5),對convertToNodeSpaceAR和convertToWorldSpaceAR進行了進一步的測試
sprite1->setAnchorPoint(ccp(0.5,0.5));
sprite1->setPosition(ccp(20,40));
CCPoint point5 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());
CCPoint point6 = sprite1->convertToWorldSpaceAR(sprite2->getPosition());
CCLog("position = (%f,%f)",point5.x,point5.y);
CCLog("position = (%f,%f)",point6.x,point5.y);
運算結果:
position = (-25.000000,-60.000000)
position = (15.000000,20.000000)
分析:重置的sprite1的坐标為(20,40),錨點為(0.5,0.5)是以對于convertToNodeSpaceAR和convertToWorldSpaceAR這兩個方法的坐标系為原點(20,40),是以用convertToNodeSpaceAR轉化之後的坐标為(-25,-60)用convertToWorldSpaceAR化之後的坐标為(15,20),和運算結果一樣。
有人會問,怎麼還是一樣啊,不是變化了node1的錨點為(0.5,0.5)了嗎 ? 那是因為,我們沒有給出下面兩個資料的值,再看下面的代碼:
CCPoint point1 = sprite1->convertToNodeSpace(sprite2->getPosition());
CCPoint point2 = sprite1->convertToWorldSpace(sprite2->getPosition());
上圖說過,node1的長是10,高是4,現在他的錨點的絕對坐标是(20,40),而且位于node1的中心,那麼可以推出node1的本地坐标是(15,38),即node1的坐下點的絕對坐标是(15,38)。那麼,上面函數的列印結果應該是:
(-20,-58)
(10,18)。
分析,convertToNodeSpace 是要計算絕對坐标(15,38)與絕對坐标(-5,-20)之間的偏移量,就是(-20,-58)。
convertToWorldSpace就是以node1的本地坐标(15,38)為原點,計算(-5,-20)的相對坐标,就是(10,18)