cocos2dx 2.x 资源加载和释放问题

前言:好久没来写东西了,这表示,最近没遇到太大的问题,否则就来这里吐槽了,不过最近还是学到不少的,自己也总结了不少,但是就是懒得过来写。不得不说,前段时间特别忙,时间总也不够用,总觉得浪费过来写东西有点不值得,刚好最近有点小空闲,正好昨天看崩溃日志的时候发现一个问题,仔细排查了下,也算是个小总结吧,可以带给这方面有同样困扰的盆友们。看下,可能有收获也不一定。不过浪费时间了也别怪我啊,哈哈哈。

我在cocos2d-x 2.x中的场景纹理内存有一个释放的策略,就是低端机(设定最大边长分辨率低于1000)会设定在场景析构(CCScene::~CCScene)时顺便清理下未用的CCSpriteFrameCache和CCTexture2DCache数据。并且,在iOS设备上运行时,当得到内存警告时,会主动设置在场景释放时顺便清理下纹理内存,这种策略会让即使高端机器也可以避免内存申请失败导致的崩溃。

场景节点结算顺序

这样的策略在写纹理加载的时候要遵循一定的规则,比如,不允许预加载暂时未使用的纹理(这个很头疼啊)。这部分后面解释,先看下场景加载和释放的顺序:

假设有场景A和场景B,A是当前的场景,B是待加载场景。

先看不带场景转换效果的进场,即直接使用CCDirector::sharedDirector()->replaceScene(B)的方式。

场景的结算顺序是这样的:

  1. A->onExitTransitionDidStart();
  2. A->onExit();
  3. A::~CCScene();
  4. B->onEnter();
  5. B->onEnterTransitionDidFinish();


如果B是由CCTransitionScene的效果,即,使用CCDirector::sharedDirector()->replaceScene(CCTransitionFade::create(0.5f,B));

场景的结算顺序是这样的:

  1. B->onEnter();
  2. A->onExitTransitionDidStart();
  3. A->onExit();
  4. B->onEnterTransitionDidFinish();
  5. A::~CCScene();// A场景会释放
  6. CCTransitionScene::~CCScene(); // 转换场景会释放


从上述的两个过程可以看出来,结算顺序的不同表现在原有场景的析构位置。第一种情况是当前场景(A)先释放,后新场景(B)加载的,而后一个的情况是B(新)场景先进场,然后A场景onExit,B场景onEnterTransitionDidFinish()后,A场景释放,最后转换场特效景释放。如果我们是在场景基类CCScene的析构函数中释放未使用的纹理内存,那在第一种情况下,可以工作得很好,无论是否采用预加载纹理资源的方式,而第二种就会出现问题。(稍后分析)


再看下纹理预加载的情况。纹理预加载这个在目前我的用法里面,只是为了在一个场景加载前,先把在当前场景中需要用到的纹理先全部加载进来的手段,这样在场景加载的时候可以显示一个等待框,当纹理内存加载完成后,后续场景内所用到得纹理都已经在内存中,就不需要再次去磁盘中读取数据,毕竟磁盘I/O是比内存读取慢好多个数量级,可能会造成卡顿,如果在业务场景内卡顿是比较影响体验的。并且,如果数据的预加载放在一个地方,也会比较易于管理,方便之后添加和删除,并且可以避免重复加载判断上的效率损失(引擎现有的机制会避免纹理内存重复加载)。


大致了解纹理预加载的效果后,再看下纹理预加载的位置。

目前使用的方式是,一个场景内使用到的纹理,由这个场景在管理,所以,一般是在需要加载的场景的init函数中加载,顺便还会在onEnter中加载一次,为了安全起见,还可以在onEnterTransitionDidFinish中加载一次,因为这些函数针对节点在界面上出现的过程来讲,都只被调用一次,作为纹理预加载来讲,应该是不错的入口。

预加载纹理还有个特点,就是仅仅会在CCTexture2DCache中带有引用(如果CCSpriteFrameCache的话也会有引用),因为可能没有绑定到界面的节点上,所以如果在纹理增加引用计数前被removeUnused了,内存中就没有了,如果后续使用类似CCSprite的createWithSpriteFrameName的方式,就会加载不到返回空指针。

问题来了

那么问题来了,如果是出现在有场景切换特效的情况下是如何呢?明显,在场景析构函数中会释放未用的纹理,从列表上明显看到,析构是最后发生的,在待加载场景(B)的onEnter和onEnterTransitionDidFinish函数调用后才发生的,也就是说,无论B场景之前预加载多少次,这些暂时没有用到的纹理,都会在A场景析构的时候被清理掉,这就是坑爹的地方!!!


在场景CCScene的析构函数中作纹理释放的好处显而易见:纹理数据针对场景保存,每个场景“管理”自己的纹理,当场景消失的时候释放自己的内存。

