Cocos2d-x 3.0坐标系详解

Cocos2d-x坐标系和OpenGL坐标系相同,都是起源于笛卡尔坐标系。

笛卡尔坐标系

笛卡尔坐标系中定义右手系原点在左下角,x向右,y向上,z向外,OpenGL坐标系为笛卡尔右手系。



屏幕坐标系和Cocos2d坐标系

标准屏幕坐标系使用和OpenGL不同的坐标系,而Cocos2d则使用和OpenGL相同的坐标系。

iOS, Android, Windows Phone等在开发应用时使用的是标准屏幕坐标系,原点为屏幕左上角,x向右,y向下。

Cocos2d坐标系和OpenGL坐标系一样,原点为屏幕左下角,x向右,y向上。


在开发中,我们经常会提到两个比较抽象的概念-世界坐标系和本地坐标系。这两个概念可以帮助我们更好的理解节点在Cocos2d坐标系中的位置以及对应关系。


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

世界坐标系也叫做绝对坐标系,是游戏开发中建立的概念。因此,世界指游戏世界。cocos2d中的元素是有父子关系的层级结构,我们通过NodesetPosition设定元素的位置使用的是相对与其父节点的本地坐标系而非世界坐标系。最后在绘制屏幕的时候cocos2d会把这些元素的本地坐标映射成世界坐标系坐标。

本地坐标系也叫相对坐标系,是和节点相关联的坐标系。每个节点都有独立的坐标系,当节点移动或改变方向时,和该节点关联的坐标系将随之移动或改变方向。

锚点(Anchor Point

将一个节点添加到父节点里面时,需要设置其在父节点上的位置,本质上是设置节点的锚点在父节点坐标系上的位置。

  • Anchor Point的两个参数都在0~1之间。它们表示的并不是像素点,而是乘数因子。(0.5, 0.5)表示Anchor Point位于节点长度乘0.5和宽度乘0.5的地方,即节点的中心
  • Cocos2d-xLayerAnchor Point为默认值(0, 0),其他Node的默认值为(0.5, 0.5)

我们用以下代码为例,使用默认Anchor Point值,将红色层放在屏幕左下角,绿色层添加到红色层上:

 CCDirector* pDirector = CCDirector::sharedDirector();

    CCPoint visibleOrigin = pDirector->getVisibleOrigin();

    CCSize visibleSize = pDirector->getVisibleSize();

    

    auto red = CCLayerColor::create(ccc4(255, 100, 100, 128), visibleSize.width/2, visibleSize.height/2);

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

    red->addChild(green);

    this->addChild(red, 0);


    printf("red Position:%f--%f  Anchor:%f--%f\n",red->getPosition().x,red->getPosition().y,red->getAnchorPoint().x,red->getAnchorPoint().y);

    printf("green Position:%f--%f  Anchor:%f--%f\n",green->getPosition().x,green->getPosition().y,green->getAnchorPoint().x,green->getAnchorPoint().y);

1)输出:

red Position:0.000000--0.000000  Anchor:0.500000--0.500000

green Position:0.000000--0.000000  Anchor:0.500000--0.500000

显示:20141110182351080.png

2)加上语句:

   red->ignoreAnchorPointForPosition(false);

    red->setAnchorPoint(ccp(0.5f,0.5f));

输出:

  red Position:0.000000--0.000000  Anchor:0.500000--0.500000

   green Position:0.000000--0.000000  Anchor:0.500000--0.500000

显示:20141110182521950.png

3)加上语句:

red->setPosition(ccp(visibleSize.width/2 + visibleOrigin.x, visibleSize.height/2 + visibleOrigin.y));

输出:

red Position:240.000000--160.000000  Anchor:0.500000--0.500000

green Position:0.000000--0.000000  Anchor:0.500000--0.500000

显示:

20141110182808621.png

4)加上语句:

  green->ignoreAnchorPointForPosition(false);

  green->setAnchorPoint(ccp(1, 1));

输出:

red Position:240.000000--160.000000  Anchor:0.500000--0.500000

green Position:0.000000--0.000000  Anchor:1.000000--1.000000

显示:

20141110183048765.png


忽略锚点(Ignore Anchor Point)

Ignore Anchor Point全称是ignoreAnchorPointForPosition,作用是将锚点固定在一个地方。

如果设置其值为true,则图片资源的Anchor Pont固定为左下角,否则即为所设置的位置。

我们用以下代码为例,将两个层的ignoreAnchorPointForPosition设为true,并将绿色的层添加到红色的层上:

  CCDirector* pDirector = CCDirector::sharedDirector();

    CCPoint visibleOrigin = pDirector->getVisibleOrigin();

    CCSize visibleSize = pDirector->getVisibleSize();

    

    auto red = CCLayerColor::create(ccc4(255, 100, 100, 128), visibleSize.width/2, visibleSize.height/2);

   red->ignoreAnchorPointForPosition(true);

    red->setAnchorPoint(ccp(0.5f,0.5f));

    red->setPosition(ccp(visibleSize.width/2 + visibleOrigin.x, visibleSize.height/2 + visibleOrigin.y));


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

    green->ignoreAnchorPointForPosition(true);

    green->setAnchorPoint(ccp(1, 1));

    red->addChild(green);

    

    this->addChild(red, 0);

    printf("red Position:%f--%f  Anchor:%f--%f\n",red->getPosition().x,red->getPosition().y,red->getAnchorPoint().x,red->getAnchorPoint().y);

    printf("green Position:%f--%f  Anchor:%f--%f\n",green->getPosition().x,green->getPosition().y,green->getAnchorPoint().x,green->getAnchorPoint().y);

