2016年5月27日 星期五

0527 Cocos2d-x 3.0坐標系詳解

Cocos2d-x 3.0坐標系詳解

Cocos2d-x坐標系和OpenGL坐標系相同,都是起源於笛卡爾坐標系。

笛卡爾坐標系

笛卡爾坐標系中定義右手系原點在左下角,x向右,y向上,z向外,OpenGL坐標係為笛卡爾右手系。
RightHand

屏幕坐標系和Cocos2d坐標系

標準屏幕坐標系使用和OpenGL不同的坐標系,而Cocos2d則使用和OpenGL相同的坐標系。
iOS, Android, Windows Phone等在開發應用時使用的是標準屏幕坐標系,原點為屏幕左上角,x向右,y向下。
Cocos2d坐標系和OpenGL坐標系一樣,原點為屏幕左下角,x向右,y向上。
UICoordinate
在開發中,我們經常會提到兩個比較抽象的概念-世界坐標系和本地坐標系。這兩個概念可以幫助我們更好的理解節點在Cocos2d坐標系中的位置以及對應關係。

世界坐標系(World Coordinate) VS 本地坐標系(Node Local)

世界坐標系也叫做絕對坐標系,是遊戲開發中建立的概念。因此,「世界」指遊戲世界。cocos2d中的元素是有父子關係的層級結構,我們通過Node的setPosition設定元素的位置使用的是相對與其父節點的本地坐標系而非世界坐標系。最後在繪製屏幕的時候cocos2d會把這些元素的本地坐標映射成世界坐標系坐標。
本地坐標系也叫相對坐標系,是和節點相關聯的坐標系。每個節點都有獨立的坐標系,當節點移動或改變方向時,和該節點關聯的坐標系將隨之移動或改變方向。

錨點(Anchor Point)

將一個節點添加到父節點裡面時,需要設置其在父節點上的位置,本質上是設置節點的錨點在父節點坐標繫上的位置。
  • Anchor Point的兩個參數都在0~1之間。它們表示的並不是像素點,而是乘數因子。(0.5, 0.5)表示Anchor Point位於節點長度乘0.5和寬度乘0.5的地方,即節點的中心
  • 在Cocos2d-x中Layer的Anchor Point為默認值(0, 0),其他Node的默認值為(0.5, 0.5)。
我們用以下代碼為例,使用默認Anchor Point值,將紅色層放在屏幕左下角,綠色層添加到紅色層上:
auto red = LayerColor::create(Color4B(255, 100, 100, 128), visibleSize.width/2, visibleSize.height/2);

auto green = LayerColor::create(Color4B(100, 255, 100, 128), visibleSize.width/4, visibleSize.height/4);

red->addChild(green);

this->addChild(red, 0);
anchorPoint
我們用以下代碼為例,將紅色層的Anchor Point設為中點放在屏幕中央,綠色層添加到紅色層上,綠色層錨點為右上角:
註:因為Layer比較特殊,它默認忽略錨點,所以要調用ignoreAnchorPointForPosition()接口來改變錨點,關於ignoreAnchorPointForPosition()接口的使用說明,我們將在後面詳細講解。
auto red = LayerColor::create(Color4B(255, 100, 100, 128), visibleSize.width/2, visibleSize.height/2);
red->ignoreAnchorPointForPosition(false);
red->setAnchorPoint(Point(0.5, 0.5));
red->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

auto green = LayerColor::create(Color4B(100, 255, 100, 128), visibleSize.width/4, visibleSize.height/4);
green->ignoreAnchorPointForPosition(false);
green->setAnchorPoint(Point(1, 1));
red->addChild(green);

this->addChild(red, 0);
anchorPoint

忽略錨點(Ignore Anchor Point)

Ignore Anchor Point全稱是ignoreAnchorPointForPosition,作用是將錨點固定在一個地方。
如果設置其值為true,則圖片資源的Anchor Pont固定為左下角,否則即為所設置的位置。
我們用以下代碼為例,將兩個層的ignoreAnchorPointForPosition設為true,並將綠色的層添加到紅色的層上:
auto red = LayerColor::create(Color4B(255, 100, 100, 128), visibleSize.width/2, visibleSize.height/2);
red->ignoreAnchorPointForPosition(true);
red->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

auto green = LayerColor::create(Color4B(100, 255, 100, 128), visibleSize.width/4, visibleSize.height/4);
green->ignoreAnchorPointForPosition(true);

red->addChild(green);

this->addChild(red, 0);
ignore

VertexZ,PositionZ和zOrder

  • VerextZ是OpenGL坐標系中的Z值
  • PositionZ是Cocos2d-x坐標系中Z值
  • zOrder是Cocos2d-x本地坐標系中Z值
