cocos2dx的 坐标转换简单总结
在不同的应用场景中,Cocos2d-x 的坐标可能需要进行转换。本文将对 Cocos2d-x 的坐标转换进行简单总结。
一、Cocos2d-x 所遵循的坐标系
Cocos2d-x 基于 OpenGL ES 构建,因此遵循 OpenGL 的坐标系,该坐标系以屏幕的左下角为坐标原点,x 轴向右,y 轴向上。而屏幕坐标系(如苹果的 Quartz2D 使用)原点在屏幕左上角,x 轴向右,y 轴向下,iOS 的屏幕触摸事件 CCTouch 传入的位置信息使用的就是该坐标系。所以在 Cocos2d 中对触摸事件做出响应前,需要先把触摸点转化到 OpenGL 坐标系,可使用 CCDirector 的 convertToGL 来完成这一转化。
二、坐标转换的常见情况及方法
(一)从当前坐标点获取世界坐标点(屏幕坐标点,OpenGL 的坐标系)
直接使用 nodeParent->convertToWorldSpace(node->getPosition());
这里需要转换坐标对象的父类调用 convertToWorldSpace,参数是对象相对于父类的坐标点,返回的是屏幕坐标点。
(二)从当前坐标点获取相对于某个 CCNode 的坐标点
直接使用 node2->convertToNodeSpace(node1->getPosition());
这里 node2 不一定是 node1 的父类,此方法可让 node1 得到相对于 node2 坐标系的坐标点,返回的就是相对于 node2 坐标系的坐标点。
(三)考虑锚点的情况
上述调用未考虑 nodeParent 和 node2 的 anchorPoint(默认使用了 (0, 0) 的锚点)。若考虑锚点,则使用 convertToWorldSpaceAR() 和 convertToNodeSpaceAR()。
nodeParent->convertToWorldSpaceAR(node->getPosition()):由于默认锚点是 (0, 0),所以其得到的坐标点是ccpAdd(nodeParent->convertToWorldSpace(node->getPosition()), ccp(nodeParent->getContentSize().width * 0.5, nodeParent->getContentSize().height * 0.5))。node2->convertToNodeSpaceAR(node1->getPosition):同样因为默认锚点是 (0, 0),得到的坐标点是ccpSub(nodeParent->convertToWorldSpace(node->getPosition()), ccp(node2->getContentSize().width * 0.5, node2->getContentSize().height * 0.5))。
三、四个坐标转换方法详解
(一)相关概念
在理解 CCPoint convertToNodeSpace(const CCPoint& worldPoint);、CCPoint convertToWorldSpace(const CCPoint& nodePoint);、CCPoint convertToNodeSpaceAR(const CCPoint& worldPoint); 和 CCPoint convertToWorldSpaceAR(const CCPoint& nodePoint); 这四个方法之前,需要对世界坐标和本地坐标有一定的理解。
- 世界坐标系:也叫做绝对坐标系,Cocos2d 中的元素具有父子关系的层级结构,我们通过
CCNode的position设定元素的位置使用的是相对其父节点的本地坐标系,而非世界坐标系。在绘制屏幕时,Cocos2d 会把这些元素的本地坐标映射成世界坐标系坐标。世界坐标系和 OpenGL 坐标系一致,原点在屏幕左下角。 - 本地坐标系:也叫做物体坐标系,是和特定物体相关联的坐标系。每个物体都有独立的坐标系,当物体移动或改变方向时,和该物体关联的坐标系将随之移动或改变方向。例如,用 Cocos2d-x 创建一个矩形
colorLayer: CCRect(10, 10, 100, 100),此时的本地坐标系以 (10, 10) 为坐标原点,x 轴向右,y 轴向上。若创建一个CCSprite,锚点为 (0.5, 0.5),位置为 (100, 100),大小为 (40, 40),这时的本地坐标系以 (80, 80) 为坐标原点,x 轴向右,y 轴向上。总体而言,本地坐标系原点为node的左下角坐标。
(二)方法具体解析
convertToNodeSpace:调用CCPoint point = node1->convertToNodeSpace(node2->getPosition());可将node2的坐标转化成相对于node1的本地坐标。例如,若node1的锚点为 (0, 0),node2的锚点为 (1, 1),转化之后,node2的坐标可能变成 (-25, -60)。convertToWorldSpace:调用CCPoint point = node1->convertToWorldSpace(node2->getPosition());是将node2的坐标转化成相对于node1的世界坐标。具体操作是先将node1的坐标当做世界坐标,然后让node2的坐标位置重置成相对于node1的世界坐标,例如可能得到 (15, 20)。convertToNodeSpaceAR:该方法是把node1的坐标系原点设置在锚点的位置。若锚点是 (0, 0),则转化之后的坐标系位置和convertToNodeSpace一样,结果也相同。convertToWorldSpaceAR:原理与convertToNodeSpaceAR类似,若锚点为 (0, 0),其转化结果和convertToWorldSpace相同。
四、测试代码及结果分析
(一)第一次测试
CCSprite *sprite1 = CCSprite::spriteWithFile("CloseNormal.png");
sprite1->setPosition(ccp(20, 40));
sprite1->setAnchorPoint(ccp(0, 0));
this->addChild(sprite1);
CCSprite *sprite2 = CCSprite::spriteWithFile("CloseNormal.png");
sprite2->setPosition(ccp(-5, -20));
sprite2->setAnchorPoint(ccp(1, 1));
this->addChild(sprite2);
CCPoint point1 = sprite1->convertToNodeSpace(sprite2->getPosition());
CCPoint point2 = sprite1->convertToWorldSpace(sprite2->getPosition());
CCPoint point3 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());
CCPoint point4 = sprite1->convertToWorldSpaceAR(sprite2->getPosition());
CCLog("position = (%f, %f)", point1.x, point1.y);
CCLog("position = (%f, %f)", point2.x, point2.y);
CCLog("position = (%f, %f)", point3.x, point3.y);
CCLog("position = (%f, %f)", point4.x, point4.y);
运行结果:
position = (-25.000000, -60.000000)
position = (15.000000, 20.000000)
position = (-25.000000, -60.000000)
position = (15.000000, 20.000000)
结果与预期相符。
(二)第二次测试
sprite1->setAnchorPoint(ccp(0.5, 0.5));
sprite1->setPosition(ccp(100, 100));
CCPoint point5 = sprite1->convertToNodeSpaceAR(sprite2->getPosition());
CCPoint point6 = sprite1->convertToWorldSpaceAR(sprite2->getPosition());
CCLog("position = (%f, %f)", point5.x, point5.y);
CCLog("position = (%f, %f)", point6.x, point5.y);
运算结果:
size = (40.000000, 40.000000)
position = (-105.000000, -120.000000)
position = (95.000000, 80.000000)
分析:重置 sprite1 的坐标为 (100, 100),锚点为 (0.5, 0.5),对于 convertToNodeSpaceAR 和 convertToWorldSpaceAR 这两个方法,其坐标系原点为 (100, 100)。所以用 convertToNodeSpaceAR 转化之后的坐标为 (-105, -120),用 convertToWorldSpaceAR 转化之后的坐标为 (95, 80),与运算结果一致。
综上所述,通过对上述坐标转换方法的理解和测试,我们可以在 Cocos2d-x 开发中灵活运用坐标转换,以满足不同的开发需求。