Cocos2D-x 以OpenGL 和OpenGL ES 为基础,所以自然支持OpenGL 坐标系。该坐标系原点在屏幕左下角,x 轴向右,y 轴向上。
屏幕坐标系使用的是不同的坐标系统,原点在屏幕左上角,x 轴向右,y 轴向下。iOS 的屏幕触摸事件CCTouch 传入的位置信息使用的是该坐标系。因此在Cocos2D-x 中对触摸事件做出响应前,需要首先把触摸点转化到OpenGL 坐标系。可以使用CCDirector 的convertToGL 方法来完成这一转化。
从触摸点获取到在屏幕坐标系中的坐标 CCTouch::getLocationInView()
从触摸点获取到在OpenGL坐标系中的坐标 CCTouch::getLocation()
2. 世界坐标系:
世界坐标系也叫作绝对坐标系,是游戏开发中建立的概念,因此,“世界”即是游戏世界。它建立了描述其他坐标系所需要的参考标准。我们能够用世界坐标系来描述其他坐标系的位置。它是Cocos2D-x 中一个比较大的概念。Cocos2D-x 中的元素是有父子关系的层级结构。通过CCNode 设置位置使用的是相对其父节点的本地坐标系,而非世界坐标系。最后在绘制屏幕的时候,Cocos2D-x 会把这些元素的本地节点坐标映射成世界坐标系坐标。世界坐标系和OpenGL 坐标系方向一致,原点在屏幕左下角,x 轴向右,y 轴向上。
CCNode 类的设置位置使用的就是父节点的节点坐标系。它和OpenGL 坐标系的方向也是一致的,x 轴向右,y 轴向上,原点在父节点的左下角。如果父节点是场景树中的顶层节点,那么它使用的节点坐标系就和世界坐标系重合了。
CCNode 类对象中有两个方便的函数可以做坐标转换:
convertToWorldSpace:把基于当前节点的本地坐标系下的坐标转换到世界坐标系中。
convertToNodeSpace:把世界坐标转换到当前节点的本地坐标系中。
这两种转换都是不考虑锚点的,都以当前节点父类的左下角的坐标为标准。另外,CCNode 还提供了convertToWorldSpaceAR 和convertToNodeSpaceAR。这两个方法完成同样的功能,但是它们的基准坐标是基于坐标锚点的。几乎所有的游戏引擎都会使用类似的本地坐标系而非世界坐标系来指定元素的位置。
这样做的好处是,当计算物体运动的时候,使用同一本地坐标系的元素可以作为一个子系统独立计算,最后再加上坐标系的运动即可,这是物理研究中常用的思路。例如,一个在行驶的车厢内上下跳动的人,只需要在每帧绘制的时候计算他在车厢坐标系中的位置,然后加上车的位置就可以计算出人在世界坐标系中的位置。如果使用单一的世界坐标系,人的运动轨迹就变复杂了,就涉及中学所学到的运动轨迹的合成与分解。
1、convertToNodeSpace 和 convertToWorldSpace
- CCSprite *sprite1 = CCSprite::create("CloseNormal.png");
- sprite1->setPosition(ccp(20,40));
- sprite1->setAnchorPoint(ccp(0,0));
- this->addChild(sprite1); //此时添加到的是世界坐标系,也就是OpenGL坐标系
- CCSprite *sprite2 = CCSprite::create("CloseNormal.png");
- sprite2->setPosition(ccp(-5,-20));
- sprite2->setAnchorPoint(ccp(1,1));
- this->addChild(sprite2); //此时添加到的是世界坐标系,也就是OpenGL坐标系
- //将 sprite2 这个节点的坐标ccp(-5,-20) 转换为 sprite1节点 下的本地(节点)坐标系统的 位置坐标
- CCPoint point1 = sprite1->convertToNodeSpace(sprite2->getPosition());
- //将 sprite2 这个节点的坐标ccp(-5,-20) 转换为 sprite1节点 下的世界坐标系统的 位置坐标
- CCPoint point2 = sprite1->convertToWorldSpace(sprite2->getPosition());
- CCLog("position = (%f,%f)",point1.x,point1.y);
- CCLog("position = (%f,%f)",point2.x,point2.y);
运行结果:
- Cocos2d: position = (-25.000000,-60.000000)
- Cocos2d: position = (15.000000,20.000000)
下面解释一下:我们添加了两个节点sprite1(node1),sprite2(node2)。
其中 : CCPoint point1 = sprite1->convertToNodeSpace(sprite2->getPosition());
相当于我们将sprite2这个节点添加到(实际没有添加,只是这样理解)sprite1这个节点上,那么就需要使用sprite1这个节点的节点坐标系统,这个节点的节点坐标系统的原点在(20,40),而sprite1的坐标是(-5,-20),那么经过变换之后,sprite1的坐标就是(-25,-60)。
其中 : CCPoint point2 = sprite1->convertToWorldSpace(sprite2->getPosition());
此时的变换是将sprite2的坐标转换到sprite1的世界坐标系下,而其中世界坐标系是没有变化的,始终都是和OpenGL等同,只不过sprite2在变换的时候将sprite1作为了”参照“而已。所以变换之后sprite2的坐标为:(15,20)
通俗一点理解就是,sprite2的坐标在sprite1节点左下角(原点)坐标的基础上加上sprite2的坐标(也即基于sprite1原点的平移,平移坐标就是sprite1的ccp(-5,-20))就可以得到sprite2变换后的坐标:(15,20)=sprite1原点坐标(20,40)+ 平移(-5,-20)
2、convertToNodeSpaceAR 和 convertToWorldSpaceAR
注意到这两个方法只不过增加了AR的约束,也即锚点。其意思是:修改变换的基准。这样理解:
convertToNodeSpaceAR,就是将节点坐标系的坐标原点修改在其锚点位置。(convertToNodeSpace 节点坐标系的原点位置是其左下角)
convertToWorldSpaceAR,就是在变换的时候将参照坐标修改到其锚点位置。(convertToWorldSpace 参照坐标是在其左下角)
下面给出一个例子说明一下这两个方法:
- CCSprite *sprite1 = CCSprite::create("CloseNormal.png");
- sprite1->setPosition(ccp(100,100));
- sprite1->setAnchorPoint(ccp(0.5,0.5));
- this->addChild(sprite1);
- CCSprite *sprite2 = CCSprite::create("CloseNormal.png");
- sprite2->setPosition(ccp(-5,-20));
- sprite2->setAnchorPoint(ccp(1,1));
- this->addChild(sprite2);
- CCPoint point3 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());
- CCPoint point4 = sprite1->convertToWorldSpaceAR(sprite2->getPosition());
- CCLog("position = (%f,%f)",point3.x,point3.y);
- CCLog("position = (%f,%f)",point4.x,point4.y);
运行结果:
- Cocos2d: position = (-105.000000,-120.000000)
- Cocos2d: position = (95.000000,80.000000)
sprite1节点的节点坐标系原点现在变成节点的锚点所在位置,在这个位置建立一个坐标系,那么就可以确定sprite2转换到这个节点坐标系所在的位置了。
而关于 convertToWorldSpaceAR 其实其变换和 convertToWorldSpace 是十分类似的,只不过其原点变成其节点的锚点,而不是左下角。
那么变换的过程: sprite2变换后的坐标(90,80) = sprite1的锚点(100,100) + 平移(-5,-20)。
http://blog.youkuaiyun.com/crayondeng/article/details/11936309
http://blog.youkuaiyun.com/o_longzhong/article/details/9749861
anchor point 究竟是怎么回事? 之所以造成不容易理解的是因为我们平时看待一个图片是 以图片的中心点 这一个维度来决定图片的位置的。
而在cocos2d中决定一个 图片的位置是由两个维度 一个是 position 另外一个是anchor point。只要我们搞清楚他们的关系,自然就迎刃而解。
默认情况下,anchor point在图片的中心位置(0.5, 0.5),取值在0到1之间的好处就是,锚点不会和具体物体的大小耦合,也即不用关注物件大小,
而应取其对应比率,如果把锚点改成(0,0),则进行放置位置时,以图片左下角作为起始点。
也就是说,把position设置成(x,y)时,画到屏幕上需要知道:到底图片上的哪个点放在屏幕的(x,y)上,而anchor point就是这个放置的点,
anchor point是可以超过图片边界的,比如下例中的(-1,-1),表示从超出图片左下角一个宽和一个高的地方放置到屏幕的(0,0)位置
(向右上偏移10个点才开始到图片的左下角,可以认为向右上偏移了10个点的空白区域)
他们的关系是这样的(假设actualPosition.x,actualPosition.y是真实图片位置的中点):
actualPosition.x = position.x + width*(0.5 - anchor_point.x); acturalPosition.y = position.y + height*(0.5 - anchor_point.y)
actualPosition 是sprite实际上在屏幕显示的位置, poistion是 程序设置的, achor_point也是程序设置的。
具体看下面的例子一:
- CCSprite
*sprite = [CCSprite spritewithFile:@"blackSquare.png"]; - sprite.position=ccp(0,0);
- sprite.anchorPoint=ccp(0,0);
- [self
addChild:sprite];
具体效果如下:
根据上面的公式: 假设精灵的width = height = 10.
actualPosition.x = 0 + 10*(0.5 - 0) = 5; actualPosition.y
(5, 5) 这个结果正是现在图片中心的在屏幕上的实际位置。
如果以左下角(Opengl坐标系的原点)为参照,就是把图片的原点锚在了屏幕坐标的原点上
例子 二:
- CCSprite
*sprite = [CCSprite spritewithFile:@"blackSquare.png"]; - sprite.position=ccp(0,0);
- sprite.anchorPoint=ccp(-1,-1);
- [self
addChild:sprite];
具体效果如下:
根据上面的公式: 假设精灵的width = height = 10.
坐标方向是opengl的方向(原点在左下角,x向左,y向上为正)
actualPosition.x = 0 + 10*(0.5 - (-1)) = 15; actualPosition.y
(15, 15) 这个结果正是现在图片中心的在屏幕上的实际位置。
如果以左下角(Opengl坐标系的原点)为参照,就是把图片的(-10,-10)这个点锚在了屏幕的原点(即图片的原点锚在了屏幕坐标的(10,10)上)
例子三
- CCSprite
*sprite = [CCSprite spritewithFile:@"blackSquare.png"]; - sprite.anchorPoint=ccp(1,1);
- sprite.position=ccp(sprite.contentSize.width
, sprite.contentSize.height); - [self
addChild:sprite];

根据上面的公式: 假设精灵的width = height = 10.
actualPosition.x = 10 + 10*(0.5 - (1)) = 5; actualPosition.y
(5, 5) 这个结果正是现在图片中心的在屏幕上的实际位置。
就是把图片的(10,10)这个点锚在了屏幕坐标的(10,10)上