1. 程式人生 > >cocos2dx學習之路----第七篇(座標系統中本地座標與世界座標的轉換詳解)

cocos2dx學習之路----第七篇(座標系統中本地座標與世界座標的轉換詳解)

這一篇我們來談談關於座標系統中本地座標與世界座標的轉換問題。

在上一篇中我們知道了標準的螢幕座標系、本地座標與世界座標的區別,還了解了關於cocos2dx的座標系問題。

其實關於OpenGL的座標,如果我們做2d程式設計,是可以暫時忽略Z軸座標的。但是卻需要記住的是渲染的深度問題,即Z_Older的值,這個值越大,其渲染出的圖將會越在上面。關於這個值暫時記住就行。

在進入正題之前,我們需要知道什麼是錨點?

錨點其實就是關於變換的參考點,比如旋轉,平移縮放等。

錨點被規範化了,就像百分比一樣,(0,0)意味著在節點的左下角,而(1,1)就在節點的右上角,你可以改變錨點的值在(0,0)到(1,1)範圍內。

預設的錨點為(0.5,0.5),即中心點。

值得注意的是,如果這個節點是一個物理節點(physics body),它的錨點被固定在中間(in the middle),不可以改變。

我們來看一張圖片:


如果我們把錨點設定為A點,那麼,讓它順時針旋轉90°之後,變成了如下圖所示:


對每個Node節點都可以通過node->setAnthorPoint();來設定。

好了,下面進入正題吧~

Node物件給我們提供以下函式進行本地座標的和世界座標的轉換:

Vec2 convertToNodeSpace(const Vec2& worldPoint) const ;//將世界座標轉換為本地座標

Vec2 convertToWorldSpace(const Vec2& nodePoint) const;//將本地座標轉換為世界座標

Vec2 convertToNodeSpaceAR(const Vec2& worldPoint) const;//不忽略參考點錨點的情況下,將世界座標轉換為本地座標

Vec2 convertToWorldSpaceAR(const Vec2& nodePoint) const;//不忽略參考點錨點的情況下,將本地座標轉換為世界座標

Vec2 convertTouchToNodeSpace(Touch * touch) const;//將世界座標中的觸控點轉換為本地座標

Vec2 convertTouchToNodeSpaceAR(Touch * touch) const;//不忽略參考點錨點的情況下,將世界座標中的觸控點轉換為本地座標

這些函式都會返回一個值,這個值便是轉換後坐標點的值,即返回一個型別為Vec2的點。

這裡我們就只講一下前面四個函式的轉換問題。

將世界座標轉換為本地座標,這個會比較簡單一點。我們先來看下下面的執行圖:


我就不先貼程式碼出來,先來講講它們是如何轉換的,表格是它們的相關資訊:

        屬性

方塊

錨點

位置

寬度

高度

Block1

(1,1)

(300,300)

200

200

Block2

(0,0)

(0,0)

100

100

需要說明的是,方塊Block2是Block1的子節點。

我們把方塊Block2以方塊Block1為參考點從方塊Block1為本地座標中轉換為世界座標,轉換的過程是:

第一種情況,在不考慮參考點錨點的前提下,轉換後點的座標 =  父節點的座標 + 子節點在父節點的座標

所以,轉換之後的點的值為:(100,100)+(0,0)  =(100,100)

實現程式碼如下:

Vec2 ConvertPoint = Block1->convertToNodeSpace(Block2->getPosition());

第二種情況,在考慮點錨點的前提下,轉換後的點座標 = 父節點的座標 + 子節點的座標 + 父節點錨點位置點*父節點實際大小

所以,轉換之後的點的值為:(100,100)+ (0,0)+(1,1) * (200,200) = (300,300)

實現程式碼如下:

Vec2 ConvertPoint = Block1->convertToNodeSpaceAR(Block2->getPosition());

以上是本地座標轉換為世界座標的實現。

現在來看看怎麼由世界座標轉換為本地座標,同樣我我們先來看下執行圖:

            屬性

方塊

  錨點

     位置

 寬度             

 高度   

Block1

(1,1)

  (100,100)

100

100

Block2

(0,0)

  (100,100)

100

100

這兩個方塊都通過this->addChild()新增上去,所以和上面不同,它們的位置可以看作世界座標。

現在,我們可以以方塊Block1為參考點把方塊Block2轉化為方塊Block1的本地座標。

思路也是和上面差不多,轉換的過程如下:

第一種情況,不考慮參考點錨點的前提下,轉換後的座標 = 方塊Block2的座標 - 方塊Block1的左下角點的座標,

所以,轉換後點的座標 = (100,100)- (0,0) = (100,100)

實現程式碼如下:

Vec2 ConvertPoint = Block1->convertToNodeSpace(layer_block2->getPosition());

第二種情況,考慮參考點錨點的前提下,轉換後的座標 = 方塊Block2的座標 - 方塊Block1的左下角座標 - 錨點的位置點 * 方塊Block1的大小,

所以,轉換後點的座標 = (100,100)- (0,0) - (1,1) * (100,100)  = (0,0)

