convetToWorldSpace,convetToNodeSpace等坐标变换函数的理解

发布于 2018-03-23  约 12 分钟

首先必须得理解锚点是啥


参考了《cocos2d-x权威指南》,有一些说法改成了我自己比较理解的话。

锚点的出现就是为sprite图片精灵的服务的,锚点指定了精灵上和其在父节点坐标系中设置的位置重合的点的位置(原话是:锚点指定了精灵上和所在节点原点(也就是设置位置的点)重合的点。),因此只有在Node节点类使用贴图的情况下,锚点才有意义。

在Cocos2d - x中CCLayer的Anchor Point为默认值(0, 0),其他Node的默认值为(0.5, 0.5),表示的是一个乘数因子。其表示锚点位于贴图长度乘0.5和宽度乘0.5即贴图中心。

改变锚点的值并不会改变节点的位置,变化的只是贴图相对于设置的相对位置,相当于在移动节点里的贴图,而非节点本身。

注意!!再理解一次:子节点添加到父节点时,参考的是父节点左下角的坐标原点而非锚点,改变某个节点中的锚点并不影响此节点的原点位置。但子节点在父节点中的位置与子节点的锚点有关,即子节点锚点与父节点原点重合。还没理解的话一定要写程序!!将一个节点添加到父节点里时,需要设置其在父节点上的位置,本质上是设置节点的锚点在父坐标系上的位置。

convertToWorldSpace转化为世界坐标


Vec2 convertToWorldSpace(const Vec2 &nodePoint)    const

Converts a Vec2 to world space coordinates.
The result is in Points.
Parameters
nodePoint A given coordinate.
Returns
:A point in world space coordinates.

cocos的文档说明写得太简洁了,看完后脑子还很糊。
首先得弄明白到底是对节点对象的坐标还是对函数传入参数代表的坐标进行转化。一般用此成员函数将传入的坐标通过某个节点坐标转化到世界坐标系中。假设已经清楚了锚点的作用了(好吧,不清楚再回过头看一下上面)。下面用4组测试对这个函数进行详细分析。

准备工作,创建两个图片精灵A、B,初始的尺寸,位置和锚点如下设置

auto A = Sprite::create("red.png");
auto B = Sprite::create("green.png");
 
A->setContentSize(Size(300,200));
B->setContentSize(Size(200,100));
 
A->setAnchorPoint(Vec2(0,0));
B->setAnchorPoint(Vec2(1,1));
 
A->setPosition(300,300);
B->setPosition(200,200);

clipboard.png

测试1:B-->convertToWorldSpace(Vec2(10,10))

这里没用到节点A),传入Vec2(10,10)进行转换.

Vec2 position1 = B->convertToWorldSpace(Vec2(10,10));
log("World Position = (%f,%f)",position1.x,position1.y);

将B的锚点设置为(1,1)的情况:
输出World Position = (10.000000,110.000000)
将B的锚点设置为(0,0)的情况:
输出World Position = (210.000000,210.000000)

第一种情况,(再次强调节点坐标系原点坐标一直在左下角,不会变!),B节点原点在世界坐标系中是(0,100),(0,100)+(10,10)=(10,110)。第二种情况,B的原点在世界坐标系中是(200,200),与上述同理相加即可得到结果。

为何是相加呢?把这个函数做个分解:①传入想转变的坐标,②借助某个节点坐标系,把传入坐标放到B的节点坐标系中,就是以B左下角为原点的坐标系。③转换到世界坐标系中,传入的坐标在B的节点坐标系就是他自己传入的坐标,在放大到世界坐标系,那就是这个坐标再加上B原点在世界坐标系中的坐标就是传入坐标在世界坐标系中的坐标,把这个过程画个图就明白了!

第一步传入的坐标可以像上述直接一个点,也可以是通过某个节点得到的坐标,例如A-->getPosition()返回的坐标(我看到很多博主在说明通过传入这种坐标时都要将两个节点揉在一起,而且各有解释。比如A的锚点相对于B的原点进行转化;A通过B来平移等,看得头都大了,最终我还是准备自己解释,请看下面)。

测试2:B-->convertToWorldSpace(A-->getPosition()),B与A是兄弟节点

Vec2 position1 = B->convertToWorldSpace(A->getPosition());

B锚点设置(1,1),A锚点设置(0,0)的情况:
输出World Position = (300.000000,400.000000)
B锚点不变,A锚点改成(1,1)的情况:
输出World Position = (300.000000,400.000000)
B锚点改成(0,0),A锚点仍然为(0,0)的情况:
输出World Position = (500.000000,500.000000)

举的例子中,A、B节点的父节点就是场景节点,其坐标系与世界坐标系重合。

我们看到改变参照节点A的锚点并没有影响,而改变B的锚点结果出现了变化。由于我们传入的只是一个Vec2类型的量,虽然是通过A节点获取到的,但其实和A节点的关系并不大。因为A节点在其父节点中的位置一开始就已确定:(200,200),而我们之前说过锚点是与在父节点中设置的位置重合的点,无论怎样改变锚点,也不可能改变这个位置。

