在creator中,为了降低DC,提供了一个方法,在项目运行的时候,运行时将内存中的任意纹理组合成一张虚拟的图集,当渲染一张贴图的时候,动态合图系统会自动检测这张贴图是否已经被加入到了动态合图系统,如果没有,并且此贴图又符合动态合图的条件,就会将此贴图合并到动态合图系统生成的大贴图中。
动态合图 是按照 渲染顺序 来选取要将哪些贴图合并到一张大图中的,这样就能确保相邻的 DrawCall 能合并为一个 DrawCall。
注意事项:
在场景加载前,动态合图系统会进行重置, SpriteFrame贴图的引用和 uv 都会恢复到初始值。
Cocos Creator是如何实现这个功能的呢?
核心思想是数据结构中常说的空间换时间, 原理其实并不复杂,就是帧缓存,简单来说就是将多份spriteFrame绘制到RenderTexture上,并记录其在新的RenderTexture中所属的位置和长宽,渲染的时候利用这些信息从RenderTexture所得到的纹理上取所需要的区域`
```
onstAtlas=require('./atlas');
let_atlases=[];
let_atlasIndex=-1;
let_maxAtlasCount=5;
let_textureSize=2048;
// Smaller frame is more likely to be affected by linear filter
let_minFrameSize=8;
let_maxFrameSize=512;
let_debugNode=null;
functionnewAtlas(){
letatlas=_atlases[++_atlasIndex]
if(!atlas){
atlas=newAtlas(_textureSize,_textureSize);
_atlases.push(atlas);
}
returnatlas;
}
functionbeforeSceneLoad(){
dynamicAtlasManager.reset();
}
let_enabled=false;
/**
* !#en Manager the dynamic atlas.
* !#zh 管理动态图集。
*@classDynamicAtlasManager
*/
letdynamicAtlasManager={
/**
* !#en Enabled or Disabled dynamic atlas.
* !#zh 开启或者关闭动态图集。
*@propertyenabled
*@type{Boolean}
*/
getenabled(){
return_enabled;
},
setenabled(value){
if(_enabled===value)return;
if(value){
this.reset();
cc.director.on(cc.Director.EVENT_BEFORE_SCENE_LAUNCH,beforeSceneLoad);
}
else{
cc.director.off(cc.Director.EVENT_BEFORE_SCENE_LAUNCH,beforeSceneLoad);
}
_enabled=value;
},
/**
* !#en The maximum number of atlas that can be created.
* !#zh 可以创建的最大图集数量。
*@propertymaxAtlasCount
*@type{Number}
*/
getmaxAtlasCount(){
return_maxAtlasCount;
},
setmaxAtlasCount(value){
_maxAtlasCount=value;
},
/**
* !#en The size of the atlas that was created
* !#zh 创建的图集的宽高
*@propertytextureSize
*@type{Number}
*/
gettextureSize(){
return_textureSize;
},
settextureSize(value){
_textureSize=value;
},
/**
* !#en The maximum size of the picture that can be added to the atlas.
* !#zh 可以添加进图集的图片的最大尺寸。
*@propertymaxFrameSize
*@type{Number}
*/
getmaxFrameSize(){
return_maxFrameSize;
},
setmaxFrameSize(value){
_maxFrameSize=value;
},
/**
* !#en Append a sprite frame into the dynamic atlas.
* !#zh 添加碎图进入动态图集。
*@methodinsertSpriteFrame
*@param{SpriteFrame} spriteFrame
*/
insertSpriteFrame(spriteFrame){
if(CC_EDITOR)returnnull;
if(!_enabled||_atlasIndex===_maxAtlasCount||
!spriteFrame||spriteFrame._original)returnnull;
lettexture=spriteFrame._texture;
if(textureinstanceofcc.RenderTexture||texture._isCompressed())returnnull;
letw=texture.width,h=texture.height;
if(w>_maxFrameSize||h>_maxFrameSize||w<=_minFrameSize||h<=_minFrameSize
||texture._getHash()!==Atlas.DEFAULT_HASH){
returnnull;
}
letatlas=_atlases[_atlasIndex];
if(!atlas){
atlas=newAtlas();
}
letframe=atlas.insertSpriteFrame(spriteFrame);
if(!frame&&_atlasIndex!==_maxAtlasCount){
atlas=newAtlas();
returnatlas.insertSpriteFrame(spriteFrame);
}
returnframe;
},
/**
* !#en Resets all dynamic atlas, and the existing ones will be destroyed.
* !#zh 重置所有动态图集,已有的动态图集会被销毁。
*@methodreset
*/
reset(){
for(leti=0,l=_atlases.length;i<l;i++){
_atlases[i].destroy();
}
_atlases.length=0;
_atlasIndex=-1;
},
/**
* !#en Displays all the dynamic atlas in the current scene, which you can use to view the current atlas state.
* !#zh 在当前场景中显示所有动态图集,可以用来查看当前的合图状态。
*@methodshowDebug
*@param{Boolean} show
*/
showDebug:CC_DEV&&function(show){
if(show){
if(!_debugNode||!_debugNode.isValid){
letwidth=cc.visibleRect.width;
letheight=cc.visibleRect.height;
_debugNode=newcc.Node('DYNAMIC_ATLAS_DEBUG_NODE');
_debugNode.width=width;
_debugNode.height=height;
_debugNode.x=width/2;
_debugNode.y=height/2;
_debugNode.zIndex=cc.macro.MAX_ZINDEX;
_debugNode.parent=cc.director.getScene();
_debugNode.groupIndex=cc.Node.BuiltinGroupIndex.DEBUG;
cc.Camera._setupDebugCamera();
letscroll=_debugNode.addComponent(cc.ScrollView);
letcontent=newcc.Node('CONTENT');
letlayout=content.addComponent(cc.Layout);
layout.type=cc.Layout.Type.VERTICAL;
layout.resizeMode=cc.Layout.ResizeMode.CONTAINER;
content.parent=_debugNode;
content.width=_textureSize;
content.anchorY=1;
content.x=_textureSize;
scroll.content=content;
for(leti=0;i<=_atlasIndex;i++){
letnode=newcc.Node('ATLAS');
lettexture=_atlases[i]._texture;
letspriteFrame=newcc.SpriteFrame();
spriteFrame.setTexture(_atlases[i]._texture);
letsprite=node.addComponent(cc.Sprite)
sprite.spriteFrame=spriteFrame;
node.parent=content;
}
}
}
else{
if(_debugNode){
_debugNode.parent=null;
_debugNode=null;
}
}
},
update(){
if(!this.enabled)return;
for(leti=0;i<=_atlasIndex;i++){
_atlases[i].update();
}
},
};
/**
*@modulecc
*/
/**
*@propertydynamicAtlasManager
*@typeDynamicAtlasManager
*/
module.exports=cc.dynamicAtlasManager=dynamicAtlasManager;
```