Vec2 ConvertPoint = Block1->convertToNodeSpaceAR(layer_block2->getPosition());

好了,現在把上面的所有程式碼都貼出來吧~

ConvertCoordinateDemo.h:

#pragma once
#include"cocos2d.h"

USING_NS_CC;

class ConvertCoordinateDemo :public Layer{
public:
	virtual bool init();
	static Scene *createScene();
	CREATE_FUNC(ConvertCoordinateDemo);

};
ConvertCoordinateDemo.cpp:
#include"ConvertCoordinateDemo.h"

Scene *ConvertCoordinateDemo::createScene()
{
	Scene* scene = Scene::create();
	ConvertCoordinateDemo *layer = ConvertCoordinateDemo::create();
	scene->addChild(layer);
	return scene;
}
bool ConvertCoordinateDemo::init(){
	if (!Layer::init()){
		return false;
	}
	/*********************************************************
		本地座標轉換為世界座標
	************************************************************/
	//方塊一
	LayerColor *layer_block1 = LayerColor::create(Color4B::RED, 100, 100);
	this->addChild(layer_block1);
	layer_block1->ignoreAnchorPointForPosition(false);//不忽略錨點
	layer_block1->setAnchorPoint(Vec2(1,1));
	layer_block1->setPosition(Vec2(100, 100));
	
	auto mark1 = Label::createWithSystemFont("Block1", "", 30);
	mark1->setPosition(Vec2(50, 50));
	layer_block1->addChild(mark1);
	//方塊二
	LayerColor *layer_block2 = LayerColor::create(Color4B::GREEN, 100, 100);
	this->addChild(layer_block2);
	layer_block2->ignoreAnchorPointForPosition(false);//層和場景都是忽略錨點的,預設為(0,0),這是設定不忽略錨點,即在中心點
	layer_block2->setAnchorPoint(Vec2::ZERO);
	layer_block2->setPosition(Vec2(100, 100));

	auto mark2 = Label::createWithSystemFont("Block2", "", 30);
	mark2->setPosition(Vec2(50, 50));
	layer_block2->addChild(mark2);

	auto Block2_convertToNodeSpace = layer_block1->convertToNodeSpace(layer_block2->getPosition());
	auto Block2_convertToNodeSpaceAR = layer_block1->convertToNodeSpaceAR(layer_block2->getPosition());

	log("Block2_convertToNodeSpace:_x = %f,_y = %f", Block2_convertToNodeSpace.x, Block2_convertToNodeSpace.y);
	log("Block2_convertToNodeSpaceAR:_x = %f,_y = %f", Block2_convertToNodeSpaceAR.x, Block2_convertToNodeSpaceAR.y);

	/*****************************************************************
		世界座標轉換為本地座標
	*******************************************************************/
	//方塊一
	//LayerColor *layer_block1 = LayerColor::create(Color4B::RED, 200, 200);
	//this->addChild(layer_block1);
	//layer_block1->ignoreAnchorPointForPosition(false);
	//layer_block1->setAnchorPoint(Vec2(1,1));
	//layer_block1->setPosition(Vec2(300, 300));

	//auto mark1 = Label::createWithSystemFont("Block1", "", 50);
	//mark1->setPosition(Vec2(100, 120));
	//layer_block1->addChild(mark1);
	//方塊二
	//LayerColor *layer_block2 = LayerColor::create(Color4B::GREEN, 100, 100);
	//layer_block1->addChild(layer_block2);
	//layer_block2->ignoreAnchorPointForPosition(false);//層和場景都是忽略錨點的,預設為(0,0),這是設定不忽略錨點,即在中心點
	//layer_block2->setAnchorPoint(Vec2::ZERO);
	//layer_block2->setPosition(Vec2::ZERO);

	//auto mark2 = Label::createWithSystemFont("Block2", "", 30);
	//mark2->setPosition(Vec2(50, 50));
	//layer_block2->addChild(mark2);

	//auto Block2_convertToWorldSpace = layer_block1->convertToWorldSpace(layer_block2->getPosition());
	//auto Block2_convertToWorldSpaceAR = layer_block1->convertToWorldSpaceAR(layer_block2->getPosition());

	//log("Block2_convertToWorldSpace:_x = %f,_y = %f", Block2_convertToWorldSpace.x, Block2_convertToWorldSpace.y);
	//log("Block2_convertToWorldSpaceAR:_x = %f,_y = %f", Block2_convertToWorldSpaceAR.x, Block2_convertToWorldSpaceAR.y);
	return true;
}
說明一下,這裡的方塊是用一個顏色層來建立,而層和場景預設是忽略錨點的,預設為(0,0),所以,為了能夠重新設定層的錨點,我們先把層的設定為不忽略錨點,即呼叫ignoreAnchorPointForPosition()這個方法把它設定為false。其它的就不再多說了。希望能夠對你有所幫助,如果有什麼問題或是錯誤可以留下你的評論,我會很樂意為你解答的~

下一篇我會來談談關於cocos2dx裡面的觸控事件該如何實現。