1. 程式人生 > >[cocos2dx]座標轉換

[cocos2dx]座標轉換

在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;

世界座標系轉節點座標系,節點座標系轉世界座標系,就這麼幾個函式就能搞定了,剩下的就是實際的應用了。對了,在實際中,一定要考慮到錨點的影響,可能你得到的結果,就是因為錨點的影響,而完全不同的。

convertToNodeSpace、convertToWorldSpace,都是以參照物的左下角作為座標點原點,向右和向上建立座標系。
------------------------------convertToNodeSpace,獲得的座標相對於參照物的座標。
------------------------------convertToWorldSpace,獲得的座標是參照物所在的座標系中的座標,即轉換到參照物的世界座標。

convertToNodeSpaceAR、convertToWorldSpaceAR,是以參照物的錨點為原點,向右、向上建立座標系。
------------------------------convertToNodeSpaceAR,獲得的座標相對於參照物的座標(此時座標系的原點在參照物的錨點)。
------------------------------convertToWorldSpaceAR,獲得的座標是參照物所在的座標系中的座標,即轉換到參照物的世界座標(此時座標系的原點在參照物的錨點)。
這樣理解比較簡單寫,有AR的是以參照物的錨點作為新建座標系的原點,沒有AR的是以參照物的左下角作為新建座標系的原點。

世界座標一般都是遊戲中的場景的絕對座標,相對座標是在世界範圍內參照其他元素的座標,絕對座標,相對座標,類似於物理中的絕對運動和相對運動

若 A 的絕對座標是(20, 20),B 的絕對座標是(30, 40)那麼A—>convertToNodeSpace(B->getPosition()) 
指的是計算 B 相對 A 的相對座標,那就是以 A 為參照計算 B 的新座標 =(30-20, 40-20)=(10, 20),
反過來 B->convertToNodeSpace(A->getPosition()) 就是以 B 為參照計算
 A = (20-30, 20-40)=(-10, -20)(PS:convertToNodeSpace(A->getPosition()) 中的 A->getPosition() 是絕對座標)
下面計算世界座標,計算世界座標是根據相對座標進行計算,好好理解這句話
若這個時候 A (30, 40) B (5, 6)都是絕對座標,但是利用下面這個函式 
A->convertToWorldSpace(B->getPosition())這個時候 B (5, 6)就變成了 A 的相對座標了,這個時候得到的新座標(35, 46)
1.convertToNodeSpace
Vec2 newPosition = node1->convertToNodeSpace(node2->getPosition());
將node2的位置座標轉換成相對於node1左下角頂點的座標。轉換方法:node1和node2位置不變,將座標軸原點設定為node1的左下角頂點,重新計算node2->getPosition()這個點的座標即為newPosition。
當呼叫此方法,返回的是相對於其父節點的節點座標,當然了,以下程式碼的實際用處並不大。

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());
接著,再打印出各點的x,y值:
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),那麼很明顯的:

  1. p1就是sprite1錨點相對於sprite2原點來說在sprite2座標系中的位置,經過對比上圖,我們可以得到(20-(-42),40-(-70))即(62,110)
  2. p2就是sprite1錨點相對於sprite2原點來說在上圖座標系中的位置,這樣我們可以計算出sprite1在sprite2座標系中的位置:(20+(-42),40+(-70)),即(-22,-30)
  3. p3就是sprite1錨點相對於sprite2錨點來說在sprite2座標系中的位置,也就是(20-(-15),40-(-30)),即(35,70)
  4. p4就是sprite1錨點相對於sprite2錨點來說在上圖座標系中的位置,也就是(20+(-15),40+(-30)),即(5,10)
  5. 現在我們可以知道,計算方法都是用sprite1的座標去加減sprite2的座標,針對本地座標系就用減法,針對世界座標系就用加法。

convertToWorldSpace實現過程

Vec2 Node::convertToWorldSpace(const Vec2& nodePoint) const
{
    Mat4 tmp = getNodeToWorldTransform();
    Vec3 vec3(nodePoint.x, nodePoint.y, 0);
    Vec3 ret;
    tmp.transformPoint(vec3,&ret);
    return Vec2(ret.x, ret.y);
}

Mat4 Node::getNodeToWorldTransform() const
{
    return this->getNodeToParentTransform(nullptr);
}