Cocos2dx学习笔记(7)——坐标系总结

本章将综合之前所讲的锚点、ZOrder,对坐标系进行详细讲解

本文仅供个人记录和复习,不用于其他用途

3.x版本改变

在3.x版本中,setZOrder()getZOrder()被替换为setLocalZOrder()getLocalZOrder()。同时也新增了setGlobalZOrder()getGlobalZOrder()。区别如下:

  • globalZOrder()是用于渲染器中用来给“绘制命令”排序的,也就是全局渲染
  • localZOrder()是用于父节点的子节点数组中给节点对象排序的

笛卡尔坐标系

关于什么是笛卡尔坐标系,学习笔记第4章的第一个图就已经表达的很清楚了(表示的是笛卡尔右手系)。事实上,标准的屏幕坐标系与OpenGL的不同。标准坐标系的原点在左上角,x向右,y向下。而OpenGL则是原点在左下角,x向右,y向上。显然,Cocos2d坐标系也是和OpenGL坐标系一样。

我们经常使用的setPosition()是基于OpenGL坐标系,而触摸事件中的Touch则是基于屏幕坐标系。

GL坐标系和UI坐标系的转换

1
2
Director::getInstance()->convertToUI();
Director::getInstance()->convertToGL();

利用导演类单例可以轻松地转换坐标系。

世界坐标系和本地坐标系

这两个是游戏开发过程中经常被提到的概念,方便理解Cocos2d坐标系。世界坐标系顾名思义,是游戏世界的坐标系,也就是我们所看到的游戏画面,说白了,就是依据屏幕来划分的坐标系。当然,这种坐标系也是我们一般所理解的,也就是每个元素在屏幕上都具有相对应的位置。那么,为什么还会有本地坐标系这个概念呢?因为Cocos2d中的每一个节点,都是相互关联的存在。setPosition()我们使用的最多,但是这个只是调整子节点在父节点中的位置。

渲染顺序

这个在ZOrder那一章已经讲过,但是呢,ZOrder属于2.x版本。3.x中的渲染顺序由两个因素决定:

  • 全局顺序:GlobalZOrder
  • 局部顺序:LocalZOrder

z的值越小,越先被渲染。所有节点的全局顺序都默认为0,也就是说节点一般按照场景图的上下顺序来进行渲染。当然,如果你不想按照场景图的上下层次,你也可以使用全局顺序。要注意的是,SpriteBatchNode不能使用全局顺序。

局部顺序就是子节点相对于其他子节点的顺序,总的来说,一个父节点下的子节点排序,就是局部顺序。如果两个子节点的LocalZOrder值相同,那么先加入父节点的就先被渲染。这一点与全局顺序不同,如果两个节点的GlobalZOrder值相同,那么渲染顺序未知。

坐标转换

这个内容在坐标系一章讲的很清楚,这里讲一下怎么把本地坐标转换成世界坐标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 把世界坐标,转换到当前节点的本地坐标系中
// 基于AnchorPoint,把基于当前节点的本地坐标系下的坐标,转换到世界坐标系中
Vec2 convertToNodeSpace(const Vec2& worldPoint) const;
Vec2 convertToNodeSpaceAR(const Vec2& worldPoint) const;
// 把基于当前节点的本地坐标系下的坐标,转换到世界坐标系中
// 基于AnchorPoint,把世界坐标转换到当前节点的本地坐标系中
Vec2 convertToWorldSpace(const Vec2& nodePoint) const;
Vec2 convertToWorldSpaceAR(const Vec2& nodePoint) const;
// 把触点坐标,转换到当前节点的本地坐标系中
Vec2 convertTouchToNodeSpace(Touch * touch) const;
Vec2 convertTouchToNodeSpaceAR(Touch * touch) const;

举个简单的例子,设A的坐标为(20, 40),锚点为(0, 0),B点坐标为(-5, -20),锚点为(1, 1)。

1
Vec2 point1 = A->convertToNodeSpace(B->getPosition());

point1的值是(-25, -60),也就是说,B相对于A而言,是处在以A的锚点为原点的坐标系的(-25, -60)的位置。简单来说,就是B相对于A的位置。

1
Vec2 point2 = A->convertToWorldSpace(B->getPosition());

point2的值是(15, 20),这里的意思就是说,B当前的坐标,就是B相对于A的位置,然后把这个位置转换成世界坐标。B的坐标是(-5, -20),那么就是在A的(-5, -20)处。在世界坐标中,A的(-5, -20)就相当于(15, 20)。简单来说,首先B的坐标是B相对于A的位置,然后把这个位置转换成世界坐标。

获取触摸点

我们在做游戏的触摸检测时,经常要用到以下格式的代码:

1
if(planeRect.containsPoint(this->getParent()->convertTouchToNodeSpace(touch)))

这里做一个判断,判断触摸点有没有在planeRect中。planeRect是通过boundingBox()获取的,代表着当前节点对象在父节点对象下的缩放之后的本地坐标大小,使用的是OpenGL坐标系。touch对象使用的是屏幕坐标系,所以必须转换到OpenGL坐标系,再转换到父节点下。具体的转换过程上面已经讲过了。