输出:


red Position:240.000000--160.000000  Anchor:0.500000--0.500000

green Position:0.000000--0.000000  Anchor:1.000000--1.000000

显示:

20141110184109578.png


触摸点(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 positionCocos2d-x用到的OpenGL坐标系上的点坐标。通常我们在开发中会使用两个接口getLocation()getLocationInView()来进行相应坐标转换工作。

在开发中一般使用getLocation()获取触摸点的GL坐标,而getLocation()内部实现是通过调用Director::getInstance()->convertToGL(_point);返回GL坐标。

// returns the current touch location in OpenGL coordinates

CCPoint CCTouch::getLocation() const

    return CCDirector::sharedDirector()->convertToGL(m_point); 

}


此外,关于世界坐标系和本地坐标系的相互转换,在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 = CCSprite::create("ccb/btn-b-0.png");//btn-b-0.png 100*100

    sprite1->setPosition(ccp(20,40));

    this->addChild(sprite1);  //此时添加到的是世界坐标系,也就是OpenGL坐标系

    

    CCSize s = sprite1->getContentSize();

    CCPoint p = sprite1->getPosition();

    CCPoint ap = sprite1->getAnchorPoint();

    CCRect rect = CCRectMake(p.x - ap.x * s.width , p.y - ap.y * s.height,s.width, s.height);

    

    auto *sprite2 = CCSprite::create("ccb/btn-b-1.png");

    sprite2->setPosition(ccp(-5,-20));

    this->addChild(sprite2); //此时添加到的是世界坐标系,也就是OpenGL坐标系

    

    // sprite2 这个节点的坐标ccp(-5,-20) 转换为 sprite1节点下的本地(节点)坐标系统的位置坐标

    CCPoint point1 = sprite1->convertToNodeSpace(sprite2->getPosition());

    CCPoint point2 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());

    // sprite2 这个节点的坐标ccp(-5,-20) 转换为 sprite1节点下的世界坐标系统的位置坐标

    CCPoint point3 = sprite1->convertToWorldSpace(sprite2->getPosition());

    CCPoint point4 = sprite1->convertToWorldSpaceAR(sprite2->getPosition());


    printf("point1 = (%.0f,%.0f)      \n",   point1.x,point1.y);

    printf("point2 = (%.0f,%.0f) \n",point2.x,point2.y);

    printf("point3 = (%.0f,%.0f) \n",point3.x,point3.y);

    printf("point4 = (%.0f,%.0f) \n",point4.x,point4.y);


先给出输出结果,再来说明是怎么转化的:

point1 = (0,-35) 

point2 = (-25,-60) 

point3 = (-10,-5) 

point4 = (15,20) 


先拿point1point2来说说没有AR和有AR的区别:

    CCPoint point1 = sprite1->convertToNodeSpace(sprite2->getPosition());

    CCPoint point2 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());


   point1point2都是把sprite2的世界坐标转化为节点sprite1的本地坐标。区别在于他们基于的sprite1的本地坐标系(节点坐标系)的位置不一样。节点的本地坐标系默认的原点位置在节点的左下角位置,即图1X1'-A1'-Y1'convertToNodeSpace来转换的时候基于的sprite1的本地坐标系的原点就是在sprite1的左下角的位置(1X1'-A1'-Y1'),而用convertToNodeSpace来转换的时候基于的sprite1的本地坐标系的原点在sprite1的锚点的位置(1X1-A1-Y1),因为我们知道一般节点的锚点在(0.5,0.5)的位置。上述代码中并没有设置sprit1的锚点,所以sprite1的默认为(0.5,0.5)。结论:没有AR后缀在转化的时候,实际上是以参照物的左下角,向上,向右来建立本地坐标系的,而有AR后缀在转化的时候,以参照物的锚点,向上向右来建立本地坐标系的。


再来说说世界坐标转本地坐标的计算方法:

point1 = A2-A1‘ = (-5,-20) - (-5,15)=(0,-35)

point2 = A2-A1 = (-5,-20)-(20,40)=(-25,-60)

结论:作为函数参数的世界坐标  -  参照物本地坐标系原点 

再来说说convertToWorldSpace的计算方法(节点坐标转世界坐标)

Point3 = A2+A1'=(-5,-20)+(-5,15)=(-10,-5)

point4 = A2+A1 =(-5,-20)+(20,40)=(15,20)

结论:节点坐标 参照物本地坐标系原点


在上面如果把sprite1的锚点修改成:

sprite1->setAnchorPoint(ccp(0,0));

则此时锚点就在左下角的位置(如图2),那么输出结果就是:

point1 = (-25,-60)      

point2 = (-25,-60) 

point3 = (15,20) 

point4 = (15,20) 




另外,这里没有提到sprite2的锚点对转换的结果的影响,因为转换结果和它没有半毛钱关系。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值