f(sixleaves) = sixleaves

重剑无锋 大巧不工

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  95 随笔 :: 0 文章 :: 7 评论 :: 0 Trackbacks
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);

anchorPoint

我们用以下代码为例,将红色层的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);

anchorPoint

我的理解:
怎么理解呢,如果你明白了我刚才说的话,就能够理解第二个代码运行的效果。我们把红色的锚点设置成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)

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

我的理解:
其实这四种相应Layer层的方法已经被废弃,但是我们现在的关注点是坐标系的理解,不必管它。
官方文档说要阐述的是,说我们通过这些事件回调函数,在touch中取得的坐标是屏幕坐标。但是由于数据的封装性,我们不用管,
我们只需要getLocation即可获得笛卡尔坐标系。
其他的Point convertToNodeSpace(const Point& worldPoint) const;的转换例子,我修改了官方的注释,只要理解了我上面所有的叙述,理解这个是十分容易的。不再复述。
说对就不难理解代码的运行效果了。

2015/4/6上午1:53:18
posted on 2015-04-06 01:53 swp 阅读(2977) 评论(0)  编辑 收藏 引用 所属分类: cocos2dx

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理