注意:
当一个节点有一个子节点的时候,如果移动父节点,子节点也会跟着做相应的移动变化,只要被添加到父节点中,子节点就被绑定了,所以子节点的位置,坐标就会被动地变化。
当一个节点有一个子节点的时候,如果通过setPosition方法设置子节点的坐标,那么,这时候其实是在以父节点左下角为原点的坐标系中,x轴最长是父节点的宽度,y轴最长是父节点的高度。后面如果有需要可以通过一些坐标的转换的方法转化为世界坐标/UI坐标。
例如,jewel1是hero的子节点,屏幕分辨率960*640,hero大小200*250
Point p1, p2,p3;
auto hero = Sprite::create("s_1.png");
hero->setPosition(visibleSize.width / 2, visibleSize.height / 2);
p1=hero->getPosition();
log("p1=(%f,%f)",p1.x,p1.y);
addChild(hero);
auto jewel1 = Sprite::create("jewel1.png");
jewel1->setAnchorPoint(Vec2(1, 0));
jewel1->setPosition(hero->getContentSize().width, 0);
p2 = jewel1->getPosition();
log("p2=(%f,%f)", p2.x, p2.y);
p3 = hero->convertToWorldSpace(jewel1->getPosition());
log("p3=(%f,%f)", p3.x, p3.y);
hero->addChild(jewel1);
输出结果
p1=(480.000000,320.000000)
p2=(200.000000,0.000000)
p3=(580.000000,195.000000)
一.UI坐标系
在进行iOS或者Android界面开发时,它的坐标系规则如下图所示:
- 原点坐标(x=0, y=0)位于左上角;
- X轴从屏幕最左边开始,由左向右渐增;
- Y轴坐标从屏幕最上方开始,由上向下渐增
二.Cocos2d-x坐标系
Cocos2d-x坐标系是这里的重点,也是我们开发时考虑的最多的。由于Cocos2d-x是基于OpenGL和OpenGL ES的。该坐标系的规则如下:
- 原点坐标(x=0, y=0)位于左下角;
- X轴从屏幕最左边开始,由左向右渐增;
- Y轴从屏幕最下方开始,由下向上渐增;
在Cocos2d-x中的场景,就是使用的该坐标系。
三.世界坐标系
世界坐标系也叫绝对坐标系,是游戏开发中建立的概念。它建立了描述其它坐标系所需要的参考标准。我们都可以使用世界坐标系来描述其它坐标系的位置。
Cocos2d-x中元素是有父子关系的层次结构,通过Node
设置位置使用的是相对其父节点的本地坐标系,而非世界坐标系,最后在绘制屏幕的时候,Cocos2d-x会把这些元素的本地节点坐标映射成世界坐标系坐标。世界坐标系和OpenGL坐标系方向一致,原点在屏幕左下角,X轴向右,Y轴向上。
四.节点坐标系
节点坐标系也叫相对坐标系,它是与特定节点相关联的坐标系。每个节点都有独立的坐标系,当节点移动或改变方向时,和该节点关联的坐标系(它的子节点)将随之移动或改变方向。比如一个Layer上面有10个精灵,当移动这个Layer的时候,这些精灵也会跟着一起移动的。
在Node
节点类中,我们可以对节点进行位置的操作,而这些设置位置使用的就是父节点的节点坐标系。它和OpenGL坐标系方向一致,原点在屏幕左下角,X轴向右,Y轴向上。有的时候,我们需要将节点坐标转换成世界坐标,或者将世界坐标转换成节点坐标。在Node
节点类中,就提供了对应的转换函数,下面我们就使用一下这些函数,加深对Cocos2d-x坐标系、世界坐标系和节点坐标系的理解。
转换API
在Cocos2d-x中提供了以下的API用来进行坐标转换。
/**
* 将世界坐标转换成节点坐标,忽略锚点的影响;结果是以点为单位。
*/
Vec2 convertToNodeSpace(const Vec2& worldPoint) const;
/**
* 将节点坐标转换成世界坐标,忽略锚点的影响;结果是以点为单位。
*/
Vec2 convertToWorldSpace(const Vec2& nodePoint) const;
/**
* 将世界坐标转换成节点坐标;结果是以点为单位。
* 会考虑到锚点的影响
*/
Vec2 convertToNodeSpaceAR(const Vec2& worldPoint) const;
/**
* 将节点坐标转换成世界坐标;结果是以点为单位。
* 会考虑到锚点的影响。
*/
Vec2 convertToWorldSpaceAR(const Vec2& nodePoint) const;
/**
* 将Touch对应的点转换成节点坐标,忽略锚点的影响。
*/
Vec2 convertTouchToNodeSpace(Touch * touch) const;
/**
* 将Touch对应的点转换成节点坐标,考虑锚点的影响。
*/
Vec2 convertTouchToNodeSpaceAR(Touch * touch) const;
好了,世界坐标系转节点坐标系,节点坐标系转世界坐标系,就这么几个函数就能搞定了,剩下的就是实际的应用了。对了,在实际中,一定要考虑到锚点的影响,可能你得到的结果,就是因为锚点的影响,而完全不同的。
坐标系变换原理
上面总结了坐标系之间转换的一些API函数,下面就来看看它们之间到底是如何转换的。看了网上很多人的博客,写的转换原理,写的都不错,就是看的云里雾里的,很多人都配上了坐标图,搞笑的是那些坐标图都是“一副”,也不知道谁抄袭的谁的。
下面就做一些简单的原理,没有过的图来说明,就是一些简短的文字,按照这些文字说明,你肯定能看的懂的。
1.convertToNodeSpace
Vec2 newPosition = node1->convertToNodeSpace(node2->getPosition());
将node2的位置坐标转换成相对于node1左下角顶点的坐标。转换方法:node1和node2位置不变,将坐标轴原点设置为node1的左下角顶点,重新计算node2->getPosition()
这个点的坐标即为newPosition。
当调用以下代码时,返回的是相对于其父节点的节点坐标,当然了,以下代码的实际用处并不大。
Vec2 newPosition = node1->convertToNodeSpace(node1->getPosition());
2.convertToNodeSpaceAR
Vec2 newPosition=node1->convertToNodeSpaceAR(node2->getPosition());
将node2的位置坐标转换成相对于node1锚点的坐标。转换方法:node1和node2位置不变,将坐标轴原点设置为node1的锚点,重新计算node2->getPosition()
这个点的坐标即为newPosition。
3.convertToWorldSpace
Vec2 newPosition=node1->convertToWorldSpace(node2->getPosition());
将node2的位置坐标转换成世界坐标。转换方法:node1的位置不变,世界坐标的坐标轴也不变,以node1的左下角顶点再建立一个坐标系(其实就是本地坐标),将node2->getPosition()
这个点设置到新建的坐标系中,以原来的世界坐标系为参考,重新计算node2->getPosition()
这个点的坐标即为newPosition。
4.convertToWorldSpaceAR
Vec2 newPosition=node1->convertToWorldSpaceAR(node2->getPosition());
将node2的位置坐标转换成世界坐标。转换方法:node1的位置不变,世界坐标的坐标轴也不变,以node1的锚点再建立一个坐标系,将node2->getPosition()
这个点设置到新建的坐标系中,以原来的世界坐标系为参考,重新计算node2->getPosition()
这个点的坐标即为newPosition。
另一个例子
首先我们添加两个测试精灵(宽:27,高:40)到场景里面:
Sprite *sprite1 = Sprite::create("player.png");
sprite1->setPosition(Vec2(20, 40));
sprite1->setAnchorPoint(Vec2(0, 0));
this->addChild(sprite1);
Sprite *sprite2 = Sprite::create("player.png");
sprite2->setPosition(Vec2(-15, -30));
sprite2->setAnchorPoint(Vec2(1, 1));
this->addChild(sprite2);
然后调试,在场景中大概是下图这样显示(以左下角为坐标原点,从左到右为x方向,从下到上为y方向,废话了:)):
在cocos2d-x中,每个精灵都有一个锚点,以后对精灵的操作(比如旋转)都会围绕锚点进行,我们暂且可以看作是精灵的中心位置,一般来说有每个方向有三种可能的值:0,0.5,1。上图中红色圆点即为各自的锚点,sprite1 锚点为 (0,0) 左下角,sprite2锚点为(1,1)在右上角。
现在我们来看看坐标系转换,同样地,我们先写点测试代码:
Point p1 = sprite2->convertToNodeSpace(sprite1->getPosition());
Point p2 = sprite2->convertToWorldSpace(sprite1->getPosition());
Point p3 = sprite2->convertToNodeSpaceAR(sprite1->getPosition());
Point p4 = sprite2->convertToWorldSpaceAR(sprite1->getPosition());
Log("p1:%f,%f", p1.x, p1.y);
Log("p2:%f,%f", p2.x, p2.y);
Log("p3:%f,%f", p3.x, p3.y);
Log("p4:%f,%f", p4.x, p4.y);
由于cocos2d-x的坐标系(本地坐标系)是以左下角为坐标原点的,所以 sprite1和sprite2的坐标原点在上图的位置分别是(20,40)、(-42,-70),那么很明显的:
p1就是sprite1锚点相对于sprite2原点来说在sprite2坐标系中的位置,经过对比上图,我们可以得到(20-(-42),40-(-70))即(62,110)
p2就是sprite1锚点相对于sprite2原点来说在上图坐标系中的位置,这样我们可以计算出sprite1在sprite2坐标系中的位置:(20+(-42),40+(-70)),即(-22,-30)
p3就是sprite1锚点相对于sprite2锚点来说在sprite2坐标系中的位置,也就是(20-(-15),40-(-30)),即(35,70)
p4就是sprite1锚点相对于sprite2锚点来说在上图坐标系中的位置,也就是(20+(-15),40+(-30)),即(5,10)
现在我们可以知道,计算方法都是用sprite1的坐标去加减sprite2的坐标,针对本地坐标系就用减法,针对世界坐标系就用加法。