• cocos子节点转父节点坐标 原理浅析(局部坐标转世界坐标同理)


    在CCNode的类中,有一个得到 一个节点坐标系转换父亲坐标系的一个矩阵,节点内坐标乘以这个矩阵,就可以转换为在节点父节点中的坐标,方法名为:

    Mat4& Node::getNodeToParentTransform()

    现在简单分析一下转换原理:

    /*
     得到节点坐标系转换到父亲的坐标系的 矩阵
     某个点(在本地也就是节点坐标) 乘以这个矩阵,就得到自己在父亲节点下的坐标,嵌套坐标
     举例子:LayerA 添加 LayerB,B的原点坐标也就是左下角坐标为10,10,也就是这一点在自身坐标下为0,0,在LayerA下
     就为10,10,这个矩阵的作用是Mat*(0,0)=(10,10),再比如在B下有一点是10,10,那么在没有旋转和缩放的前提下,在LayerA中的位置应该为20,20 ,Mat*(10,10)=(20,20)
     所以这个矩阵叫做 转换 子节点坐标到父节点坐标的 转换矩阵
     */
    const Mat4& Node::getNodeToParentTransform() const
    {
        /*
         
          先 缩放,在旋转,最后平移,
         详细可以看3d数学基础 8.3,解释了缩放矩阵,旋转矩阵,平移矩阵,然后相乘
         但是这里注意的是(3d数学是行向量,也就是 X * S * R *T,顺序执行),
         而cocos 是列向量 是 T * R * S * X,所以计算矩阵的时候要倒着乘,
         
         */
        
        if (_transformDirty)
        {
            // Translate values
            float x = _position.x;
            float y = _position.y;
            float z = _positionZ;
            /*
             旋转和缩放都是以锚点为中心,所以这里要计算出锚点
             1 如果忽略锚点,说明x,y 指的是 左下角坐标,因为计算的时候要以锚点为准,所以需要加上anchorPointInPoints
             2 如果没有忽略锚点,说明x,y指的就是锚点,直接往下计算就可以了
             */
            if (_ignoreAnchorPointForPosition)//,加上锚点的坐标,计算缩放和旋转,以锚点为中心计算,
            {
                x += _anchorPointInPoints.x;//_anchorPointInPoints为物体左下角和锚点之间的距离
                y += _anchorPointInPoints.y;
            }
           //计算出cos和sin,这里是4个弦组成的 旋转矩阵,radians为度数,绕着旋转的点是锚点
            float cx = 1, sx = 0, cy = 1, sy = 0;
            if (_rotationZ_X || _rotationZ_Y)// 2D这两个值始终是相等的,暂时不考虑3D
            {
                //右手坐标系,所以如果旋转45,那么弧度是负,顺时针为负
                float radiansX = -CC_DEGREES_TO_RADIANS(_rotationZ_X);
                float radiansY = -CC_DEGREES_TO_RADIANS(_rotationZ_Y);
                cx = cosf(radiansX);
                sx = sinf(radiansX);
                cy = cosf(radiansY);
                sy = sinf(radiansY);
            }
    
            bool needsSkewMatrix = ( _skewX || _skewY );//是否切面
           
            /*
             这里这么做的原因,因为 旋转和缩放是以锚点为中心,但是平移量是以原点距离父节点原点为准,也就是需要知道
             原点的坐标,也就是左下角的坐标,因为涉及到缩放,所以锚点坐标要程序scale,得到新的左下角坐标距离锚点的位置
             ,比如锚点坐标为30,30(本地坐标),也就是左下角距离锚点30,30,scale=0.5的时候,变为 左下角距离锚点
             15,15
             */
            Vec2 anchorPoint;
            anchorPoint.x = _anchorPointInPoints.x * _scaleX;
            anchorPoint.y = _anchorPointInPoints.y * _scaleY;
    
             /*
             如果锚点不是左下角,那么需要通过下面的计算得出旋转之后的左下角的坐标。
             锚点为旋转点,那么左下角距离原点的坐标成为了负数,也就是-30,-30,的出来的结果是相对于
             锚点的偏移量,所以下面
             anchorPoint要加一个负号,通过-anchorPoint和旋转矩阵想乘,得到了新的左下角的相对于锚点的偏移量
             而x,y为锚点的世界坐标(这里是指父节点中的坐标),(也就是到 ‘原点’的距离),加上相对于锚点为中心的旋转后的左下角坐标的偏移量,得到新的(想象一个如果锚点在左下角,就好理解了,一个意思)
             左下角的世界坐标(在父节点中的坐标)
             */
            //如果不是切变 并且锚点不在左下角,计算出左下角的父节点坐标
            //如果是切变,往后会有处理
            //其实这里是在mat矩阵形成前,先求出了左下角的x,y,其实如果不在这求出
            //先求出 mat矩阵,这个矩阵是集缩放和旋转一体的,然后anchorPointInPoints(初始左下角坐标,相对于锚点)与这个矩阵想乘
            //仍然可以求出变换之后的左下角坐标,后面的切变求偏移就是这么计算的,
            //因为求切变的时候,最终的矩阵集 切变,缩放,旋转矩阵为一体了,就差一个平移了,然后anchorPointInPoints
            //与这个矩阵想乘,然后再被锚点坐标相加,得出来父节点坐标下的左下角最终坐标,并把这个值作为平移值
            if (! needsSkewMatrix && !_anchorPointInPoints.equals(Vec2::ZERO))
            {
               float xx=  cy * -anchorPoint.x + -sx * -anchorPoint.y;
                x +=xx;
                float yy=sy * -anchorPoint.x +  cx * -anchorPoint.y;
                y += yy;
               
            }
          //先缩放,在旋转,在平移的组合矩阵
            // T * R * S *坐标,顺序别错了
            // 这个矩阵* 本地坐标,得到就是相对于父节点的坐标(相当于本地坐标转世界坐标,父节点相当于世界了)
            float mat[] = {
                            cy * _scaleX,   sy * _scaleX,   0,          0,
                            -sx * _scaleY,  cx * _scaleY,   0,          0,
                            0,              0,              _scaleZ,    0,
                            x,              y,              z,          1 };
            
            _transform.set(mat);//应该是更新之后的矩阵
    
            
            
            //*************关于3d的东西,忽略*************//
            if(!_ignoreAnchorPointForPosition)
            {
                _transform.translate(anchorPoint.x, anchorPoint.y, 0);//
            }
            
            //下面是绕 Y轴或X轴旋转,一般是3d的时候,2d的时候就是围绕点旋转,不考虑
            // XXX
            // FIX ME: Expensive operation.
            // FIX ME: It should be done together with the rotationZ
            if(_rotationY) {
                Mat4 rotY;
                Mat4::createRotationY(CC_DEGREES_TO_RADIANS(_rotationY), &rotY);
                _transform = _transform * rotY;
            }
            if(_rotationX) {
                Mat4 rotX;
                Mat4::createRotationX(CC_DEGREES_TO_RADIANS(_rotationX), &rotX);
                _transform = _transform * rotX;
            }
            
            if(!_ignoreAnchorPointForPosition)
            {
                _transform.translate(-anchorPoint.x, -anchorPoint.y, 0);
            }
            //*************关于3d的东西,先不看 结束*************//
            
           //如果是切变,那么此时的x,y还是锚点坐标
            if (needsSkewMatrix)
            {
                //这个矩阵的意思是 坐标X根据坐标Y被切变,坐标Y根据坐标X被切变,Z轴忽略,只考虑2d
                //
                Mat4 skewMatrix(1, (float)tanf(CC_DEGREES_TO_RADIANS(_skewY)), 0, 0,
                                  (float)tanf(CC_DEGREES_TO_RADIANS(_skewX)), 1, 0, 0,
                                  0,  0,  1, 0,
                                  0,  0,  0, 1);
                 //这里的顺序是_transform * skewMatrix,而不是相反,本地坐标先被切变得到新的本地坐标,然后在
                // 乘以缩放旋转平移矩阵
                _transform = _transform * skewMatrix;
    
                // adjust anchor point
                //和旋转的原理是一样的,12,13指的是 x,y原点距离世界坐标原点的值,因为之前的计算都是围绕锚点展开的
                //所以此时的x,y是指锚点世界坐标,需要转换成左下角原点,算法上面说过,和旋转一样
    //0 1 4 5代表的是 第一列的前两个和第二列的前两个值,缩放和旋转也是影响的这几个值(只考虑2d情况)
    if (!_anchorPointInPoints.equals(Vec2::ZERO)) { // XXX: Argh, Mat4 needs a "translate" method. // XXX: Although this is faster than multiplying a vec4 * mat4 _transform.m[12] += _transform.m[0] * -_anchorPointInPoints.x + _transform.m[4] * -_anchorPointInPoints.y; _transform.m[13] += _transform.m[1] * -_anchorPointInPoints.x + _transform.m[5] * -_anchorPointInPoints.y; } } if (_useAdditionalTransform) { _transform = _transform * _additionalTransform; } _transformDirty = false; } return _transform; }
  • 相关阅读:
    AC日记——红色的幻想乡 洛谷 P3801
    AC日记——Power收集 洛谷 P3800
    AC日记——妖梦拼木棒 洛谷 P3799
    AC日记——妖梦斩木棒 洛谷 P3797
    AC日记——小魔女帕琪 洛谷 P3802
    AC日记——双栈排序 洛谷 P1155
    AC日记——明明的烦恼 bzoj 1005
    AC日记——[HNOI2014]世界树 bzoj 3572
    AC日记——魔法森林 洛谷 P2387
    AC日记——【模板】点分治(聪聪可可) 洛谷 P2634
  • 原文地址:https://www.cnblogs.com/xiaonanxia/p/9104248.html
Copyright © 2020-2023  润新知