这个好处针对不带过度特效的场景切换可谓是好得不得了啊,但是问题就出在,纹理数据其实独立于场景的,场景A可以使用,场景B也可以使用,同样,如果场景A释放了,场景B直接使用就会出问题。要么在每次使用前都加载一次,要么采用每次加载的方式去调用(内部判断是否在内存中,有就直接使用,没有就按照路径去加载),但是因为有个误操作的存在:把纹理打包成一个图片,用CCSpriteFrameCache的方式去管理。其实CCSpriteFrame本来就是用作帧序列图的操作会比较合适,但是貌似用它来管理整个纹理也是很不错的选择,并且加载后,可以调用createWithSpriteFrameName这种方法去创建CCSprite,用起来还是很爽的。但是无节制的使用总会带来各种各样的问题。

因为转换特效的场景切换(使用CCTransitionScene的子类),会使得单个场景对自己的纹理管理流程被切断(如果是带原子性的操作就没OK了,但明显过渡切换的业务类型不允许原子操作——这个自己理解下,不理解也没关系),他们访问的其实是共享的资源,如果顺序不对,会导致逻辑混乱,比如预加载后续使用的纹理数据时,B场景本来以为预加载的纹理后续可以使用,但是A场景析构的时候把未使用的纹理清除了,如果B场景在使用这些纹理之前有做判断是否加载再使用就可以避免空指针的问题,但是如果没判断,就会导致纹理空指针,一访问就崩溃了。

针对引擎的纹理加载释放的代码规范

这里的纹理加载和释放,cocos2d-x 2.x中已经提供了对应的方法,但是因为控制粒度的问题,所以还是需要自己去管理。我这里会造成使用问题的关键点在于,把零碎的图片,整合打包成一个纹理,然后通过一个描述文件(*.plist)去告诉引擎如何切割得到对应的单个纹理数据。然后代码中很爽(自以为很爽)得使用createWithSpriteFrameName来生成CCSprite。并且可以直接使用图片包内的单个图片的名字去得到CCSprite,减少了好多路径代码的书写。

使用CCSpriteFrameCache的好处

开始用起来是很爽的,不得不承认,但是这样的操作其实是有问题的,就是CCSpriteFrame的出现,应该是为了帧序列图,不过这个我也没有深入探究(我能问谁?),但是它这个还正好解决了一些碎片化的小图浪费纹理内存的问题。

使用CCSpriteFrameCache的困扰

不过使用上的限制也不少,比如,小图的名字不能重复,否则,在CCSpriteFrameCache中,同样名字的CCSpriteFrame只会保留最后加载的那个,也就是说,CCSpriteFrameCache,可以看成是一个key=value的map,key是不允许重复的。这个问题会给CCSpriteFrameCache的滥用造成隐患,比如场景中的两个对话框的背景图,名字一样的,但是纹路不同(反正就是两张图的文字一样),并且是放在两个plist描述文件中的,如果两个plist被CCSpriteFrameCache一起加载,那就有一个图片通过createWithSpriteFrameName的方式会访问不到。两个对话框会使用同一个背景,这就和设计不符了。当然,CCTexture2D中,两个纹理都在(其实只是一个纹理图,不同的区块罢了)。

可参考的解决方案

其实要解决这个问题,并不一定是引擎要做的 ,当然,如果能把这层包在引擎之下,当然是最好啦。我们在使用这些API便利的同时,是需要用一些规范来限制的。

如果不使用CCSpriteFrameCache用作纹理的管理,显然不现实,小碎图那些长长的路径简直要了命了,更何况小碎图浪费内存的问题咋整?

不要使用预加载未使用的纹理,这也是个办法,就是书写的时候要费力点了,要区分,哪些数据是后期加载的,在显示之前,都要先加载一次plist。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


    本文实践自 RayWenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建实时纹理、如何用Gimp创建无缝拼接纹
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@163.com微信公众号:HopToad 欢迎转载,转载标注出处:http://blog.csdn.netotbaron/article/details/424343991.  软件准备 下载地址:http://cn.cocos2d-x.org/download 2.  简介2.1         引用C
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从Cocos2D-x官网上下载,进入网页http://www.cocos2d-x.org/download,点击Cocos2d-x以下的Download  v3.0,保存到自定义的文件夹2:从python官网上下载。进入网页https://www.python.org/downloads/,我当前下载的是3.4.0(当前最新
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发引擎,易学易用,支持多种智能移动平台。官网地址:http://cocos2d-x.org/当前版本:2.0    有很多的学习资料,在这里我只做为自己的笔记记录下来,错误之处还请指出。在VisualStudio2008平台的编译:1.下载当前稳
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《最强大脑》娱乐节目。将2048改造成一款挑战玩家对数字记忆的小游戏。邮箱:appdevzw@163.com微信公众号:HopToadAPK下载地址:http://download.csdn.net/detailotbaron/8446223源码下载地址:http://download.csdn.net/
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试以QtCreatorIDE来进行CMake构建。Cocos2d-x3.X地址:https://github.com/cocos2d/cocos2d-x1.打开QtCreator,菜单栏→"打开文件或项目...",打开cocos2d-x目录下的CMakeLists.txt文件;2.弹出CMake向导,如下图所示:设置
 下载地址:链接:https://pan.baidu.com/s/1IkQsMU6NoERAAQLcCUMcXQ提取码:p1pb下载完成后,解压进入build目录使用vs2013打开工程设置平台工具集,打开设置界面设置: 点击开始编译等待编译结束编译成功在build文件下会出现一个新文件夹Debug.win32,里面就是编译
