cocos2dx坐标系与笛卡尔坐标系简而言之,cocos2dx的2d中的坐标系与我们初中所学的笛卡尔坐标系一样。也就是向右为X正轴,向上为Y正轴。
在屏幕中,其原点位于屏幕的左下方。
屏幕坐标系屏幕坐标系的Y正轴是向下,X正轴不变。原点位于左上方。
World Coordinate与Node Local
World Coordinate == > 世界坐标系、绝对坐标系
Node Local == > 本地坐标系、相对坐标系
世界坐标系的意思是指游戏世界。Node Local坐标系其实就是我们在调用setPositon的时候设置的位置是相对于父节点的,是相对坐标,而不是绝对坐标。
我们需要知道的一个比较重要的东西就是Anchor Point == > 锚点
之所以有锚点这个概念,是因为在游戏中,每个节点都有自己的坐标系,这样我们setPositon才能是相对于父亲节点的,也就是相对坐标系。
但是每个节点的坐标系原点,可能不一样。所以我们就把这个点成为锚点。简而言之,锚点就是游戏中节点的坐标系原点。而且每个节点都有自己的坐标系。
验证:看如下官方文档。
我们用以下代码为例,使用默认Anchor Point值,将红色层放在屏幕左下角,绿色层添加到红色层上:
1 2 3 4 5 6 7 | 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);
|
我们用以下代码为例,将红色层的Anchor Point设为中点放在屏幕中央,绿色层添加到红色层上,绿色层锚点为右上角:
注:
因为Layer比较特殊,它默认忽略锚点,所以要调用ignoreAnchorPointForPosition()
接口来改变锚点,关于ignoreAnchorPointForPosition()
接口的使用说明,我们将在后面详细讲解。
1 2 3 4 5 6 7 8 9 10 11 | 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);
|
我的理解:怎么理解呢,如果你明白了我刚才说的话,就能够理解第二个代码运行的效果。我们把红色的锚点设置成0.5,05,绿色的设置成1,1则如图上所示,两个锚点分别为期两个节点的坐标原点。而绿色没有设置位置,所以其位置默认是相对于红色是0,0位置。
为什么红色成的左下角为本地坐标原点了。你可以这样理解。
1.每个节点的本地坐标原点都是在左下角。
2.每个节点还有一个与AnchorPoint有关的坐标系,其坐标原点就是AnchorPoint,我们把其成为Anchor坐标系。
3.setPosition针对的是AnchorPoint坐标系下的节点,与父亲本地节点坐标系的位置。(此时AnchorPoint即代表这个节点,而节点的之后的渲染效果就看节点的AnchorPoint是在节点的什么位置
)
几个知识要点换种说法:ignoreAnchorPointForPosition(
true
); == > 本质上就是setAnchorPoint(0,0);
什么时候把参数设置为false呢?
这种情况只有在Layer类中才需要使用,因为Layer的锚点是(0,0)。也就是等效于
ignoreAnchorPointForPosition(
true
);
所以我们要修改其锚点需要先
ignoreAnchorPointForPosition(
false
);
VertexZ,PositionZ和zOrder
关于这三个参数先看官方解释。
- VerextZ是OpenGL坐标系中的Z值
- PositionZ是Cocos2d-x坐标系中Z值
- zOrder是Cocos2d-x本地坐标系中Z值
在实际开发中我们只需关注zOrder。
可以通过setPositionZ
接口来设置PositionZ。
以下是setPositionZ
接口的说明:
1 | Sets the 'z' coordinate in the position. It is the OpenGL Z vertex value.
|
即PositionZ的值即为opengl的z值VertexZ。同样节点的PositionZ也是决定了该节点的渲染顺序,值越大,但是与zOrder不同的区别在于,PositionZ是全局渲染顺序即在根节点上的渲染顺序,而zOrder则是局部渲染顺序,即该节点在其父节点上的渲染顺序,与Node的层级有关。
我的理解:
我们开发中真正用到的也就是PositionZ,而PositionZ等效于VertexZ。PositionZ是什么呢,其实PositionZ就是三维坐标中的Z轴,而cocos2dx的坐标系和OpenGl、笛卡尔坐标系是一致的,简而言之前两种都是笛卡尔坐标系。也就是说我们以屏幕左下角为原点。
向右为X轴正向,向上为Y轴正向。所以Z轴就是向我们对着屏幕的这边,也就是所我们设置Z轴,其实可以想象成立体的空间,在这个空间内,节点的是有顺序的,Z值越高,我们看过去越能先看到,因为我们所看的屏幕相当于俯视图。对把屏幕想成一张俯视图。
所以Z轴值越大,就越先看到,越能盖住它下面的东西,因为我们看的是俯视图。有趣吧!.
那么OrderZ又是什么呢,OrderZ只是设置,在父亲节点下,他们的绘制顺序。但是PositionZ是决定最终呈现在屏幕面前的视觉顺序。
我们接着来看官方所讲述的触摸事件于坐标系的关系。先看官方文档解释如下:(我修改了官方注释,请看我的理解)
触摸点(Touch position)
所以在处理触摸事件时需要用重写以下四个函数:
1 2 3 4 | 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中定义了以下四个常用的坐标变换的相关方法。
1 2 3 4 5 6 7 8 9 10 11 | // 把世界坐标转换到当前节点的本地坐标系中
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 ;
|
下面通过一个例子来说明这四个方法的理解和作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | auto *sprite1 = Sprite::create( "HelloWorld.png" );
sprite1->setPosition(ccp(20,40));
sprite1->setAnchorPoint(ccp(0,0));
this ->addChild(sprite1); //添加到父亲节点下,sprite1的锚点相对于父亲节点的本地坐标系的位置是x = 20, y = 40。而这个锚点又是自身的左下角。所以你可以画出如下图
auto *sprite2 = Sprite::create( "HelloWorld.png" );
sprite2->setPosition(ccp(-5,-20));
sprite2->setAnchorPoint(ccp(1,1));
this ->addChild(sprite2); //意思如上
//将 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);
|
1 2 3 4 | 运行结果:
Cocos2d: position = (-25.000000,-60.000000)
Cocos2d: position = (15.000000,20.000000)
|
其中: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)。
我的理解:
其实这四种相应Layer层的方法已经被废弃,但是我们现在的关注点是坐标系的理解,不必管它。
官方文档说要阐述的是,说我们通过这些事件回调函数,在touch中取得的坐标是屏幕坐标。但是由于数据的封装性,我们不用管,
我们只需要getLocation即可获得笛卡尔坐标系。
其他的Point convertToNodeSpace(
const
Point& worldPoint)
const
;的转换例子,我修改了官方的注释,只要理解了我上面所有的叙述,理解这个是十分容易的。不再复述。
说对就不难理解代码的运行效果了。
2015/4/6上午1:53:18