这一篇博客我主要想分析CCNode的坐标转换相关的底层实现,其他功能只做大概的介绍,先给出头文件的代码分析:
- #include "ccMacros.h"
- #include "cocoa/CCAffineTransform.h"
- #include "cocoa/CCArray.h"
- #include "CCGL.h"
- #include "shaders/ccGLStateCache.h"
- #include "shaders/CCGLProgram.h"
- #include "kazmath/kazmath.h"
- #include "script_support/CCScriptSupport.h"
- #include "CCProtocols.h"
- NS_CC_BEGIN
- class CCCamera;
- class CCGridBase;
- class CCPoint;
- class CCTouch;
- class CCAction;
- class CCRGBAProtocol;
- class CCLabelProtocol;
- class CCScheduler;
- class CCActionManager;
- class CCDictionary;
- //2dx 2.1.4新出来的东西
- class CCComponent;
- class CCComponentContainer;
- enum {
- //CCNode tag的默认值
- kCCNodeTagInvalid = -1,
- };
- /**
- CCNode能够产生的5个事件,如果有脚本回调将通知给对应的脚本
- */
- enum {
- kCCNodeOnEnter, //节点准备进入runningScene子节点
- kCCNodeOnExit, //节点已经退出runningScene子节点
- kCCNodeOnEnterTransitionDidFinish, //节点已经进入runningScene子节点
- kCCNodeOnExitTransitionDidStart, //节点准备退出runningScene子节点
- kCCNodeOnCleanup //Stops all running actions and schedulers
- };
- /**
- 为了方便阅读我按照功能模块来划分将相关代码换了次序
- */
- class CC_DLL CCNode : public CCObject
- {
- protected:
- /**
- 父子节点关系
- */
- bool m_bVisible; ///< is this node visible
- int m_nZOrder; ///< z-order value that affects the draw order
- bool m_bReorderChildDirty; ///< children order dirty flag
- CCArray* m_pChildren; ///< array of children nodes
- CCNode* m_pParent; ///< weak reference to parent node
- int m_nTag; ///< a tag. Can be any number you assigned just to identify this node
- unsigned int m_uOrderOfArrival; ///< used to preserve sequence while sorting children with the same zOrder
- bool m_bRunning; ///< is running
- /**
- 节点位置信息(与<strong>坐标</strong><strong>转换</strong>相关的)
- */
- float m_fRotationX;
- float m_fRotationY;
- float m_fScaleX;
- float m_fScaleY;
- CCPoint m_obPosition;
- float m_fSkewX;
- float m_fSkewY;
- CCPoint m_obAnchorPointInPoints;//只读
- CCPoint m_obAnchorPoint;
- bool m_bIgnoreAnchorPointForPosition;
- CCSize m_obContentSize;
- //<strong>转换</strong>矩阵
- CCAffineTransform m_sAdditionalTransform; //为什么需要新增这个<strong>转换</strong>矩阵看对应的函数注释
- CCAffineTransform m_sTransform; //node to parent transform
- CCAffineTransform m_sInverse; //inverse matrix of node to parent transform
- bool m_bTransformDirty; ///< transform dirty flag
- bool m_bInverseDirty; ///< transform dirty flag
- bool m_bAdditionalTransformDirty; ///< The flag to check whether the additional transform is dirty
- CCCamera *m_pCamera; ///< a camera
- CCGridBase *m_pGrid; ///< a grid
- /**
- 渲染相关
- */
- CCGLProgram *m_pShaderProgram; ///< OpenGL shader
- ccGLServerState m_eGLServerState; ///< OpenGL servier side state(这个state貌似这个版本没什么用了)
- /**
- 计时器与action的支持
- */
- CCScheduler* m_pScheduler; ///< scheduler used to schedule timers and updates
- CCActionManager*m_pActionManager; ///< a pointer to ActionManager singleton, which is used to handle all the actions
- /**
- 相关事件脚本回调
- */
- int m_nScriptHandler; ///< script handler for onEnter() & onExit(), used in Javascript binding and Lua binding.
- int m_nUpdateScriptHandler;///< script handler for update() callback per frame, which is invoked from lua & javascript.
- ccScriptType m_eScriptType;///< type of script binding, lua or javascript
- /**
- 自定义数据存储
- */
- void *m_pUserData; ///< A user assingned void pointer, Can be point to any cpp object
- CCObject *m_pUserObject; ///< A user assigned CCObject
- CCComponentContainer *m_pComponentContainer; ///< Dictionary of components
- /**
- OpenGL real Z vertex
- */
- float m_fVertexZ;
- public:
- CCNode(void);
- virtual ~CCNode(void);
- virtual bool init();
- static CCNode * create(void);
- const char* description(void);
- /**
- 父子节点之间关系
- */
- //改变zorder如果有父节点,则根据新的zorder排序
- virtual void setZOrder(int zOrder);
- //只改变zorder
- virtual void _setZOrder(int z);
- virtual int getZOrder();
- //CCNode的id标识符
- virtual int getTag() const;
- virtual void setTag(int nTag);
- //可见性,如果不可见该节点以及子节点将不会被visit
- virtual void setVisible(bool visible);
- virtual bool isVisible();
- //以下函数无需纠结
- virtual void addChild(CCNode * child);
- virtual void addChild(CCNode * child, int zOrder);
- virtual void addChild(CCNode* child, int zOrder, int tag);
- CCNode * getChildByTag(int tag);
- virtual CCArray* getChildren();
- unsigned int getChildrenCount(void) const;
- virtual void setParent(CCNode* parent);//parent a weak reference
- virtual CCNode* getParent();
- virtual void removeFromParent();
- virtual void removeFromParentAndCleanup(bool cleanup);
- virtual void removeChild(CCNode* child);
- virtual void removeChild(CCNode* child, bool cleanup);
- virtual void removeChildByTag(int tag);
- virtual void removeChildByTag(int tag, bool cleanup);
- virtual void removeAllChildren();
- virtual void removeAllChildrenWithCleanup(bool cleanup);
- virtual void reorderChild(CCNode * child, int zOrder);
- virtual void sortAllChildren();
- virtual void setOrderOfArrival(unsigned int uOrderOfArrival);
- virtual unsigned int getOrderOfArrival();
- virtual bool isRunning();//节点是否在runningScene的子节点
- //5种事件(对应最上面的枚举)
- virtual void onEnter();
- virtual void onEnterTransitionDidFinish();
- virtual void onExitTransitionDidStart();
- virtual void onExit();
- virtual void cleanup(void);//此函数将停条该节点以及子节点的所有计时器以及actions如果有脚本回调则通知 给脚本
- /**
- <strong>坐标</strong>系<strong>转换</strong><strong>机制</strong>相关函数
- <strong>分析</strong>的<strong>重点</strong>:
- */
- virtual void setScaleX(float fScaleX);
- virtual float getScaleX();
- virtual void setScaleY(float fScaleY);
- virtual float getScaleY();
- virtual void setScale(float scale);
- virtual float getScale();
- virtual void setPosition(const CCPoint &position);
- virtual const CCPoint& getPosition();
- virtual void setPosition(float x, float y);
- virtual void getPosition(float* x, float* y);
- virtual void setPositionX(float x);
- virtual float getPositionX(void);
- virtual void setPositionY(float y);
- virtual float getPositionY(void);
- virtual void setSkewX(float fSkewX);
- virtual float getSkewX();
- virtual void setSkewY(float fSkewY);
- virtual float getSkewY();
- virtual void setAnchorPoint(const CCPoint& anchorPoint);
- virtual const CCPoint& getAnchorPoint();
- virtual const CCPoint& getAnchorPointInPoints();
- virtual void ignoreAnchorPointForPosition(bool ignore);
- virtual bool isIgnoreAnchorPointForPosition();
- virtual void setContentSize(const CCSize& contentSize);
- virtual const CCSize& getContentSize() const;
- virtual void setRotation(float fRotation);
- virtual float getRotation();
- virtual void setRotationX(float fRotaionX);
- virtual float getRotationX();
- virtual void setRotationY(float fRotationY);
- virtual float getRotationY();
- //矩阵<strong>转换</strong>
- void transform(void);
- void transformAncestors(void);
- virtual void updateTransform(void);
- virtual CCAffineTransform nodeToParentTransform(void);//将node<strong>坐标</strong>系<strong>转换</strong>到parent<strong>坐标</strong>系
- virtual CCAffineTransform parentToNodeTransform(void);
- virtual CCAffineTransform nodeToWorldTransform(void);
- virtual CCAffineTransform worldToNodeTransform(void);
- CCPoint convertToNodeSpace(const CCPoint& worldPoint);
- CCPoint convertToWorldSpace(const CCPoint& nodePoint);
- CCPoint convertTouchToNodeSpace(CCTouch * touch);
- //treating the returned/received node point as anchor relative.
- CCPoint convertToNodeSpaceAR(const CCPoint& worldPoint);
- CCPoint convertToWorldSpaceAR(const CCPoint& nodePoint);
- CCPoint convertTouchToNodeSpaceAR(CCTouch * touch);
- /**
- * Sets the additional transform.
- *
- * @note The additional transform will be concatenated at the end of nodeToParentTransform.
- * It could be used to simulate `parent-child` relationship between two nodes (e.g. one is in BatchNode, another isn't).
- * @code
- // create a batchNode
- CCSpriteBatchNode* batch= CCSpriteBatchNode::create("Icon-114.png");
- this->addChild(batch);
- // create two sprites, spriteA will be added to batchNode, they are using different textures.
- CCSprite* spriteA = CCSprite::createWithTexture(batch->getTexture());
- CCSprite* spriteB = CCSprite::create("Icon-72.png");
- batch->addChild(spriteA);
- // We can't make spriteB as spriteA's child since they use different textures. So just add it to layer.
- // But we want to simulate `parent-child` relationship for these two node.
- this->addChild(spriteB);
- //position
- spriteA->setPosition(ccp(200, 200));
- // Gets the spriteA's transform.
- CCAffineTransform t = spriteA->nodeToParentTransform();
- // Sets the additional transform to spriteB, spriteB's postion will based on its pseudo parent i.e. spriteA.
- spriteB->setAdditionalTransform(t);
- //scale
- spriteA->setScale(2);
- // Gets the spriteA's transform.
- t = spriteA->nodeToParentTransform();
- // Sets the additional transform to spriteB, spriteB's scale will based on its pseudo parent i.e. spriteA.
- spriteB->setAdditionalTransform(t);
- //rotation
- spriteA->setRotation(20);
- // Gets the spriteA's transform.
- t = spriteA->nodeToParentTransform();
- // Sets the additional transform to spriteB, spriteB's rotation will based on its pseudo parent i.e. spriteA.
- spriteB->setAdditionalTransform(t);
- * @endcode
- */
- void setAdditionalTransform(const CCAffineTransform& additionalTransform);
- //网格(暂时没研究过以后有时间研究)
- virtual CCGridBase* getGrid();
- virtual void setGrid(CCGridBase *pGrid);
- virtual CCCamera* getCamera();
- CCRect boundingBox(void);
- virtual void visit(void);
- virtual void draw(void);
- /**
- 绘制相关
- */
- virtual CCGLProgram* getShaderProgram();
- virtual void setShaderProgram(CCGLProgram *pShaderProgram);
- virtual void setGLServerState(ccGLServerState glServerState);
- virtual ccGLServerState getGLServerState();
- /**
- action相关,可直接参照CCActionManager的相关函数
- */
- virtual void setActionManager(CCActionManager* actionManager);
- virtual CCActionManager* getActionManager();
- CCAction* runAction(CCAction* action);
- void stopAllActions(void);
- void stopAction(CCAction* action);
- void stopActionByTag(int tag);
- CCAction* getActionByTag(int tag);
- unsigned int numberOfRunningActions(void);
- /**
- Scheduler
- 直接查看CCScheduler的相关定义即可
- */
- virtual void setScheduler(CCScheduler* scheduler);
- virtual CCScheduler* getScheduler();
- bool isScheduled(SEL_SCHEDULE selector);
- void scheduleUpdate(void);
- void scheduleUpdateWithPriority(int priority);
- void unscheduleUpdate(void);
- void schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay);
- void schedule(SEL_SCHEDULE selector, float interval);
- void scheduleOnce(SEL_SCHEDULE selector, float delay);
- void schedule(SEL_SCHEDULE selector);
- void unschedule(SEL_SCHEDULE selector);
- void unscheduleAllSelectors(void);
- void resumeSchedulerAndActions(void);
- void pauseSchedulerAndActions(void);
- //Update method will be called automatically every frame if "scheduleUpdate" is called, and the node is "live"
- virtual void update(float delta);
- /**
- 脚本事件回调相关
- */
- virtual void registerScriptHandler(int handler);
- virtual void unregisterScriptHandler(void);
- inline int getScriptHandler() { return m_nScriptHandler; };
- void scheduleUpdateWithPriorityLua(int nHandler, int priority);//Schedule update for lua script.
- /**
- 自定义数据存储
- */
- virtual void* getUserData();
- virtual void setUserData(void *pUserData);
- virtual CCObject* getUserObject();
- virtual void setUserObject(CCObject *pUserObject);
- CCComponent* getComponent(const char *pName) const;
- virtual bool addComponent(CCComponent *pComponent);
- virtual bool removeComponent(const char *pName);
- virtual void removeAllComponents();
- /**
- opengl 顶点流中的顶点<strong>坐标</strong>的z值
- 无实现,需要在派生类中实现
- */
- virtual void setVertexZ(float vertexZ);
- virtual float getVertexZ();
- private:
- /// lazy allocs m_pChildren
- void childrenAlloc(void);
- /// helper that reorder a child
- void insertChild(CCNode* child, int z);
- /// Removes a child, call child->onExit(), do cleanup, remove it from children array.
- void detachChild(CCNode *child, bool doCleanup);
- /// Convert cocos2d coordinates to UI windows coordinate.
- CCPoint convertToWindowSpace(const CCPoint& nodePoint);
- };
回想一下director的drawScene函数里每帧都会调用一次函数里面有这写代码:
kmGLPushMatrix();// draw the scene
if (m_pRunningScene)
{
m_pRunningScene->visit();
}
// draw the notifications node
if (m_pNotificationNode)
{
m_pNotificationNode->visit();
}
if (m_bDisplayStats)
{
showStats();
}
kmGLPopMatrix();
这样就开始访问遍历以m_pRunningScene为根节点的结点树了。
为了更加明确的说明转换的流程,再回忆下director设置投影矩阵的时候(最初使用 kazmath来设置view matrix 与 projection matrix的函数):
的CCDirector::setProjection函数(可以参考 cocos2dx 2.1.4 程序运行脉络解析3—解析CCEGLView )的分析。
再看visit函数:
- void CCNode::visit()
- {
- // quick return if not visible. children won't be drawn.
- if (!m_bVisible)
- {
- return;
- }
- //压入该节点的<strong>转换</strong>矩阵
- kmGLPushMatrix();
- //grid暂且不<strong>分析</strong>
- if (m_pGrid && m_pGrid->isActive())
- {
- m_pGrid->beforeDraw();
- }
- //将压入矩阵堆栈的矩阵生成依据该节点相关属性的<strong>转换</strong>矩阵
- this->transform();
- //visit 的访问(绘制次序):先访问zorder小于0的、再draw自己,再访问zorder大于0的
- CCNode* pNode = NULL;
- unsigned int i = 0;
- if(m_pChildren && m_pChildren->count() > 0)
- {
- sortAllChildren();
- // draw children zOrder < 0
- ccArray *arrayData = m_pChildren->data;
- for( ; i < arrayData->num; i++ )
- {
- pNode = (CCNode*) arrayData->arr[i];
- if ( pNode && pNode->m_nZOrder < 0 )
- {
- pNode->visit();
- }
- else
- {
- break;
- }
- }
- // self draw
- this->draw();
- for( ; i < arrayData->num; i++ )
- {
- pNode = (CCNode*) arrayData->arr[i];
- if (pNode)
- {
- pNode->visit();
- }
- }
- }
- else
- {
- this->draw();
- }
- // reset for next frame
- m_uOrderOfArrival = 0;
- //grid暂且不<strong>分析</strong>
- if (m_pGrid && m_pGrid->isActive())
- {
- m_pGrid->afterDraw(this);
- }
- //当前节点访问完成后pop出该节点关联的<strong>转换</strong>矩阵
- kmGLPopMatrix();
- }
CCNode实际上是调用transform函数将自己的转换矩阵正确的设置到 KM_GL_MODELVIEW 矩阵堆栈栈顶:
- void CCNode::transform()
- {
- kmMat4 transfrom4x4;
- // Convert 3x3 into 4x4 matrix
- //获取根据CCNode相关属性而生成的转置矩阵
- CCAffineTransform tmpAffine = this->nodeToParentTransform();
- //将cocos2dx矩阵<strong>转换</strong>成opengl矩阵
- CGAffineToGL(&tmpAffine, transfrom4x4.mat);
- // Update Z vertex manually
- transfrom4x4.mat[14] = m_fVertexZ;
- //将栈顶执行该节点的<strong>转换</strong>操作
- kmGLMultMatrix( &transfrom4x4 );
- // XXX: Expensive calls. Camera should be integrated into the cached affine matrix
- //如果设置了camera的话需要额外的设置camera对应<strong>转换</strong>
- //2dx限定如果有了grid就无视camera了
- //camera 相关参数合成的matrix与view transform matrix是一致的
- //经过camera<strong>转换</strong>后就能够呈现使用不同角度(位置)来观看这个节点要绘制的2d图片的效果了
- //(猜想2dx的45度的视觉效果就是用camera实现的吧)
- if ( m_pCamera != NULL && !(m_pGrid != NULL && m_pGrid->isActive()) )
- {
- bool translate = (m_obAnchorPointInPoints.x != 0.0f || m_obAnchorPointInPoints.y != 0.0f);
- //将锚点移到<strong>局部</strong><strong>坐标</strong>系的原点
- if( translate )
- kmGLTranslatef(RENDER_IN_SUBPIXEL(m_obAnchorPointInPoints.x), RENDER_IN_SUBPIXEL(m_obAnchorPointInPoints.y), 0 );
- //添加一个额外的view Matrix transform
- m_pCamera->locate();
- //恢复锚点原来的位置
- if( translate )
- kmGLTranslatef(RENDER_IN_SUBPIXEL(-m_obAnchorPointInPoints.x), RENDER_IN_SUBPIXEL(-m_obAnchorPointInPoints.y), 0 );
- }
- }
需要注意的是cocos2dx米有使用kazmath 的 kmMat4 而是使用 CCAffineTransform,
其原因是:CCAffineTransform只针对cocos2dx底层支持的2d转换来实现的,效率上而言CCAffineTransform肯定是更快的。
struct CCAffineTransform {
float a, b, c, d;
float tx, ty;
};
给出两个矩阵转换的关系图:
CCAffineTransform.h里面有相关的函数对CCAffineTransform 结构体进行相关的转换操作类似kazmath里面的基本数据的相关功能分布形式。
要根据CCNode的相关参数来合成一个 针对父节点转换的矩阵,使CCNode的坐标为父节点坐标系的坐标,由于父节点在node之前被访问父节点的转换矩阵也预先push
进栈了,以此类推当CCNode将相关的转换矩设置到栈顶后得到的坐标是全局坐标系(相关坐标数据经过该转换矩阵转换后),其原点是左下角,与opengl坐标系一致。
下面分析CCNode::nodeToParentTransform:
额,在解释之前为了让大家更清楚具体的转换流程大家想想CCSprite里面有存储这这么一个结构:
// vertex coords, texture coords and color info
ccV3F_C4B_T2F_Quad m_sQuad;
这个结构体保存了4个点结构体有个字段是顶点坐标的字段,里面保存了要传入opengl的顶点坐标,而这个值传入后经过mazmath栈顶转换矩阵转换后才得出正确的坐标(这个过程是在shader里面发生的可以参考shader解析篇)你可以把m_sQuad对应的4个点形成的矩形当做看得见的一张图片,而里面的坐标位置是一个相对于该点的局部坐标,一般而言这4个点组成的矩形大小跟对应CCNode的大小相当(一般为origin=(0,0) size = contentSize ,姑且以这4个点的坐标为参考再来看一下下面的函数实现)。
- CCAffineTransform CCNode::nodeToParentTransform(void)
- {
- if (m_bTransformDirty)
- {
- //x,y执行平移<strong>转换</strong>
- float x = m_obPosition.x;
- float y = m_obPosition.y;
- //如果忽略锚点则需要将左下角移至<strong>坐标</strong>(父节点<strong>坐标</strong>系)原点
- if (m_bIgnoreAnchorPointForPosition)
- {
- x += m_obAnchorPointInPoints.x;
- y += m_obAnchorPointInPoints.y;
- }
- //执行旋转操作
- //执行旋转的方向为:CW(顺时针)
- //rotate x:执行旋转后矩形的竖直方向与y轴所成的角度
- //rotate y:执行旋转后矩形的水平方向与x轴所成的角度
- //If we skew with the exact same value for both x and y then we're simply just rotating
- float cx = 1, sx = 0, cy = 1, sy = 0;
- if (m_fRotationX || m_fRotationY)
- {
- float radiansX = -CC_DEGREES_TO_RADIANS(m_fRotationX);
- float radiansY = -CC_DEGREES_TO_RADIANS(m_fRotationY);
- cx = cosf(radiansX);
- sx = sinf(radiansX);
- cy = cosf(radiansY);
- sy = sinf(radiansY);
- }
- bool needsSkewMatrix = ( m_fSkewX || m_fSkewY );
- // optimization:
- // inline anchor point calculation if skew is not needed
- // Adjusted transform calculation for rotational skew
- if (! needsSkewMatrix && !m_obAnchorPointInPoints.equals(CCPointZero))
- {
- x += cy * -m_obAnchorPointInPoints.x * m_fScaleX + -sx * -m_obAnchorPointInPoints.y * m_fScaleY;
- y += sy * -m_obAnchorPointInPoints.x * m_fScaleX + cx * -m_obAnchorPointInPoints.y * m_fScaleY;
- }
- // Build Transform Matrix
- // Adjusted transform calculation for rotational skew
- //生成的矩阵相当于下面的矩阵,如果x == y 那么右边的矩阵为绕z轴旋转的矩阵了
- //
- //m_sTransform =:
- //
- //|1 , 0 , x| |cy * m_fScaleX , -sx * m_fScaleY , 0|
- //|0 , 1 , y| * |sy * m_fScaleX , cx * m_fScaleY , 0|
- //|0 , 0 , 1| | 0 , 0 , 1|
- m_sTransform = CCAffineTransformMake( cy * m_fScaleX, sy * m_fScaleX,
- -sx * m_fScaleY, cx * m_fScaleY,
- x, y );
- // XXX: Try to inline skew
- // If skew is needed, apply skew and then anchor point
- if (needsSkewMatrix)
- {
- CCAffineTransform skewMatrix = CCAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(m_fSkewY)),
- tanf(CC_DEGREES_TO_RADIANS(m_fSkewX)), 1.0f,
- 0.0f, 0.0f );
- //m_sTransform = m_sTransform * skewMatrix
- m_sTransform = CCAffineTransformConcat(skewMatrix, m_sTransform);
- // adjust anchor point
- if (!m_obAnchorPointInPoints.equals(CCPointZero))
- {
- //m_sTransform = m_sTransform * TM(-m_obAnchorPointInPoints.x,-m_obAnchorPointInPoints.y)
- m_sTransform = CCAffineTransformTranslate(m_sTransform, -m_obAnchorPointInPoints.x, -m_obAnchorPointInPoints.y);
- }
- }
- if (m_bAdditionalTransformDirty)
- {
- //最后执行AdditionalTransform<strong>转换</strong>
- //m_sTransform = m_sAdditionalTransform * m_sTransform
- m_sTransform = CCAffineTransformConcat(m_sTransform, m_sAdditionalTransform);
- m_bAdditionalTransformDirty = false;
- }
- //总结一下<strong>转换</strong>的顺序:(有括号的矩阵可能不需要执行<strong>转换</strong>)
- //m_sTransform = [m_sAdditionalTransform *] translate * rotate [* skewMatrix [* TM(-m_obAnchorPointInPoints.x,-m_obAnchorPointInPoints.y)]]
- //顶点的<strong>转换</strong>顺序是由右到左一次与上面的矩阵相乘执行<strong>转换</strong>的其中最左边锚点偏移<strong>转换</strong>是可选的是因为translate矩阵的生成过程做了优化处理(skew<strong>转换</strong>优化),
- //参考上面2dx注释,可将条件if (! needsSkewMatrix && !m_obAnchorPointInPoints.equals(CCPointZero))使之为真来推算<strong>转换</strong>结果是一样的
- //如果将所有的<strong>转换</strong>全部都用上则<strong>转换</strong>顺序为:先移动让锚点为<strong>坐标</strong>原点,再执行skew<strong>转换</strong>,在执行rotation<strong>转换</strong>,最后执行移动让锚点位置在父节点<strong>坐标</strong>系对应的<strong>坐标</strong>位置,
- //最后如果有额外的<strong>转换</strong>则执行m_sAdditionalTransform<strong>转换</strong>
- m_bTransformDirty = false;
- }
- return m_sTransform;
- }
最后请大家注意下CCAffineTransform有个很奇葩的现象:
执行函数:
CC_DLL CCAffineTransform CCAffineTransformConcat(const CCAffineTransform& t1, const CCAffineTransform& t2);
实际上是返回 t2 * t1的结果。
一般顶点转换操作:
CC_DLL CCPoint __CCPointApplyAffineTransform(const CCPoint& point, const CCAffineTransform& t);
返回 t * point的值
以下函数:
//t * TM(tx,ty)
CC_DLL CCAffineTransform CCAffineTransformTranslate(const CCAffineTransform& t, float tx, float ty);
//Rotate around Z axis
//t*RZM(angle)
CC_DLL CCAffineTransform CCAffineTransformRotate(const CCAffineTransform& aTransform, float anAngle);
//t*SM(sx,sy)
CC_DLL CCAffineTransform CCAffineTransformScale(const CCAffineTransform& t, float sx, float sy);
矩阵相乘的顺序不同执行变换的结果也是不同的,我被这几个可恶的函数搞晕了好长时间了,哎...
ok分析 完毕,如果有错误的地方欢迎指处~~~