分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net前言上次用象棋演示了cocos2dx的基本用法,但是对cocos2dx并没有作深入的讨论,这次以超级马里奥的源代码为线索,我们一起来学习超级马里奥的实
1. 圆形音量button事实上作者的本意应该是叫做“电位计button”。可是我觉得它和我们的圆形音量button非常像,所以就这么叫它吧~先看效果:好了,不多解释,本篇到此为止。(旁白: 噗。就这样结束了?)啊才怪~我们来看看代码:[cpp] viewplaincopyprint?CCContro
原文链接:http://www.cnblogs.com/physwf/archive/2013/04/26/3043912.html为了进一步深入学习贯彻Cocos2d,我们将自己写一个场景类,但我们不会走的太远,凡是都要循序渐进,哪怕只前进一点点,那也至少是前进了,总比贪多嚼不烂一头雾水的好。在上一节中我们建
2019独角兽企业重金招聘Python工程师标准>>>cocos2d2.0之后加入了一种九宫格的实现,主要作用是用来拉伸图片,这样的好处在于保留图片四个角不变形的同时,对图片中间部分进行拉伸,来满足一些控件的自适应(PS: 比如包括按钮,对话框,最直观的形象就是ios里的短信气泡了),这就要求图
原文链接:http://www.cnblogs.com/linji/p/3599478.html1.环境和工具准备Win7VS2010/2012,至于2008v2版本之后似乎就不支持了。 2.安装pythonv.2.0版本之前是用vs模板创建工程的,到vs2.2之后就改用python创建了。到python官网下载版本2.7.5的,然后
环境:ubuntu14.04adt-bundle-linux-x86_64android-ndk-r9d-linux-x86_64cocos2d-x-3.0正式版apache-ant1.9.3python2.7(ubuntu自带)加入环境变量exportANDROID_SDK_ROOT=/home/yangming/adt-bundle-linux/sdkexportPATH=${PATH}:/$ANDROID_SDK_ROOTools/export
1开发背景游戏程序设计涉及了学科中的各个方面,鉴于目的在于学习与进步,本游戏《FlappyBird》采用了两个不同的开发方式来开发本款游戏,一类直接采用win32底层API来实现,另一类采用当前火热的cocos2d-x游戏引擎来开发本游戏。2需求分析2.1数据分析本项目要开发的是一款游
原文链接:http://www.cnblogs.com/linji/p/3599912.html//纯色色块控件(锚点默认左下角)CCLayerColor*ccc=CCLayerColor::create(ccc4(255,0,0,128),200,100);//渐变色块控件CCLayerGradient*ccc=CCLayerGradient::create(ccc4(255,0,0,
原文链接:http://www.cnblogs.com/linji/p/3599488.html//载入一张图片CCSprite*leftDoor=CCSprite::create("loading/door.png");leftDoor->setAnchorPoint(ccp(1,0.5));//设置锚点为右边中心点leftDoor->setPosition(ccp(240,160));/
为了答谢广大学员对智捷课堂以及关老师的支持,现购买51CTO学院关老师的Cocos2d-x课程之一可以送智捷课堂编写图书一本(专题可以送3本)。一、Cocos2d-x课程列表:1、Cocos2d-x入门与提高视频教程__Part22、Cocos2d-x数据持久化与网络通信__Part33、Cocos2d-x架构设计与性能优化内存优
Spawn让多个action同时执行。Spawn有多种不同的create方法,最终都调用了createWithTwoActions(FiniteTimeAction*action1,FiniteTimeAction*action2)方法。createWithTwoActions调用initWithTwoActions方法:对两个action变量初始化:_one=action1;_two=action2;如果两个a
需要环境:php,luajit.昨天在cygwin上安装php和luajit环境,这真特么是一个坑。建议不要用虚拟环境安装打包环境,否则可能会出现各种莫名问题。折腾了一下午,最终将环境转向linux。其中,luajit的安装脚本已经在quick-cocos2d-x-develop/bin/中,直接luajit_install.sh即可。我的lin
v3.0相对v2.2来说,最引人注意的。应该是对触摸层级的优化。和lambda回调函数的引入(嗯嗯,不枉我改了那么多类名。话说,每次cocos2dx大更新。总要改掉一堆类名函数名)。这些特性应该有不少人研究了,所以今天说点跟图片有关的东西。v3.0在载入图片方面也有了非常大改变,仅仅只是