别人的说法①:可以把这个变换看作是A的锚点相对于B的原点在世界坐标系中的位置。这个说法欠妥,如果转换的关系是这样的话,结果应该是B节点的原点与A节点锚点的相对位置,那么就是A节点锚点所在位置的坐标减去B节点原点所在位置的结果。上述情况,B的原点在(0,100),A节点锚点在(300,300),输出结果是(300,400),显然是相减而非相加。第二、三种情况同理。

别人的说法②:点可以看成向量,B节点坐标系是其父节点坐标系位移那么多量得来。而A参照了B节点,进行同样的位移,也就是在A向量+B位置。结果就是位移后的结果。这个解释可以。不过我们不妨就把传入的参数看成一个点吧,而不要联系到两个节点上(如果这样的理解有问题的话,欢迎指正!)。

测试3:B-->convertToWorldSpace(A-->getPosition()),B是A的孩子节点

auto A = Sprite::create("red.png");
auto B = Sprite::create("green.png");
 
A->setContentSize(Size(300,200));
B->setContentSize(Size(200,100));
 
A->setAnchorPoint(Vec2(0,0));
B->setAnchorPoint(Vec2(1,1));
 
A->setPosition(300,300);
B->setPosition(200,200);
this->addChild(A, 0);
A->addChild(B);
Vec2 position1 = B->convertToWorldSpace(Vec2(0,0));
log("World Position = (%f,%f)",position1.x,position1.y);

结果: World Position = (300.000000,400.000000)

因为B是A的子节点,以A左下角原点(300,300)设置B的位置(200,200),B锚点设置是(1,1),所以相对于节点A的节点坐标系B的原点坐标在(0,100),但在世界坐标系是即(300,300)+(0,100)=(300,400),传入点是(0,0),相当于没加,所以最后结果(0,0)+(300,400)=(300,400)

Vec2 position1 = B->convertToWorldSpace(A->getPosition());

结果:B Node World Position = (600.000000,700.000000)

上面计算过了,B原点位置在世界坐标系中是(300,400),由于获取的AgetPosition()得到的坐标就是刚开始相对于其父节点所设置的坐标(300,300),那么结果就是这两个的和。

改变锚点的情况与测试2同理,只要照着测试1中说的那样去思考,无论是A还是B的锚点改变都能很容易计算。

测试4:B-->convertToWorldSpace(A-->getPosition()),A是B的孩子节点

this->addChild(B, 0);
B->addChild(A, 0);
Vec2 position1 = B->convertToWorldSpace(origin);
log("World Position = (%f,%f)",position1.x,position1.y);

结果World Position = (0.000000,100.000000)

B是A的父节点,B的原点在世界坐标系中坐标为(0,100),后面不再赘述。

Vec2 position1 = B->convertToWorldSpace(A->getPosition());

结果B Node World Position = (300.000000,400.000000)

B的原点在世界坐标系中为(0,100),A是B的子节点,以B的节点坐标系设置坐标(300,300),且B的锚点设置是(0,0),那么B的锚点坐标和其原点重合,AgetPosition()得到的位置就是相对于其父节点所设置的坐标(300,300),最后结果在世界坐标系中A的原点的坐标(0,100)+(300,300)= (400,400)

改变锚点的情况不再赘述。

convertToNodeSpace转化为节点坐标


Vec2 convertToNodeSpace(const Vec2 &worldPoint)    const

Converts a Vec2 to node (local) space coordinates.
The result is in Points.
Parameters
worldPoint A given coordinate.
Returns
:A point in node (local) space coordinates.

这个函数是将传入的坐标转化到某个节点的节点坐标系。理解这个函数就是以参考的节点坐标左下角为原点画坐标系,观察传入点的相对位置,就能知道是传入点坐标减去节点原点世界坐标系坐标。仍然可以分步理解。①传入一个节点,怎么传入都行,只要是Vec2类型的,②通过世界坐标系比较传入点和节点原点坐标的相对位置。③转化到节点坐标系,以节点原点画坐标,得到传入点在节点坐标系中的位置。

auto A = Sprite::create("red.png");
auto B = Sprite::create("green.png");
 
A->setContentSize(Size(300,200));
B->setContentSize(Size(200,100));
 
A->setAnchorPoint(Vec2(0,0));
B->setAnchorPoint(Vec2(1,1));
 
A->setPosition(300,300);
B->setPosition(200,200);
this->addChild(B, 0);
this->addChild(A, 0);
Vec2 position1 = B->convertToNodeSpace(Vec2(10,10));
log("Node Position = (%f,%f)",position1.x,position1.y);

结果:Node Position = (10.000000,-90.000000)
这里没有涉及A节点,忽略A。

B的原点坐标在世界坐标系中的位置是(0,100),传入坐标(10,10),(10,10)-(0,100)=(10,-90),相减的原因按照上述分解画个图就能明白。
改变锚点和父子节点只要理解了为什么相减,过程都与convertToWorldSpace一样。

convertToWorldSpaceAR与convertToNodeSpaceAR


只是把参考节点原点改成了节点锚点,如果真的理解了上面的内容,理解这两个易如反掌。故不再赘述。

阅读 1.3k发布于 2018-03-23

推荐阅读
COCOS2D-X学习笔记
用户专栏

正在学习cocos这个开源游戏引擎,学习过程中碰到了不少问题。网上搜索的答案很多,自己动手敲悟出的才会...

0 人关注
1 篇文章
专栏主页
目录