天天看點

cocos2d 坐标系的了解 Cocos2d坐标系

本文主要對 cocos2d官網上的内容進行了補充注解。http://www.cocos2d-x.org/docs/manual/framework/native/graphic/coordinate-system/zh

Cocos2d坐标系

不同坐标系簡介

笛卡爾坐标系

你可能上學的時候就已經知道“笛卡爾坐标系”了,它在幾何課本裡經常用到。如果你已經忘得差不多了,下面這些圖檔可以很快喚起你的記憶:

cocos2d 坐标系的了解 Cocos2d坐标系

在移動遊戲開發過程中,有三種類型的坐标系你可能遇到:

UI坐标系

iOS/Android/Windows SDK中的通用UI坐标系:

  • 起點坐标(x=0, y=0)位于左上角
  • X軸從螢幕最左邊開始,由左向右漸增
  • Y軸坐标從螢幕最上方開始,由上向下漸增

詳見下圖

cocos2d 坐标系的了解 Cocos2d坐标系

Direct3D坐标系

DirectX 使用Left-handed Cartesian Coordinate

OpenGL和Cocos2d坐标系

Cocos2d-x/-html5/-iphone使用的坐标系和OpenGL的坐标系一樣,名為“Right-handed Cartesian Coordinate Syste”。

cocos2d 坐标系的了解 Cocos2d坐标系

在2D世界中,我們僅會使用x軸和y軸。是以在你的cocos2d遊戲中:

起點坐标(x=0, y=0)位于左下角,這意味着螢幕位于

X軸從螢幕最左邊開始,由左向右漸增

Y軸坐标從螢幕最下方開始,由下向上漸增

下面這張圖檔有助于更好的闡述Cocos2d-x坐标:

cocos2d 坐标系的了解 Cocos2d坐标系

一定要注意:通用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)。是以,子對象位置是:

cocos2d 坐标系的了解 Cocos2d坐标系

看到這裡,我們對 錨點還是不太清晰。畢竟文檔隻是一片介紹性的文檔,還需要消化一番。對于一塊圖像而已,cos2d總是用一塊矩形來描述它的,既然如此,那麼必然有四個頂點,左下(0,0),右下(1,0),左上(0,1),右上(1,1),如下圖所示:

cocos2d 坐标系的了解 Cocos2d坐标系

按照了解來看,錨點并不是一個絕對值,隻是一個相對值,如果選擇(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)開始畫圖,不同的隻是錨點,畫出的圖像對于下圖中的四種矩形:

cocos2d 坐标系的了解 Cocos2d坐标系

如此說來,通俗的說,錨點就是決定圖像如何擺放,setPosition(100,100) 隻是告訴cos2d我們要畫一個圖像,至于怎麼畫,畫的位置如何,完全是由錨點來決定的。可以想象一下,如果錨點是(.0.5,0.5),那畫出的矩形正好以上圖中的(100,100)為中心,将4個矩形串起來。

再看下面官方給出的兩個例子,更加證明了這個道理。

錨點

作為例子,下面這個精靈有的錨點位于 ccp(0,0),位置位于ccp(0,0)。

cocos2d 坐标系的了解 Cocos2d坐标系

這個矩形精靈将被放到它的父對象(layer)的左下角。

示例:

// create sprite
    CCSprite* sprite = CCSprite::create("bottomleft.png");
    sprite->setAnchorPoint(ccp(0, 0));// Anchor Point
    sprite->setPosition(ccp(0,0));
    addChild(sprite);
      
cocos2d 坐标系的了解 Cocos2d坐标系

在另一個例子中,我們會擺放一個坐标為ccp(0.5,0.5)的anchorPoint,以便您更好的了解錨點的相對值。

cocos2d 坐标系的了解 Cocos2d坐标系
// create sprite
    CCSprite* sprite = CCSprite::create("center.png");
    sprite->setAnchorPoint(ccp(0.5, 0.5));// Anchor Point
    sprite->setPosition(ccp(0,0));
    addChild(sprite);
      
cocos2d 坐标系的了解 Cocos2d坐标系

正如你從圖中看出的,錨點取的不是像素值,此值的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).

cocos2d 坐标系的了解 Cocos2d坐标系

說到這裡,其實文檔有遺忘了一個概念,那就是“本地坐标”的概念。本地坐标永遠都是指矩形的左下(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在螢幕上對應的坐标。

cocos2d 坐标系的了解 Cocos2d坐标系

這個地方翻譯顯得有點意猶未盡啊,最後一句應該是"上面的代碼會轉換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)