Cocos2d-x 坐标系统详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wlk1229/article/details/82597814

Cocos2d-x 坐标系统详解

声明:本文使用的是cocos2d-x-3.17的代码

本文将详细介绍Cocos2d中的坐标系统,分析坐标系统中的坐标转换。Cocos2d-x 3.X支持3D功能,Cocos2d中的坐标系统也是3维,如果想彻底理解Cocs2d的坐标系统,需要一些3D图形学的知识,需要理解3D系统中的坐标转换。

 

全局坐标与局部坐标

2d坐标系统是3D坐标系统的一个子集,在Cocos2d中默认情况下使用2d在窗口上绘制时,使用的3D系统中z=0.0的平面。2d坐标因为系统更加直观,这里使用2d坐标系统讲解。

 

窗口坐标

Cocos2d中的绘制窗口左下角为原点,向上为Y轴正方向,右边为Y轴负方向,如下图:

窗口坐标系统我们称之为全局坐标系统也就是代码中的wordplase。有全局坐标系统,也会有局部坐标系,对于每个节点也都会有一个局部坐标系,这里的节点是指继承至Node类的节点,Cocos2d中的位置以及节点坐标转换都实现在Node类中。节点的局部坐标系统也是左下角为原点,向上为Y轴正方向,右边为Y轴负方向,如下图:

 

锚点

需要注意的是锚点并不是节点局部坐标系的原点。节点都会有一个锚点,使用函数Node::setPosition设置节点坐标时,设置的位置是子节点锚点在父节点局部坐标系统中的位置。节点的旋转,缩放,倾斜都是围绕锚点进行变换的。锚点坐标采用的是[0,1]的规范化坐标,值代表的是节点宽高的百分百。 默认情况下大部分节类的锚点都在节点显示内容的中心,即(0.5, 0.5),具体局部坐标的位置为getContentSize()/2,也有例外如:Camera和DrawNode锚点都是(0, 0)。

 

当节点绕着节点进行旋转、缩放,倾斜后,其局部坐标系也会有相应的变换,如下图,对节点顺时针旋转60度:

 

 

3D坐标系统

3D坐标系统相对于2D坐标系统增加了z轴,Cocos2d中的默认情况下z轴正方向从屏幕向外射出,是一个右手坐标系。

http://www.cocos.com/docs/native/v3/coordinate-system/res/RightHand.png

对于每个节点的局部坐标,也都可以扩展为一个3D右手坐标系统。

 

 

坐标转换

Cocos2d中经常需要用到将局部坐标变换到全局坐标,或者全局坐标变换到局部坐标,这些坐标转换都是通过矩阵来实现的,具体原理可以在3D编程中学习,书籍《3D数学基础:图形与游戏开发》有详细讲解。

 

坐标系与坐标变换

矩阵变换都是针对点进行变换的,并没有对坐标系进行变换,如果是坐标系变换,需要对坐标系内所有点进行一个反向变换。原理:当需要将点 (x, y)移动到点(x’, y’)时,我们可以有两种方法:一种时在当前坐标系下直接移动点,另一种时移动坐标系,如下图:

从图中可以看出移动点和移动坐标系的方向刚好时相反的。所以如果是坐标系变换,实际上是对坐标系内点进行反向变换。

 

父节点到子节点局域坐标转换

在父节点局域坐标系下有一个点(x, y),现在要求该店在字节点局域坐标系下的值(x’, y’)。如下图,有一个场景Sense类,场景中有一个点精灵,且这个点精灵顺时针旋转了90度。

黄点的在场景坐标系中第地址为(x, y),现在要求在精灵坐标系下的坐标值(x’, y’)。

这个过程实际是一个坐标系转换的过程,相当于把黄点所在的场景坐标系转换成精灵的坐标系。整个转换过程需要分成三个步骤,因为点是绕锚点旋转的,所以第一步骤是将坐标系移动到锚点,第二步坐标系绕锚点旋转90度,第三部将坐标原点移动到精灵坐标的原点,三个步骤如下图:

这三个转换步骤需要使用三个矩阵,转换步骤代码如下:

Mat4 t1, t2, t3;
//移动坐标系到锚点,sp->getPosition为锚点在父节点的坐标
Mat4::createTranslation(-sp->getPositionX(), -sp->getPositionY(), -sp->getPositionZ(), &t1);

//旋转坐标
Mat4::createRotation(sp->getRotationQuat(), &t2);
t2.inverse();//创建的矩阵是对点进行sp坐标系下点的旋转,坐标系需要逆向变换

//将原点移动到sp坐标系原点
Mat4::createTranslation(sp->getAnchorPointInPoints().x, sp->getAnchorPointInPoints().y, sp->getPositionZ(), &t3);

Vec3 vc1(x, y, 0), vc2, vc3, vc4;
t1.transformPoint(vc1, &vc2); //移动坐标系到锚点
t2.transformPoint(vc2, &vc3); //旋转坐标
t3.transformPoint(vc3, &vc4); //将原点移动到sp坐标系原点

 

子节点到父节点局域坐标转换

这个跟父节点到子节点局域坐标转换是一个相反的过程,

第一步骤是将精灵坐标系移动到锚点,第二步坐标系绕锚点逆向旋转,第三部将坐标原点移动到场景坐标系的原点。函数Node::getNodeToParentTransform()也是按这三个步骤进行转换的,只是第二步不光有旋转,还有缩放,倾斜等转换。

如下图所示:

代码如下:

Mat4 transform1, transform2, transform3;
//移动sp坐标系到锚点
Mat4::createTranslation(-sp->getAnchorPointInPoints().x, -sp->getAnchorPointInPoints().y, sp->getPositionZ(), &transform1);
//旋转坐标系
Mat4::createRotation(sp->getRotationQuat(), &transform2);
//移动坐标系到与父节点原点
Mat4::createTranslation(sp->getPositionX(), sp->getPositionY(), sp->getPositionZ(), &transform3);

Vec3 v3D4(x,y, sp->getPositionZ()), vl1, vl2, vl3;
transform1.transformPoint(v3D4, &vl1);
transform2.transformPoint(vl1, &vl2);
transform3.transformPoint(vl2, &vl3);

 

全局坐标与局部坐标转换

节点局部坐标要转换成全局坐标需要将坐标转换到父节点坐标系坐标,然后将父节点下的坐标转换到父节点的父节点坐标,依次这样转换直到没有父节点为止。

函数Node::getNodeToParentTransform(Node* ancestor),参数ancestor父节点为 null 时获取的转换矩阵为子节点坐标系转换到全局坐标系的矩阵。以下是代码:

Mat4 Node::getNodeToParentTransform(Node* ancestor) const
{
    Mat4 t(this->getNodeToParentTransform());

    for (Node *p = _parent;  p != nullptr && p != ancestor ; p = p->getParent())
    {
        t = p->getNodeToParentTransform() * t;
    }

    return t;
}

 

 

 

猜你喜欢

转载自blog.csdn.net/wlk1229/article/details/82597814
今日推荐