Hexococos2dx 3.0 ---- Node 2014-10-15
对于cocos2dx中的基类Node
,一切都从这开始,Node这里值得分析的东西很多,看来是得花很长一个篇幅来讲。
我预计可以分3块东西讲
- 数学相关
- 渲染相关
- 引擎相关
路径
1 | 2d/base-node/CCNode.h 2d/base-node/CCNode.cpp |
类图
先附上一张类图
好长哦~~
源码分析
Node
作为大部分子类的父类,可见是得好好分析分析。
先贴上构造函数,析构函数
1 | Node::Node(void) |
待我去看看Components是毛
by etond 20141022 10:31
bingling~
我回来了,貌似传说中的数据驱动(Entiy-Component-System)的组件~
by etond 20141022 11:21
自然还是需要google一番才是
- Understanding Component-Entity-Systems
Evolve Your Hierarchy Refactoring Game Entities with Components
Case Study: Bomberman Mechanics in an Entity-Component-System
或许发现一个不错的网站 www.gamedev.net
下午好好研究这几篇文章之后再继续写
我还找到了一个挺不错的pptOGDC 2014: Component based entity system mobile game development
by etond 20141022 11:44
此块内容移步Entity Component System
终于从栈中递归出来了。(鼓掌~)
这里先看看Node的成员变量
。
1 | /* 位置,大小,缩放,扭曲,描点 */ |
(我赤裸裸的从源码里面拷贝了这些代码,并加上了邪恶的中文注释)
数学相关
先看看getNodeToParentTransform
的实现,这个方法每一帧都会被调用,去获得最新的变换矩阵。
1 | const kmMat4& Node::getNodeToParentTransform() const |
可以看到,Node的位置,大小,旋转等成员变量都是在这里使用。
PS:位置变换&大小变换 == 位置变换矩阵x大小变换矩阵
cocos2dx里面有一系列转换的函数,如获取当前节点在世界坐标的位置
这里就列出convertToWorldSpace
的相关方法
1 | kmMat4 Node::getNodeToWorldTransform() const |
从上面几个方法可以知道
- 求当前节点到根节点的所有变换矩阵乘积 — 矩阵T
- 求T其逆矩阵 — 矩阵iT
- 根据逆矩阵和当前相对于父节点的位置,便可以得到世界坐标的位置。
PS:涉及到线性代数逆矩阵相关东西 —- 3D数学第九章
渲染相关
visit
1 | void Node::visit(Renderer* renderer, const kmMat4 &parentTransform, bool parentTransformUpdated) { if (!_visible) { return; } bool dirty = _transformUpdated || parentTransformUpdated; if(dirty) _modelViewTransform = this->transform(parentTransform); _transformUpdated = false; /* 矩阵栈 */ /* 要被弃用 */ kmGLPushMatrix(); kmGLLoadMatrix(&_modelViewTransform); int i = 0; /* 渲染树 */ /* 递归渲染, dfs */ if(!_children.empty()) { /* 先对子儿子们排序,就是std::vector的排序 */ sortAllChildren(); /* 先绘制在该Node下面的 */ for( ; i < _children.size(); i++ ) { auto node = _children.at(i); if ( node && node->_localZOrder < 0 ) node->visit(renderer, _modelViewTransform, dirty); else break; } /* 然后绘制该节点 */ this->draw(renderer, _modelViewTransform, dirty); /* 然后绘制在该Node上面的节点 */ for(auto it=_children.cbegin()+i; it != _children.cend(); ++it) (*it)->visit(renderer, _modelViewTransform, dirty); } else { this->draw(renderer, _modelViewTransform, dirty); } /* 避免再次排序? */ _orderOfArrival = 0; kmGLPopMatrix(); } /* sortAllChildren的比较函数 */ ibool nodeComparisonLess(Node* n1, Node* n2) { return( n1->getLocalZOrder() < n2->getLocalZOrder() || ( n1->getLocalZOrder() == n2->getLocalZOrder() && n1->getOrderOfArrival() < n2->getOrderOfArrival() ) ); } |
这里的在visit中_orderOfArrival在渲染结束之后被置为0,这里结合nodeComparisonLess这个比较函数来看,
_orderOfArrival是Node维护的,每个节点的值都不同,而_localZOrder是用户自定义的,当_localZOrder相同的时候才去比较_orderOfArrival
将_orderOfArrival置为0的意思,是表示该节点已经排序过了。再排序的时候就不用做多余的交换操作。
值得注意的是源码中的一句注释
1 | // XXX: Yes, nodes might have a sort problem once every 15 days |
这里的问题应该是int溢出,根据上面的条件,下面这个等式
2147482647 / 60 / 3600 / 24 / 27(Nodes) = 15 (days)
不过这个问题出现的几率比较小,毕竟让这个s_globalOrderOfArrival整形溢出还是需要挺长时间的,毕竟不是每一帧都去改变很多的Node的_localZOrder值。
引擎相关
addChild & removeChild
1 | void Node::addChild(Node *child, int zOrder, int tag) |
onEnter & onExit
代码中略去的脚本相关的代码
1 | void Node::onEnter() |
onEnter
和onExit
一样,都是通过递归调用去执行以当前节点为根的一颗渲染树=。=
Ending
这文章写了我好久,讲述下艰辛的历程:
Node –> ComponentContainer –> Entity Component System –> template –> c++泛型编程
|
|—–> kazmath –> 线性代数
大概是这样,主要是自己本身基础不够扎实导致的各种问题。
还是得仔细研究研究c++ primer 5th才是啊,不然连c++都不会了要。