注意:
當一個節點有一個子節點的時候,如果移動父節點,子節點也會跟着做相應的移動變化,只要被添加到父節點中,子節點就被綁定了,所以子節點的位置,坐標就會被動地變化。
當一個節點有一個子節點的時候,如果通過setPosition方法設置子節點的坐標,那么,這時候其實是在以父節點左下角為原點的坐標系中,x軸最長是父節點的寬度,y軸最長是父節點的高度。后面如果有需要可以通過一些坐標的轉換的方法轉化為世界坐標/UI坐標。
例如,jewel1是hero的子節點,屏幕分辨率960*640,hero大小200*250
Point p1, p2,p3;
auto hero = Sprite::create("s_1.png");
hero->setPosition(visibleSize.width / 2, visibleSize.height / 2);
p1=hero->getPosition();
log("p1=(%f,%f)",p1.x,p1.y);
addChild(hero);
auto jewel1 = Sprite::create("jewel1.png");
jewel1->setAnchorPoint(Vec2(1, 0));
jewel1->setPosition(hero->getContentSize().width, 0);
p2 = jewel1->getPosition();
log("p2=(%f,%f)", p2.x, p2.y);
p3 = hero->convertToWorldSpace(jewel1->getPosition());
log("p3=(%f,%f)", p3.x, p3.y);
hero->addChild(jewel1);
輸出結果
p1=(480.000000,320.000000)
p2=(200.000000,0.000000)
p3=(580.000000,195.000000)
一.UI坐標系
在進行iOS或者Android界面開發時,它的坐標系規則如下圖所示:
- 原點坐標(x=0, y=0)位於左上角;
- X軸從屏幕最左邊開始,由左向右漸增;
- Y軸坐標從屏幕最上方開始,由上向下漸增
二.Cocos2d-x坐標系
Cocos2d-x坐標系是這里的重點,也是我們開發時考慮的最多的。由於Cocos2d-x是基於OpenGL和OpenGL ES的。該坐標系的規則如下:
- 原點坐標(x=0, y=0)位於左下角;
- X軸從屏幕最左邊開始,由左向右漸增;
- Y軸從屏幕最下方開始,由下向上漸增;
在Cocos2d-x中的場景,就是使用的該坐標系。
三.世界坐標系
世界坐標系也叫絕對坐標系,是游戲開發中建立的概念。它建立了描述其它坐標系所需要的參考標准。我們都可以使用世界坐標系來描述其它坐標系的位置。
Cocos2d-x中元素是有父子關系的層次結構,通過Node
設置位置使用的是相對其父節點的本地坐標系,而非世界坐標系,最后在繪制屏幕的時候,Cocos2d-x會把這些元素的本地節點坐標映射成世界坐標系坐標。世界坐標系和OpenGL坐標系方向一致,原點在屏幕左下角,X軸向右,Y軸向上。
四.節點坐標系
節點坐標系也叫相對坐標系,它是與特定節點相關聯的坐標系。每個節點都有獨立的坐標系,當節點移動或改變方向時,和該節點關聯的坐標系(它的子節點)將隨之移動或改變方向。比如一個Layer上面有10個精靈,當移動這個Layer的時候,這些精靈也會跟着一起移動的。
在Node
節點類中,我們可以對節點進行位置的操作,而這些設置位置使用的就是父節點的節點坐標系。它和OpenGL坐標系方向一致,原點在屏幕左下角,X軸向右,Y軸向上。有的時候,我們需要將節點坐標轉換成世界坐標,或者將世界坐標轉換成節點坐標。在Node
節點類中,就提供了對應的轉換函數,下面我們就使用一下這些函數,加深對Cocos2d-x坐標系、世界坐標系和節點坐標系的理解。
轉換API
在Cocos2d-x中提供了以下的API用來進行坐標轉換。
/** * 將世界坐標轉換成節點坐標,忽略錨點的影響;結果是以點為單位。 */ Vec2 convertToNodeSpace(const Vec2& worldPoint) const; /** * 將節點坐標轉換成世界坐標,忽略錨點的影響;結果是以點為單位。 */ Vec2 convertToWorldSpace(const Vec2& nodePoint) const; /** * 將世界坐標轉換成節點坐標;結果是以點為單位。 * 會考慮到錨點的影響 */ Vec2 convertToNodeSpaceAR(const Vec2& worldPoint) const; /** * 將節點坐標轉換成世界坐標;結果是以點為單位。 * 會考慮到錨點的影響。 */ Vec2 convertToWorldSpaceAR(const Vec2& nodePoint) const; /** * 將Touch對應的點轉換成節點坐標,忽略錨點的影響。 */ Vec2 convertTouchToNodeSpace(Touch * touch) const; /** * 將Touch對應的點轉換成節點坐標,考慮錨點的影響。 */ Vec2 convertTouchToNodeSpaceAR(Touch * touch) const;
好了,世界坐標系轉節點坐標系,節點坐標系轉世界坐標系,就這么幾個函數就能搞定了,剩下的就是實際的應用了。對了,在實際中,一定要考慮到錨點的影響,可能你得到的結果,就是因為錨點的影響,而完全不同的。
坐標系變換原理
上面總結了坐標系之間轉換的一些API函數,下面就來看看它們之間到底是如何轉換的。看了網上很多人的博客,寫的轉換原理,寫的都不錯,就是看的雲里霧里的,很多人都配上了坐標圖,搞笑的是那些坐標圖都是“一副”,也不知道誰抄襲的誰的。
下面就做一些簡單的原理,沒有過的圖來說明,就是一些簡短的文字,按照這些文字說明,你肯定能看的懂的。
1.convertToNodeSpace
Vec2 newPosition = node1->convertToNodeSpace(node2->getPosition());
將node2的位置坐標轉換成相對於node1左下角頂點的坐標。轉換方法:node1和node2位置不變,將坐標軸原點設置為node1的左下角頂點,重新計算node2->getPosition()
這個點的坐標即為newPosition。
當調用以下代碼時,返回的是相對於其父節點的節點坐標,當然了,以下代碼的實際用處並不大。
Vec2 newPosition = node1->convertToNodeSpace(node1->getPosition());
2.convertToNodeSpaceAR
Vec2 newPosition=node1->convertToNodeSpaceAR(node2->getPosition());
將node2的位置坐標轉換成相對於node1錨點的坐標。轉換方法:node1和node2位置不變,將坐標軸原點設置為node1的錨點,重新計算node2->getPosition()
這個點的坐標即為newPosition。
3.convertToWorldSpace
Vec2 newPosition=node1->convertToWorldSpace(node2->getPosition());
將node2的位置坐標轉換成世界坐標。轉換方法:node1的位置不變,世界坐標的坐標軸也不變,以node1的左下角頂點再建立一個坐標系(其實就是本地坐標),將node2->getPosition()
這個點設置到新建的坐標系中,以原來的世界坐標系為參考,重新計算node2->getPosition()
這個點的坐標即為newPosition。
4.convertToWorldSpaceAR
Vec2 newPosition=node1->convertToWorldSpaceAR(node2->getPosition());
將node2的位置坐標轉換成世界坐標。轉換方法:node1的位置不變,世界坐標的坐標軸也不變,以node1的錨點再建立一個坐標系,將node2->getPosition()
這個點設置到新建的坐標系中,以原來的世界坐標系為參考,重新計算node2->getPosition()
這個點的坐標即為newPosition。
另一個例子
首先我們添加兩個測試精靈(寬:27,高:40)到場景里面:
Sprite *sprite1 = Sprite::create("player.png");
sprite1->setPosition(Vec2(20, 40));
sprite1->setAnchorPoint(Vec2(0, 0));
this->addChild(sprite1);
Sprite *sprite2 = Sprite::create("player.png");
sprite2->setPosition(Vec2(-15, -30));
sprite2->setAnchorPoint(Vec2(1, 1));
this->addChild(sprite2);
然后調試,在場景中大概是下圖這樣顯示(以左下角為坐標原點,從左到右為x方向,從下到上為y方向,廢話了:)):
在cocos2d-x中,每個精靈都有一個錨點,以后對精靈的操作(比如旋轉)都會圍繞錨點進行,我們暫且可以看作是精靈的中心位置,一般來說有每個方向有三種可能的值:0,0.5,1。上圖中紅色圓點即為各自的錨點,sprite1 錨點為 (0,0) 左下角,sprite2錨點為(1,1)在右上角。
現在我們來看看坐標系轉換,同樣地,我們先寫點測試代碼:
Point p1 = sprite2->convertToNodeSpace(sprite1->getPosition());
Point p2 = sprite2->convertToWorldSpace(sprite1->getPosition());
Point p3 = sprite2->convertToNodeSpaceAR(sprite1->getPosition());
Point p4 = sprite2->convertToWorldSpaceAR(sprite1->getPosition());
Log("p1:%f,%f", p1.x, p1.y);
Log("p2:%f,%f", p2.x, p2.y);
Log("p3:%f,%f", p3.x, p3.y);
Log("p4:%f,%f", p4.x, p4.y);
由於cocos2d-x的坐標系(本地坐標系)是以左下角為坐標原點的,所以 sprite1和sprite2的坐標原點在上圖的位置分別是(20,40)、(-42,-70),那么很明顯的:
p1就是sprite1錨點相對於sprite2原點來說在sprite2坐標系中的位置,經過對比上圖,我們可以得到(20-(-42),40-(-70))即(62,110)
p2就是sprite1錨點相對於sprite2原點來說在上圖坐標系中的位置,這樣我們可以計算出sprite1在sprite2坐標系中的位置:(20+(-42),40+(-70)),即(-22,-30)
p3就是sprite1錨點相對於sprite2錨點來說在sprite2坐標系中的位置,也就是(20-(-15),40-(-30)),即(35,70)
p4就是sprite1錨點相對於sprite2錨點來說在上圖坐標系中的位置,也就是(20+(-15),40+(-30)),即(5,10)
現在我們可以知道,計算方法都是用sprite1的坐標去加減sprite2的坐標,針對本地坐標系就用減法,針對世界坐標系就用加法。