这节来讲纹理管理。我这里说的纹理有两个含义,一个是OpenGL中的纹理,还有一个就是CCTexture的。CCTexture是OpenGL中的纹理呈现,所以说到纹理时大部分是指CCTexture了。上次说到,我们绘制时,可以使用CCTexture中的OpenGL索引也就是name来进行纹理绑定。而且cocos2d-x使用CCTextureCache来进行统一管理。
CCTextureCache的作用很简单,为已经加载过的纹理提供一个名字绑定而已.我们直观的认为纹理应该是用string作为索引的,但是OpenGL为我们分配的却是一个GLuint值而已。所以需要用一个map来将他们一一对应起来。所以在CCTextureCache中最重要的就是一个
CCDictionary* m_pTextures。要注意的一点就是用来作为key的是全路径,而不是简单的文件名。所以原则上是可以存在多个文件名相同的纹理的,只要你把他们保存在不同的路径中。但是这会带来其他的问题。有些平台的路径是扁平的。所以最好不要在resource目录下设置子目录。以免带来移植的不便。虽然CCTextureCache本身的内容很简单,但是它却提供了几个方便快捷的函数:
void addImageAsync(const char *path, CCObject *target, SEL_CallFuncO selector);
这个函数用来做异步加载资源。如果在主线程中加载资源的话会导致主线程卡死一段时间。对用户而已,肯定是希望一个动态活泼的加载动画,而不是一个死的加载提示。所以用这个函数是很方便的。但是这个函数使用起来有个很小的不便,他始终在另外一个线程中加载资源。而且没有给出精细的加载进度。所以在使用合图的情况下会出现大段加载。大家可以hack出精细的进度出来,就是有点麻烦而已。
CCTexture2D* CCTextureCache::addUIImage(CCImage *image, const char *key)
这个函数可以加载自定义的图片,还可以使用自定义的key。有时候我们需要使用程序生成的图片作为纹理,这个函数就非常方便了。但是那个key的带path的设计使得整个代码十分dirty。大家可以看看它对路径的处理,我认为是没有做好的。
void CCTextureCache::removeUnusedTextures()
这个函数是懒人的护符,如果在项目中调用了这个函数,就说明这个项目代码没有设计好。资源管理混乱。但是在已经如此的情况下,它能简单有效的减缓内存紧张的问题。不至于让系统强杀游戏。实际上如果收到内存紧张的消息,cocos2d-x会默认的调用一个这个函数。
void CCTextureCache::dumpCachedTextureInfo()
这个函数用来输出当前的纹理信息,如果想做一些内存优化,这个函数是很方便的。
void CCTextureCache::reloadAllTextures()
这个函数也是很无奈的一个函数,他的性能不好,但是十分的方便。为什么需要这个函数呢?因为在Android上如果切换程序的话,程序自身的OpenGL上下文会被析构,导致自身的纹理全部释放掉。当程序切回来来的时候就是黑屏了。虽然现在Android上cocos2d-x能保存OpenGL的上下文,但是用的确实一个十分dirty的方法,没有按照Android原生的设计,用了比较hack的方式。虽然的确有效,但是也很危险。在没有官方支持的情况下看你项目的取舍了。话说Android的设计就没想过游戏开发的感受么。所以这个函数还是很有必要的。
看其源码CCTextureCache只是将名字缓存起来了,感觉有点假,但是在他的源码中还有一个VolatileTexture和一个std::list<VolatileTexture*> VolatileTexture::textures。这个却是货真价实的将纹理数据保存下来了。它之所以能够缓存纹理,就是因为有一个CCImage *uiImage的成员变量。而且这个函数有点特殊,他有个一个static的程序来管理自己所有的实例,也就是说VolatileTexture和VolatileTextureManager合体了。而且这个类好像就是用来辅助reloadAllTextures的,如果将当前的纹理预先保存在内存中,重载当然就会很快了。这就是靠大内存来解决设计短板啊,Android开发真苦逼。