遊戲人生,混亂的座標系和錨點

有關座標和適配的問題往往讓人髮指,最近項目對worldtoNode,nodetoworld搞的我頭都大了好多圈,加上ancher和適配,筆者都快瘋了,這裏準備做個系類,把這裏的東西屢一下,整理個文檔。

先從座標系和錨點開始。這裏想提醒讀者,這部分看似簡單,其實挺混亂的,而且相當重要。

coco2dx中涉及四種座標

GL座標系:

Cocos2D以OpenglES爲圖形庫,所以它使用OpenglES座標系。GL座標系原點在屏幕左下角,x軸向右,y軸向上。
屏幕座標:

系蘋果的Quarze2D使用的是不同的座標系統,原點在屏幕左上角,x軸向右,y軸向下。ios的屏幕觸摸事件CCTouch傳入的位置信息使用的是該座標系。因此在cocos2d中對觸摸事件做出響應前需要首先把觸摸點轉化到GL座標系。可以使用CCDirector的convertToGL來完成這一轉化。

世界座標系:

也叫做絕對座標系。世界座標系和GL座標系一致,原點在屏幕左下角。

本地座標系:

本地座標系也叫做物體座標系,是和特定物體相關聯的座標系(父節點)

其實GL座標和世界座標是一樣的,屏幕座標對於使用cocos2dx的開發者而言大部分時間可以無視,那麼其實需要大家理解的就是——世界座標,本地座標。


本地座標是相對於父節點而言的座標,例如在一個CCLayer中添加一個CCSprite,此時setPosition設置的就是相對CCLaye的本地座標。在渲染的過程中會將本地座標變成世界座標,所以我們很難發現其實我們設置的是相對座標。那麼相對座標究竟是什麼座標?世界座標是以屏幕坐下角爲(0,0),x軸向右,y軸向上的座標,本地座標則是以其父節點座標爲(0,0),x軸向右,y軸向上的座標。帶入個場景:


一個Layer裏添加一個sprite,設置layer座標爲(100,100),sprite座標(0,0)

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !CCLayer::init() )
    {
        return false;
    }
    
<span style="white-space:pre">	</span>//this->setPosition(ccp(100,100));
    // add "HelloWorld" splash screen"
    CCSprite* pSprite = CCSprite::create("HelloWorld.png");
	pSprite->setScale(0.5f);
	pSprite->setPosition(ccp(0,0)); 
	this->addChild(pSprite, 1);
    return true;
}


然後放開註釋,this->setPosition(ccp(100,100))

相信大家有些理解相對座標了,在不設置layer座標的情況下layer座標(0,0),sprite的setposition(0,0)與世界座標一樣,都是坐下角爲原點。但是layer座標變爲(100,100)之後,sprite座標就變成了父節點layer的座標(100,100)爲原點的相對座標位置。

cocos2dx在這裏提供了2個方法,將世界座標與本地座標相對轉化,只不過有點坑的是你需要知道世界座標與父節點的座標。

CCPoint convertToNodeSpace(const CCPoint& worldPoint);
CCPoint convertToWorldSpace(const CCPoint& nodePoint);
cocos的某系操作基於本地座標,例如moveTo,有些是基於世界座標,例如touch。當我們需要touch時間檢測碰撞或者觸摸,然後移動sprite的時候就需要用到以上函數了。

	CCRect pRect = pSprite->boundingBox();
	pRect.origin = pSprite->convertToWorldSpace(ccp(0,0));
	if(pRect.containsPoint(ccp(x,y)))
	{
		CCLOG("監測到點擊精靈事件");
		CCActionInterval* anim = CCMoveTo::create(1.0f,ccp(x,y));
		pSprite->runAction(anim);
	}

如果運行以上代碼,如果sprite的父節點座標爲(0,0),那麼精靈會點擊的時候移動到點擊位置,如果不是(0,0),那麼恭喜你,精靈會移動到想都想不到的地方。怎麼解決?我的方法是使用moveBy,計算點擊位置與精靈位置的矢量差,通過moveBy移動。


接下來咱們說說錨點:

這是一個難以理解的東西,什麼是錨點?這個不好說,還是看看下邊的例子吧。

首先ignoreAnchorPointForPosition這個東西,很神奇,先看看設置爲false的情況。

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !CCLayer::init() )
    {
        return false;
    }

    // add "HelloWorld" splash screen"
    CCSprite* pSprite = CCSprite::create("HelloWorld.png");
	pSprite->setScale(0.5f);
	pSprite->setPosition(ccp(0,0));
	pSprite->ignoreAnchorPointForPosition(false);
	pSprite->setAnchorPoint(ccp(0,0));
	CCPoint point = this->getAnchorPoint();
	this->addChild(pSprite, 1);
	//initScroll();
    return true;
}


pSprite->setAnchorPoint(ccp(0.5,0.5));




在ignoreAnchorPointForPosition(false);的情況下,設置錨點可以這樣理解,設置位置就是設置錨點的位置,當錨點爲(0,0)的時候,圖片的左下角爲錨點,圖片左下角與設置位置重合。當錨點爲(0.5,0.5)時候,圖片的中點爲錨點,設置位置(0,0)就是圖片中點位置爲(0,0)。


在說說ignoreAnchorPointForPosition(true)的情況,設置錨點爲(0.5,0.5),運行後如下



這是什麼情況? 我們先命名一個變量:父子點,父節點提供給子節點的座標系原點。經過測試,設置爲ture之後,設置子節點錨點時父子點一同被設置,父節點的父子點和sprite的錨點都被設置到了中點上,所以當setPosition爲(0.5,0.5),sprite位置(0,0)的時候,就是父節點的父子點與sprite的錨點重合,得到如上情況。

總結一下:所謂的設置位置,用的都是本地座標,設置的是相對位置。設置位置就是設置父子點與子錨點之間的相對位置關係。錨點從0-1,及從左到右,從下到上。默認情況下父子點位置爲父節點左下角。默認情況下ignoreAnchorPointForPosition爲FALSE,錨點爲(0.5,0.5)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章