coccos creater 题点
cocos工作机制类
1、cocos内存管理方式
cocos 是通过引用计数方式管理内存的,主要通过两种方式实现。
1)手动管理内存
当我们创建一个实例时,基类Ref里面有一个叫referenceCount的参数会置1,它代表我们引用的次数,使用retain()函数可以使reference+1,release()函数会使referenceCount -1;当这个参数减至0时会被引擎delete掉释放内存。
2)自动回收池
有手动就会有自动。
我们在create()一个对象时,调用new函数创建对象并且还会利用autorelease()方法 把该对象的指针加入自动回收池,
主线程 每一帧在Application::getInstance()->run()的过程中会调用mainloop()方法
在mainloop()方法中会利用单例的PoolManager类 getCurPool()获取当前的自动回收池,并调用自动回收池中的clear()方法,
clear()方法里,会遍历移除所有与该自动回收池相关联的对象,并调用一次release()方法,如果这个对象没被调用且referenceCount被减至0就会delete掉。
若这个对象已经addChild到其他控件上,被引用的话,referenceCount不会被减到0,等到其父节点被清理的时候再回收。
好处:
1)无需手动和release()和retain(),避免内存泄露
2)在每一帧结束后对无用的对象进行自动处理内存回收,方便可靠。
可以优化的地方
1)PoolManager管理的是一个_releasePoolStack(存储自动回收池的栈),一般情况下,每一帧结束后,我们只是把当前的池子清空,然后执行下一帧的操作。
但是我们也应该考虑到释放池本身维护着一个将要执行释放操作的对象列表,如果在一帧内生成了大量的autoRelease对象,将会导致释放池性能下降。
因此,在生成autorelease对象密集的区域(通常是循环中)的前后,我们最好可以手动创建并释放一个回收池。
2)autorelease()只有在自动释放池被释放时才会进行一次释放操作,若果对象释放的次数超过了应有的次数,则这个错误在调用autorelease()时并不会被发现,只有当自动释放池被释放时(通知也就是游戏的每一帧结束时),游戏才会崩溃。
在这种情况下,定位错误就变得十分困难了。因此,我们建议在开发过程中应该避免滥用autorelease(),只在工厂方法等不得不用的情况下使用,尽量以release()来释放对象引用。
2、cocos的渲染机制
3.x之前是通过每一个node的draw方法来使用OpenGl Es 代码进行渲染。
3.x之后则使用了新的渲染机制,统一把所有的需要渲染的node安放在一个列队中再进行自动批处理渲染。
主线程每一帧在Application::getInstance()->run()的过程中会调用mainloop()方法,mainloop()方法中会调用一次drawScene()方法
方法名是drawScene,那么重点肯定就是对当前场景需要渲染的节点进行渲染。先清除渲染状态,然后调用render()方法。
接下来我们看render函数具体是怎么实现的:它首先是使用visit方法让需要被渲染的节点进行排序并插入到渲染队列CommandQueue中,然后在一起自动批处理进行渲染。
我们接着看visit方法,首先它根据localZOrder使用sortAllChildren()来进行排序。
紧接着对localZOrder < 0的结点进行递归渲染。
然后是渲染本身节点,最后在递归渲染localZOrder>0的子节点。其实本质就是按照(左,中,右)中序遍历进行渲染
往下看每个节点的draw函数,我们可以发现,3.x之后没有直接在draw函数中进行渲染,而是把渲染命令压入到渲染序列中去,最后再回到render函数中进行自动批处理渲染。
void Sprite::draw(Renderer * Renderer ,const Mat4 &transform,uint32_t flags){
// 判定纹理是否有效
if (_texture == nullptr) { return; }
#if CC_USE_CULLING // Don't calculate the culling if the transform was not updated
auto visitingCamera = Camera::getVisitingCamera();
auto defaultCamera = Camera::getDefaultCamera();
if (visitingCamera == defaultCamera) {
_insideBounds = ((flags & FLAGS_TRANSFORM_DIRTY) || visitingCamera->isViewProjectionUpdated()) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
} else {
//xxx:this always return true since
_insideBounds = renderer->checkVisibility(transform, _contentSize); }
// 判定渲染的纹理是否在可见区域内
if(_insideBounds)
#endif {
_trianglesCommand.init(_globalZOrder, _texture, getGLProgramState(), _blendFunc, _polyInfo.triangles, transform, flags);
// 将绘制命令添加到renderer绘制栈RenderQueue中
renderer->addCommand(&_trianglesCommand);
}
}
}
}
在render中,对列队中需要渲染的结点根据其GlobalZorder进行排序,
globalZOrder是一个float(不是int)的参数,这个值在渲染器中用来给RenderCommand排序。
较低的值拥有较高的优先级。这意味着一个globalZOrder为-10的节点会比一个globalZorder为10的 节点优先绘制。
globalZOrder为0(默认值)的节点将会根据Scene Graph 顺序 绘制。
然后再对列队中渲染命令调用OpenGl的API进行渲染。自此完成了整个渲染流程。
总结来说:
导演类中的mainLoop中会调用drawScene,
在drawScene中会调用场景类的render,
render中会递归执行节点的visit,visit中会调用精灵类的draw,draw中会执行渲染类的addCommand。
对所有节点执行完addCommand后,会执行渲染类processRenderCommand,接下来执行渲染类的drawBatchedTriangles,
最终drawBatchedTriangles内会调用多个OpenGL API完成渲染。
3、cocos的cache缓冲区
TextureCache:加载大纹理图如 背景图片(pkm,png)
Director::getInstance()->getTextureCache()->addImage(filename)
// 存入
Texture2D *texture = Director::getInstance()->getTextureCache()->getTextureForKey(textureKeyName)
// 使用
Director::getInstance()->getTextureCache()->removeUnusedTextures()
// 清理没被引用的
Director::getInstance()->getTextureCache()->removeAllTextures() // 清理全部
SpriteFrameCache.:加载碎的精灵帧图片,plist文件
texture)//清理某图片
4、SpriteBatchNode精灵批处理类
简单应用方面
1、creator与2dx有什么区别?
creator 是一套包含编辑器在内的开发框架,其内部引擎使用了2dx的精简修改版本。
creater的开发是以js、ts语言开发,以内容创作为核心,以脚本作为自定义组件,绑定到场景节点上。
2、creater 中需要动态载入的资源,放在工程的那个子目录中。
动态载入的资源,需要放入到自己创建的resources文件夹下 ,也就是:assets/resources。
3、写出代码片段:获取节点node上的label组件,并将内容设置为“hello”
let label = node.getComponent("label");
label.string = "hellow";
4、列出组件的生命周期回调函数,并说明其调用的时机。
onLoad() 组件首次激活时触发
start() 组件第一次执行update之前触发
update() 每一帧渲染之前调用
lateUpdate() 多有组件update调用后调用
onDestory() 组件或所在节点调用了destory()时调用,并在当前帧结束时同意回收组件。
onEnable() 组件的enabel属性从false变为true的时候调用
onDisable() 组件的enable属性从true变成false的时候调用
5、creater 中ui对齐组件,如果制作屏幕大小的组件,要怎么使用?
widget组件。设置 top bottom left right 的值为 0px。且该节点的父节点到场景跟节点都必须绑定 widget组件并且设置屏幕大小。或者1.10之后可设置target为最上层的节点。
6、写一小段代码,使用action将节点node在1秒钟内从当前的位置移动到(100,100)
let action=moveTo(100,100);
node.runAction(action)
游戏制作
7、cocos中的sprite的blend属性,src Blend Factor 设置SRC_ALPHA和ONE_MINUS_SRC_ALPHA分别是什么意思,有什么作用?
表示绘制sprite的时候,和frame buffer 上面已经有的像素进行混合的公式参数。
效果是 标准的透明图元渲染.
8、****什么是drawcall,为什么减少drawcall可以优化游戏速度。如何减少drawcall?在creater中应该怎么做?
1)简称 绘制调用
就是CPU调用图形绘制接口api(例如OpenGl中的glDrawElement命令)来命令GPU进行图形绘制(渲染)的操作。
2)每次调用dc的时候,cpu都会做很多处理,包括准备数据,状态,指令等。在这一阶段,cpu会做很多工作,例如检查渲染状态等,而一旦cpu完成了这些准备工作,GPU就可以进行渲染了。Gpu的渲染能力是很强的,渲染300个和来渲染3000个三角形网格通常是没什么区别的,因此GPU的渲染速度,往往会快于cpu,如果dc数量太多的话,CPU就会把大量的时间,用在指令的提交上,造成CPU过载。
因此造成dc性能问题的CPU
3)解决办法是尽可能合批处理,即将小的dc合并成打的dc
1、将碎图合并成大图
静态合图
动态合图
textuepaker打包图集
注意:1、尽量将处于同一界面的(UI)下的相邻且渲染状态相同的碎图打包成图集。
2、改变渲染状态,会打断合批,例如:改变纹理状态(预乘、循环模式和过滤模式)或者Material(材质)、blend(混合模式)等等,所以使用自定义sharder也会打断合批。
3、合理控制图集的大小,避免单个图像加载时间过长。
4、尺寸太大,没必要打进图集
5、间距默认保持2并保持勾选扩边选项,避免图像裁剪错误和出现黑边的情况。
2、label用 bmfont位图
9、一张1024 *1024,32位的贴图,在内存里面占多少字节?
图像占用的内存公式是:
numBytes = width * height * bitsPerPixel /8
套用公式,RGBA8888是32位
1024 * 1024 *32 /8 = 4194304(字节)
4194304 /1024 /1024 =4 M
10、如果做一个射击游戏,需要发射大量的子弹,为了避免频繁的申请内存,一般会采取什么方法?这个机制会提供哪些接口供游戏逻辑使用?
采用 对象池/内存缓存机制。apinodepool 对象池;
preAllocat(count)接口预先分配一定的数量的对象
get()接口返回一个池里面的空闲对象
put(obj)接口 将对象返回到池里面,
freeAll()/clear()接口清空整个对象池,释放所有内存