1.内存管理
引用计数类Ref,构造时计数为1,retain+1,release-1(减到0就delete掉)。
Ref的autorelease将它添加到自动释放池去,将在当前帧结束时被release一次(即如果中途未被retain则会被释放掉)。
另外cocos2dx内置的一些容器比如Vector、Map,在添加和删除的时候会对元素进行retain和release。所以当你addChild的时候,里面_children.pushBack(child)就会将其引用计数加1,这样就不会被自动释放掉了。
2.ui树
通常我们使用的节点的坐标是其相对于父级节点的相对坐标。而每个元素的本地坐标系是以元素的 左下角为原点(注意不是以锚点处为原点!),向右上为正轴。
而节点的坐标指的是节点锚点处在父亲坐标系的位置,即相对于父亲左下角的位置。
node->convertToWorldSpace(pos)是将相对于node左下角的坐标转换到世界坐标,所以这里pos如果是node的坐标的话结果就是node左下角的世界坐标。
node->convertToWorldSpaceAR(pos)是将相对于node锚点的坐标转换到世界坐标,所以这里pos如果是Zero的话结果就是node锚点处的世界坐标。
所以获取node的世界坐标有两种方法:一种是通过父亲将node的位置转换到worldSpace,另一种是convertToWorldSpaceAR(Vec2::ZERO)。
所以在编写包含多个子节点的控件时,应该将这些子节点和父节点的左下角对齐,而不是和其锚点对齐。因为父节点的锚点不管怎么变,子节点跟他的关系都是固定的。
3.逻辑深度
节点现在有globalZOrder(默认为0),决定节点的渲染顺序(command的执行顺序)。
void RenderQueue::push_back(RenderCommand* command) { float z = command->getGlobalOrder(); if(z < 0) { _commands[QUEUE_GROUP::GLOBALZ_NEG].push_back(command); } else if(z > 0) { _commands[QUEUE_GROUP::GLOBALZ_POS].push_back(command); } else { if(command->is3D()) { if(command->isTransparent()) { _commands[QUEUE_GROUP::TRANSPARENT_3D].push_back(command); } else { _commands[QUEUE_GROUP::OPAQUE_3D].push_back(command); } } else { _commands[QUEUE_GROUP::GLOBALZ_ZERO].push_back(command); } } }这个是在渲染队列添加渲染命令时用到,将不同globalZOrder的节点放到不同的命令集合去。然后在渲染时从负到正进行渲染,详见Renderer::visitRenderQueue。
节点的遍历visit顺序和之前一样,通过localZOrder(就是2.x的zorder)从小到大进行。
//visit the scene visit(renderer,transform,0); renderer->render();总的来说,场景的渲染流程:通过localZOrder遍历visit所有节点,在里面调用节点的draw添加command到各自globalZOrder对应的命令集合去,然后渲染器再依次执行命令集合。注意 因为SpritebatchNode是将子元素组织成一个BatchCommand,会被统一添加到一个globalZOrder对应的命令集去渲染,所以给其子元素设置不同的globalZOrder是没有效果的。
4.场景管理
Director里提供了一些scene的管理方法,比如runWith、replace、push、pop、popToRoot等,但引擎永远只会渲染一个scene,所以不会有同时渲染两个scene的可能。所以要实现多个窗口同时渲染就需要自己在scene里面实现,而不能通过多个scene。所以最好把scene当成一个大的场景模块(即表示当前很长一段时间都执行与此相关的逻辑),比如login、main等,再在大模块内部进行窗口管理。
5.实时更新
scheduler会按优先级 从小到大进行更新回调。ActionManager优先级是最小的(也就是最高的,最先执行):
// action manager _actionManager = new (std::nothrow) ActionManager(); _scheduler->scheduleUpdate(_actionManager,Scheduler::PRIORITY_SYSTEM,false);而node的scheduleUpdate默认优先级是0:
void Node::scheduleUpdate() { scheduleUpdateWithPriority(0); }而node的schedule不能设置优先级。原因是Scheduler里有4个容器:_updatesNegList,_updates0List,_updatesPosList,_hashForTimers。然后它update的时候也是按这个顺序更新的。scheduleUpdate的节点就放大前面3个容器,而schedule的就放到最后一个容器(则最后更新)。
在这些都更新完之后,还会执行一些异步函数(主要是其他线程 需要在主线程执行逻辑时使用):
// // Functions allocated from another thread // // Testing size is faster than locking / unlocking. // And almost never there will be functions scheduled to be called. if( !_functionsToPerform.empty() ) { _performMutex.lock(); // fixed #4123: Save the callback functions,they must be invoked after '_performMutex.unlock()',otherwise if new functions are added in callback,it will cause thread deadlock. auto temp = _functionsToPerform; _functionsToPerform.clear(); _performMutex.unlock(); for( const auto &function : temp ) { function(); } }