在實際開發中我們只需關注zOrder。
可以通過setPositionZ接口來設置PositionZ。
以下是setPositionZ接口的說明:
Sets the 'z' coordinate in the position. It is the OpenGL Z vertex value.
即PositionZ的值即為opengl的z值VertexZ。同樣節點的PositionZ也是決定了該節點的渲染順序,值越大,但是與zOrder不同的區別在於,PositionZ是全局渲染順序即在根節點上的渲染順序,而zOrder則是局部渲染順序,即該節點在其父節點上的渲染順序,與Node的層級有關。用以下事例來說明:
    auto red = LayerColor::create(Color4B(255, 100, 100, 255), visibleSize.width/2, visibleSize.height/2);
    red->ignoreAnchorPointForPosition(false);
    red->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2));

    auto green = LayerColor::create(Color4B(100, 255, 100, 255), visibleSize.width/4, visibleSize.height/4);
    green->ignoreAnchorPointForPosition(false);
    green->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2 - 100));
    red->setPositionZ(1);
    green->setPositionZ(0);
    this->addChild(red, 0);
    this->addChild(green, 1);
zOrder
雖然green的zOrder大於red的zOder,但是因為red的PositionZ較大,所以red還是在green上面顯示。

觸摸點(Touch position)

所以在處理觸摸事件時需要用重寫以下四個函數:
    virtual bool onTouchBegan(Touch *touch, Event * event);
    virtual void onTouchEnded(Touch *touch, Event * event);
    virtual void onTouchCancelled(Touch *touch, Event * event);
    virtual void onTouchMoved(Touch *touch, Event * event);
在函數中獲取到touch,我們在設計遊戲邏輯時需要用到觸摸點在Cocos2d坐標系中的位置,就需要將touch的坐標轉換成OpenGL坐標系中的點坐標。
Touch position是屏幕坐標系中的點,OpenGL position是Cocos2d-x用到的OpenGL坐標繫上的點坐標。通常我們在開發中會使用兩個接口getLocation()getLocationInView()來進行相應坐標轉換工作。
在開發中一般使用getLocation()獲取觸摸點的GL坐標,而getLocation()內部實現是通過調用Director::getInstance()->convertToGL(_point);返回GL坐標。
此外,關於世界坐標系和本地坐標系的相互轉換,在Node中定義了以下四個常用的坐標變換的相關方法。
    // 把世界坐標轉換到當前節點的本地坐標系中
    Point convertToNodeSpace(const Point& worldPoint) const;

    // 把基於當前節點的本地坐標系下的坐標轉換到世界坐標系中
    Point convertToWorldSpace(const Point& nodePoint) const;

    // 基於Anchor Point把基於當前節點的本地坐標系下的坐標轉換到世界坐標系中
    Point convertToNodeSpaceAR(const Point& worldPoint) const;

    // 基於Anchor Point把世界坐標轉換到當前節點的本地坐標系中
    Point convertToWorldSpaceAR(const Point& nodePoint) const;
下面通過一個例子來說明這四個方法的理解和作用:
    auto *sprite1 = Sprite::create("HelloWorld.png");
    sprite1->setPosition(ccp(20,40));
    sprite1->setAnchorPoint(ccp(0,0));
    this->addChild(sprite1);  //此時添加到的是世界坐標系,也就是OpenGL坐標系

    auto *sprite2 = Sprite::create("HelloWorld.png");
    sprite2->setPosition(ccp(-5,-20));
    sprite2->setAnchorPoint(ccp(1,1));
    this->addChild(sprite2); //此時添加到的是世界坐標系,也就是OpenGL坐標系

    //將 sprite2 這個節點的坐標ccp(-5,-20) 轉換為 sprite1節點 下的本地(節點)坐標系統的 位置坐標
    Point point1 = sprite1->convertToNodeSpace(sprite2->getPosition());

    //將 sprite2 這個節點的坐標ccp(-5,-20) 轉換為 sprite1節點 下的世界坐標系統的 位置坐標
    Point point2 = sprite1->convertToWorldSpace(sprite2->getPosition());

    log("position = (%f,%f)",point1.x,point1.y);
    log("position = (%f,%f)",point2.x,point2.y);
運行結果:

Cocos2d: position = (-25.000000,-60.000000)
Cocos2d: position = (15.000000,20.000000)
convert1
convert2
其中:Point point1 = sprite1->convertToNodeSpace(sprite2->getPosition());
相當於sprite2這個節點添加到(實際沒有添加,只是這樣理解)sprite1這個節點上,那麼就需要使用sprite1這個節點的節點坐標系統,這個節點的節點坐標系統的原點在(20,40),而sprite1的坐標是(-5,-20),那麼經過變換之後,sprite1的坐標就是(-25,-60)。
其中:Point point2 = sprite1->convertToWorldSpace(sprite2->getPosition());
此時的變換是將sprite2的坐標轉換到sprite1的世界坐標系下,而其中世界坐標系是沒有變化的,始終都是和OpenGL等同,只不過sprite2在變換的時候將sprite1作為了」參照「而已。所以變換之後sprite2的坐標為:(15,20)。
convert3

沒有留言:

張貼留言

cocos2dx-lua 建立滑鼠監聽

重要關鍵字  EVENT_MOUSE_SCROLL addEventListenerWithSceneGraphPriority      if IsPc() then --建立滑鼠監聽         local listener = cc.EventListenerMouse...