请教一个关于CCTexture2D的poi怎么使用live2d方法

(1)CCTexture2D纹理
CCTexture2D *pTexture=CCTextureCache::shareTextureCache()-&addImage(&Hero.png&);
CCTextureCache相当于CCTexture2D的容器,是内存池,用来用来缓存CCTexture2D对象的,它内部有一个字典CCMutableDictionary,key为图片的名称,值是CCTexture2D。当调用它的addImage函数添加图片时,会先根据图片名称去内存中查找是否已存在,是则直接取出返回。
如果需要一次加载多张图片的时候,可以先把图片加载到CCTextureCache中,这样使用图片的时候速度就会很快了。
(2)CCSpriteFrameCache
它是管理CCSpriteFrame的内存池,跟CCTextureCache功能一样,不过跟CCTextureCache不同的是,如果内存池中不存在要查找的帧,它会提示找不到,而不会去本地加载图片。它的内部封装了一个字典:CCDictionary
*m_pSpriteFrames,key为帧的名称。CCSpriteFrameCache一般用来处理plist文件(这个文件指定了每个独立的精灵在这张“大图”里面的位置和大小),该文件对应一张包含多个精灵的大图,plist文件可以使用TexturePacker制作
CCSpriteFrameCache::sharedSpriteFrameCache()-&addSpriteFramesWithFile(&sprite_sheet.plist&,&sprite_sheet.png&);
CCSprite *pbackground = CCSprite::createWithSpriteFrameName(&background.png&);//其中图片“background.png”在&sprite_sheet.plist&和&sprite_sheet.png&中存储。
pSpriteBatchNode-&addChild(pbackground);//pSpriteBatchNode在下面的第三点定义到,这是CCSpriteBatchNode和CCSpriteFrameCache相联系的地方,可以把通过CCSpriteFrameCache存储的纹理(该纹理必须在父节点的图片中)生成的精灵加入到pSpriteBatchNode。
(3)CCSpriteBatchNode
//第一个参数指的是用一张图片来初始化CCSpriteBatchNode(现在只存储一张父节点图片,接下来存储字节点图片的时候要在父节点的图片中),CCSpriteBatchNode的容量为200
CCSpriteBatchNode *pSpriteBatchNode=CCSpriteBatchNode::create(&sprite_sheet.png&,200);
CCSpriteBatchNode和CCSpriteFrameCache结合使用
必须保证CCSpriteFrameCache和CCSpriteBatchNode加载的是同一纹理贴图。同样是&sprite_sheet.png&。
它是批处理绘制精灵,主要是用来提高精灵的绘制效率的,需要绘制的精灵数量越多,效果越明显。因为cocos2d-x采用opengl es绘制图片的,opengl es绘制每个精灵都会执行:open-draw-close流程。而CCSpriteBatchNode是把多个精灵放到一个纹理上,绘制的时候直接统一绘制该texture,不需要单独绘制子节点,这样opengl
es绘制的时候变成了:open-draw()-draw()…-draw()-close(),节省了多次open-close的时间。CCSpriteBatchNode内部封装了一个CCTextureAtlas(纹理图集,它内部封装了一个CCTexture2D)和一个CCArray(用来存储CCSpriteBatchNode的子节点:单个精灵)。注意:因为绘制的时候只open-close一次,所以CCSpriteBatchNode对象的所有子节点都必须和它是用同一个texture(同一张图片)。
sprite = CCSprite::spriteWithTexture(pSpriteBatchNode-&getTexture(), CCRectMake(x,y,85,121));
(4)CCSpriteFrameCache和CCSpriteBatchNode的区别
CCSpriteNodePatch只是读取一张拼图,然后根据CCRect去读取该大拼图的纹理的不同部分,返回小拼图给需要的Sprite。而CCSpriteFrameCache是读取拼图和plist的,获取小纹理图时,可以根据plist中对应的文件名。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:17375次
排名:千里之外
原创:30篇
(1)(1)(2)(5)(16)(8)(2)(2)(2)CCTexture2D - WANGWEIKE的博客 - ITeye技术网站
博客分类:
1.在加载图片之前,转换像素格式
&&& 默认情况下,cocos2d里面加载图片,图片是作为作为32位加载进来的。这意味着每个像素占4个字节的内存空间。当需要非常高质量的显示效果时非常好!但是有时候需要折中一下,因为以前的设备内存很有限,如果全部使用32的像素格式来加载图片的话,会造成内存消耗过多。
&&& 当加载大的图片的时候(比如背景图片),最佳实践是使用16位的像素格式来加载–也就是牺牲一点质量来减少内存开销。cocos2d里面有很多不同的像素格式 –这个教程中,我们选择16位的像素格式,RGB565,因为背景一般不需要透明效果。(少了Alpha通道,RGBA就是有Alpha通道)
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGB565];
background = [CCSprite spriteWithFile:@"blue-shooting-stars.png"];
background.anchorPoint = ccp(0,0);
[self addChild:background];
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_Default];
2.CCSpriteFrameCache加载的是一张拼接过的大图,每一个小图只是大图中的一个区域,这些区域信息都在plist文件中保存。用的时候只需要根据小图的名称就可以加载到这个区域。
CCTextureCache 是普通的图片缓存,我们所有直接加载的图片都会默认放到这个缓存中,以提高调用效率。
addImage的参数是资源中的直接存在图片文件名
[[CCTextureCache sharedTextureCache] addImage:@"stars.png"];
3.调用CCSpriteFrameCache的addSpriteFramesWithFile方法,然后把Zwoptex生成的plist文件当作参数传进去。这个方法做了以下几件事:
o寻找工程目录下面和输入的参数名字一样,但是后缀是.png的图片文件。然后把这个文件加入到共享的CCTextureCache中。
o解析plist文件,追踪所有的sprite在spritesheet中的位置,内部使用CCSpriteFrame对象来追踪这些信息。
4.优化cocos2d/x程序的内存使用和程序大
http://xiaominghimi./5955
& iOS和android游戏纹理优化和内存优化
/demo_c89_i33080.html
浏览: 43276 次
来自: Mars
locationFromTouches 是自定义的函数吧cocos2d-x的Sptrite Action SpriteFrame Animation texture使用的简单方法---学习笔记
cocos2d-x的Sptrite Action SpriteFrame Animation texture使用的简单方法---学习笔记
第一次写这东西,还是一边学习一边写的,主要目的是作为个人的笔记。各位看客,多多包含。
一边看代码摸索,一边找网上的资料总结。
& 这个链接对于下面的一些关键对象解释的很给力,对于理解很有帮助,初学游戏编程的可以看看。
spriteframeCache 图片资源的缓存,在程序的内存空间只存在一个.包含Texture2D信息
它是管理CCSpriteFrame的内存池,跟CCTextureCache功能一样,不过跟CCTextureCache不同的是,如果内存池中不存在要查找的帧,它会提示找不到,而不会去本地加载图片。它的内部封装了一个字典:CCDictionary *m_pSpriteFrames,key为帧的名称。CCSpriteFrameCache一般用来处理plist文件(这个文件指定了每个独立的精灵在这张“大图”里面的位置和大小),该文件对应一张包含多个精灵的大图,plist文件可以使用TexturePacker制作。
TextureCache
它相当于CCTexture2D的容器,是内存池,用来缓存CCTexture2D对象的,它内部有一个字典CCMutableDictionary m_pTextures,key为图片的名称,值是CCTexture2D。当调用它的addImage函数添加图片时,会先根据图片名称去内存中查找是否已存在,是则直接取出返回
Texture2D&&&&&&&&&&
图片的纹理信息,认为是一张图片的全部信息,可以使用Rect获取整个图片中的部分信息
可以把它看成一个纹理,它是cocos2d-x渲染图形的重要参数,用来贴图,因为cocos2d-x使用opengl es绘制2d图形的,它的尺寸是2的n次方。一般通过以下方式获得:
CCTexture2D* cache = CCTextureCache::sharedTextureCache()-&addImage(&hero.png&);
spriteframe &&&&&&&&
使用Texture2D的信息来初始化,Texture2D可以是一个spriteFrame,也可以是多个SpriteFrame组成的。如果Texture2D包含多个SpriteFrame,那么图片资源此时可能是一张由多个图片和成出来的,使用合成图片是为了提高效率。
SpriteFrame表示精灵的某一帧,大多数情况下精灵本身的图片有多帧。它内部封装了Texture2D和Rect,可以从一个大图片取出一部分作为一帧。
就是游戏中一个对象,可以理解为在界面上的一个容器用来装图片的,将图片装在这个sprite容器里面用来显示。
加载SpriteFrameCache 方法& :
SpriteFrameCache::getInstance()-&addSpriteFramesWithFile(const char *pszPlist);& 使用.Plist文件一次将合成的PNG图片中的信息分离,构成一个frame cache词典,每一个frame纹理对应一个名字(在Plist文件中会将每一个对应的图片和名字对应)。这些信息保存在缓存中,在使用的时候,可以通过SpriteFrameCache::getInstance()-&getSpriteFrameByName(const char*framename),frame
cache中其中一个对应 的frame 。
&&&&&&&&& 如果这些图片是一连串动作的分解图片,存在Plist文件,为了在游戏中呈现出动画效果,需要做一些处理。
&&&&&&&&& 1.首先,将这些图片保存在一个数组中,Array* animFrames = Array::createWithCapacity(int NUM),为了动作好看,顺序肯定是要控制好的。
&&&&&&&&& 2.然后使用这个动作数组创建一个动画对象 Animation *animation = Animation::createWithSpriteFrames(animFrames, 0.2f);& 0.2f应该是2个图片刷新之间的间隔,这个函数没有看,个人猜测。
&&&&&&&&& 3.AnimationCache::getInstance()-&addAnimation(animation, &dance&);& 。(add://后来看了下代码,发现这个操作的作用仅仅是给动作建立一个索引,方便信息的管理,对于显示并没有实际的用处;但是由于getInstance()方法是一个静态的方法,所以如果这样保存后,在其他位置使用的时候可以方便很多)
&&&&&&&&& AnimationCache::getInstance()-&addAnimationsWithFile(&animations/animations.plist&);& animations.plist文件中指定的frame文件必须已经创建了FrameCache。
animations.plist文件里面可以包含多个动画,每一个动画由一组图片构成。
&&&&&&&&& 很明显了addAnimation()方法和addSpriteFramesWithFile(const char *pszPlist)方法一样的目的,都是将图片信息保存到缓存,在需要使用的时候,可以高效的调用;
&&&&&&&&& 不同之处是前者是将一个动画保存起来(虽然animation是由多个SpriteFrame图片构成的,但是实际上可以认为这就是一个已经组织好的动画片段,可以直接调度使用);
&&&&&&&&& SpriteFrameCache::getInstance()-&addSpriteFramesWithFile(const char *pszPlist);是一次将多个图片保存起来,并且构成一个索引(后者对应的一次处理一个图片),创建的是SpriteFrame的集合,由于图片的顺序可能并不是程序员所期望的,在使用的时候需要程序员自己确定使用的是索引中的哪一个SpriteFrame。
&&&&&&&&& 4.mAnimation = AnimationCache::getInstance()-&animationByName(const char* name)& 获取之前添加到Animationcache的索引中的动画,根据需要设置动画结束后的动作mAnimation-&setRestoreOriginalFrame(true); 这个是设置还原为初始frame。
&&&&&&&&& 5.创建mAnimate = Animate::create(mAnimation);
&&&&&&&&& 可以直接Sprite::create()-&runAction(Action mAction),也可以根据需要使用mAnimate创建如下2个Action的子类对象,
&&&&&&&&&& ActionInterval *seq = RepeatForever::create(animB);
& & &&&&&& Sequence *seq = Sequence::create(animB,NULL);
Action子类很多,其他的子类还没看到。应该还有其他的对象可以用。
上面的图片有对应的Plist文件,那么如果没有Plist文件的图片该怎么处理呢?
根据指定路径下的图片创建图片纹理& CCTexture2D CCTextureCache::sharedTextureCache()-&addImage(const std::string &&filepath)
如果图片的纹理仅仅存在一个元素(不是合成的图片),可以直接使用纹理创建精灵Sprite::createWithTexture(&*&texture),也可以直接根据图片文件创建精灵Sprite::create(const
char *filename);
如果图片是合成的包含很多元素,Sprite::create(const char *filename, cosnt Rect& rect)获取图片中一个指定的元素来创建一个精灵;根据文件创建SpriteFrame的方法SpriteFrame::create(const char *filename, cosnt Rect& rect),根据纹理创建SpriteFrame的方法createWithTexture(
&*&texture, cosnt Rect& rect)
Rect这个结构体指定了合成图片中一个元素在图片中的位置。
我的热门文章
即使是一小步也想与你分享本周问答荣誉榜
本月问答荣誉榜
含有标签"CCImage"的问题
curSprite-&setAnchorPoint(oint(0, 0));
CCRenderTexture *renderer=CCRenderTexture::create(curSprite-&getContentSize().width, curSprite-&getContentSize().height, kCCTexture2DPixelFormat_RGBA8888);
renderer-&begin();
renderer-&visit();
renderer-&end();
CCImage * img=renderer-&newCCImage();
unsigned char *data_ = img-&getData();
请问接下来如何获得curSprite上每一个像素的argb值?&
请教如何将一个CCTexture2D纹理转化为CCImage图片。
知道的兄弟姐妹们麻烦解答一下。谢了!6350人阅读
cocos2d-x(41)
1.通常情况下用PVR格式的文件来进行图片显示的时候,在运行速度和内存消耗方面都要比PNG格式要快和小。一般情况下PVR消耗的内存比PNG消耗的内存小25%左右。PVR格式可以用ZWoptex导出。PVR是apple芯片能直接读取和显示的文件.
2.图片抗锯齿处理。
图片放大时的处理:
图片在放大的时候会出现锯齿。纹理类提供了setAntiAliasTexParameters()函数来处理抗锯齿。当图片放大的时候会使用相邻的四个像素进行混合运算。从而消除锯齿。但是会让图片产生模糊的感觉。
左边使用的就是setAntiAliasTexParameters()。和右边的图片相比无锯齿,但是有模糊的感觉.右边的图片使用的是setAliasTexParameters()。不模糊,但是有锯齿。
图片缩小时的抗锯齿处理:
使用,缩小时效果会更好,左边使用
3.cocos2d-x的纹理类支持从多种格式的文件加载图片.包括:.png&, .jpeg, .pvr, .gz, .webp, .tiff, .ccz等格式的文件 。
4.最大限度的通过一些方法和手段减小纹理对内存消耗.
(1) 通过CCTexture2D来修改纹理的像素格式,可以减少纹理占用的内存大小和渲染的速度。
使用16-bit纹理
最快速地减少纹理内存占用的办法就是把它们作为16位颜色深度的纹理来加载。cocos2d默认的纹理像素格式是32位颜色深度。如果把颜色深度减半,那么内存消耗也就可以减少一半。并且这还会带来渲染效率的提升,大约提高10%。
你可以使用CCTexture2D对象的类方法来更改默认的纹理像素格式,代码如下:
这里有个问题:首先,纹理像素格式的改变会影响后面加载的所有纹理。因此,如果你想后面加载纹理使用不同的像素格式的话,必须再调用此方法,并且重新设置一遍像素格式。
其次,如果你的CCTexture2D设置的像素格式与图片本身的像素格式不匹配的话,就会导致显示严重失真。比如颜色不对,或者透明度不对等等。
有哪些比较有用的纹理像素格式呢?
RGBA8888是默认的格式。对于16位的纹理来说,使用RGB565可以获得最佳颜色质量,因为16位全部用来显示颜色:总共有65536总颜色值。但是,这里有个缺点,除非图片是矩形的,并且没有透明像素。所以RBG565格式比较适合背景图片和一些矩形的用户控件。
RBG5A1格式使用一位颜色来表示alpha通道,因此图片可以拥有透明区域。只是,1位似乎有点不够用,它只能表示32768种可用颜色值。而且图片要么只能全部是透明像素,或者全部是不透明的像素。因为一位的alpha通道的缘故,所以没有中间值。但是你可以使用fade in/out动作来改变纹理的opacity属性。
如果你的图片包含有半透明的区域,那么RBGA;式很有用。它允许每一个像素值有127个alpha值,因此透明效率与RGBA;式的纹理差别不是很大。但是,由于颜色总量减少至4096,所以,RBGA4444是16位图片格式里面颜色质量最差的。
现在,你可以得到16位纹理的不足之处了:它由于颜色总量的减少,有一些图片显示起来可能会失真,而且可能会产生“梯度”。
(2)避免一个接一个地加载PNG和JPG纹理(他们之间至少等待一帧)
cocos2d里面纹理加载分为两个阶段:1.从图片文件中创建一个UIImage对象。2.以这个创建好的UIImage对象来创建CCTexture2D对象。这意味着,当一个纹理被加载的时候,在短时候内,它会消耗两倍于它本身内存占用的内存大小。(译注:为什么只是短时间内呢?因为autoRelease pool和引用计数的关系,临时创建的UIImage对象会被回收。)
当你在一个方法体内,接二连三地加载4个纹理的时候,这个内存问题会变得更加糟糕。因为在这个方法还没结束之前,每一个纹理都会消耗两倍于它本身的内存。
我不是很确定,现在的cocos2d是否仍然如此。或者这种情况是否只适用于手工引用计数管理,或许ARC不会如此呢?我习惯于按顺序加载纹理,但是在加载下一个纹理之前要等待一帧。这将会使得任何纹理加载的消耗对内存的压力降低。因为等待一帧,引用计数会把临时的UIImage对象释放掉,减少内存压力。此外,在后续的文章中,如果你想在背景线程中按序加载纹理的话,也可以采用这种方法。
(3)iphone中不要使用jpg来创建纹理对象
cocos2d-iphone使用JPG纹理的时候有一个问题。因为JPG纹理在加载的时候,会实时地转化为PNG格式的纹理。这意味着cocos2d-iphone加载纹理是非常慢的(这里有),而且JPG纹理将消耗三倍于本身内存占用大小的内存。
一个大小的纹理会消耗16M的内存。当你加载它的时候,在短时间内,它将消耗32MB内存。现在,如果这个图片是JPG格式,你会看到这个数字会达到48MB,因为额外的UIImage对象的创建。虽然,最终内存都会降到16M,但是,那一个时刻的内存飙高,足以让os杀死你的游戏进程,造成crash,影响用户体验。
JPG不论在加载速度和内存消耗方面都很差。所以,千万不要使用JPG!
(4)忽视文件图片大小
这种情况,我见到很多。它乍听起来可能觉得有点荒诞,但事实如此,因为它需要关于文件格式的知识,而这些知识并不是每一个程序员都了解的。我经常听到的论断就是“嘿!我的程序不可能有内存警告,我所有的图片资源加起来还不到30MB!”。
怎么说呢,因为图片文件大小和纹理内存占用是两码事。假设他们是帐篷。图片文件就相当于帐篷被装在行李箱。但是,如果你想要使用帐篷的话,它必须被撑起来,被“膨胀”。
图片文件和纹理的关系与此类似。图片文件大多是压缩过的,它们被使用的话必须先解压缩,然后才能会GPU所处理,变成我们熟知的纹理。一个的png图片,采用32位颜色深度编码,那么它在磁盘上占用空间只有2MB。但是,如果变成纹理,它将消耗16MB的内存!
当然,减少纹理占用内存大小是有办法滴。
第一个办法就是:上面的条款(1),用SetDefaultAlphaPixelFormat()来实现.尽量使用颜色深度为16bit的图片。但是条款 (1)也提到了,如果图片本身的颜色深度如果是32位。但是用SetDefaultAlphaPixelFormat()设置成16位。则可能让图片失真.图片看起来就很模糊,这当然是有解决的办法,庆幸我们有TexturePacker(TP)工具.
TP有一个特性叫做“抖动”,它可以使得原本由于颜色数量减少而产生的失真问题得到改善。(TP里面有很多抖动算法,关于这些算法,读者可以参考我翻译的)。
特别是在拥有Retina显示的像素密度下,你几乎看不出16位与32位的纹理之间的显示差别。当然,前提是你需要采用“抖动”算法。
第二个办法是:使用NPOT纹理
NOPT是“non power of two”的缩写,译作“不是2的幂”。NPOT stands for “non power of two”. 在cocos2d1.x的时候,你必须在ccConfig.h文件中开启对NPOT的支持,但是,cocos2d 2.x就不需要了,它默认是支持NPOT的。所有3代(iphone 3GS)以后的ios设置都支持cocos2d 2.x(因为它们支持OpenGL ES2.0),所以也都能支持NPOT纹理。
如果纹理图集(texture atlas)使用NPOT的纹理,它将有一个具大的优势:它允许TP更好地压缩纹理。因此,我们会更少地浪费纹理图集的空白区域。而且,这样的纹理在加载的时候,会少使用1%到49%左右的内存。而且你可以使用TP强制生成NPOT的纹理。(你只需要勾选“allow free size”即可)
为什么要关心NPOT呢?因为苹果的OpenGL驱动有一个bug,导致如果使用POT的纹理,则会产生。
第三个办法是:默认使用PVR格式的纹理
TP让你可以创建PVR格式的纹理。除了PVR纹理支持NPOT外,它们不仅可以不是2的幂,而且还可以不是方形的。
PVR是最灵活的纹理文件格式。除了支持标准的未压缩的RGB图片格式外,不支持有损压缩的pvrtc格式。另外,未压缩的pvr格式的纹理的内存消耗非常地低。不像png图片那样要消耗2倍于本身内存占用大小的内存,pvr格式只需要消耗纹理本身内存大小再加上一点点处理该图片格式的内存大小。
pvr格式的一个缺点就是,你不能在Mac上面打开查看。但是,如果你安装了TP的话,就可以使用TP自带的pvr图片浏览器来浏览pvr格式的图片了。(强烈建议大家购买TP,支持TP,不要再盗版了)
使用PVR格式的文件几乎没有缺点。此外,它还可以极大地提高加载速度,后面我会解释到。
使用pvr.ccz文件格式
在三种可选用的pvr文件格式中,优先选择pvr.ccz格式。它是专门为cocos2d和TP设计的。在TP里面,这是它生成的最小的pvr文件。而且pvr.ccz格式比其它任何文件格式的加载速度。
当在cocos2d里面使用pvr格式的纹理时,只使用pvr.ccz格式,不要使用其它格式!因为它加载速度超快,而且加载的时候使用更少的内存!
当视觉察觉不出来的时候,可以考虑使用PVRTC压缩
PVR纹理支持PVRTC纹理压缩格式。它主要是采用的。如果拿PVRTC图片与JPG图片作对比的话,它只有JPG图片中等质量,但是,最大的好处是可以不用在内存里面解压缩纹理。
这里把32位的png图片(左边)与最佳质量的PVRTC4(4位)图片(点击图片查看完整的大小)作对比:
注意,在一些高对比度的地方,明显有一些瑕疵。有颜色梯度的地方看起来还好一点。
PVRTC肯定不是大部分游戏想要采用的纹理格式。但是,它们对于粒子效果来说,非常适用。因为那些小的粒子在不停地移动、旋转、缩放,所以你很难看出一些视觉瑕疵。
预先加载所有的纹理
就像标题所说,尽你所能,一定要预先加载所有的纹理。如果你的所有的纹理加起来不超过80MB内存消耗的话(指的是拥有Retina显示的设备,非Retina的减半考虑),你可以在第一个loading场景的时候就全部加载进来。
这样做最大的好处在于,你的游戏体验会表现得非常平滑,而且你不需要再担心资源的加载和卸载问题了。
这样也使得你可以让每一个纹理都使用合适的纹理像素格式,而且可以更方便地找出其它与纹理无关的内存问题。因为如果与纹理有关,那么在第一次加载所有的纹理的时候,这个问题就会暴露出来的。如果所有的纹理都加载完毕,这时候再出现内存问题,那么肯定就与纹理无关了,而是其它的问题了。
如果你知道问题与纹理无关的话,那么你查找剩下的内存问题将会变得更加简单。而且你避免了前面说的这种情况:当的纹理加载的时候,它本来只需要消耗16MB内存,但是短时间会冲到32MB内存。后面会提出一种方法来解决“间歇性内存飙高”(“译者发明滴”)的方法。(译者:希望下次开发者的对话中“间歇性内存飙高”的说法会出现,呵呵)
按照纹理size从大到小的顺序加载纹理
由于加载纹理时额外的内存消耗问题,所以,采用按纹理size从大到小的方式来加载纹理是一个最佳实践。
假设,你有一个占内存16MB的纹理和四个占用内存4MB的纹理。如果你首先加载4MB的纹理,这个程序将会使用16MB的内存,而当它加载第四张纹理的时候,短时间内会飙到20MB。这时,你要加载16MB的那个纹理了,内存会马上飙到48MB(4*4 + 16*2),然后再降到32MB(4*4 + 16)。
但是,反过来,你先加载16MB的纹理,然后短时候内飙到32MB。然后又降到16MB。这时候,你再依次加载剩下的4个4MB的,这时,最多会彪到(4*3 + 4*2 + 16=36)MB。
在这两种情况下,内存的峰值使用相差12MB,要知道,可能就是这12MB会断送你的游戏进程的小命哦!
避免在收到内存警告消息的时候清除缓存
我有时候看到了一种奇怪的“自己开枪打自己的脚”的行为:纹理已经全部在Loading场景里面加载完毕了,这时候,内存警告发生了,然后cocos2d就会把没有使用的纹理从缓存中释放掉。
听起来不错,没有使用到的纹理都被释放掉了,但是!。。。
你刚刚把所有的纹理都加载进来,还没有进入任何一个场景中(此时所有的纹理都被当作“unused”),但是马上被全部从texture cache中移除出去。可是,你又需要在其它场景中使用它们。怎么办?你需要接着判断,如果有纹理没有加载,就继续加载。但是,一加载,由于“间歇性内存飙高”,又马上收到了内存警告,再释放,再判断,再加载。。。。 我的天,这是一个死循环啊!这也能解释为什么有些童鞋,在loading场景完了之后进入下一个场景 的时候很卡的原因了。
现在,当我收到内存警告的时候,我的做法是----什么也不做。内存警告仍然在发生,但是,它只是在程序刚开始加载的时候。我知道这是为什么,因为“间歇性内存飙高”嘛,所以,我不去管它。(但是,如果是游戏过程中再收到内存警告,你就要注意了,因为这时候可能你有内存泄漏了!!!)
我有时候会想办法改善一下,通过移除掉一些不使用的纹理和一些只有在很特殊的场景才会使用的图片(比如settings界面,玩家是不经常访问的)。然后,不管什么时候,当我需要某张图片的时候,我会首先检查一下该sprite frame是否在cache中,如果没有就加载。你会在后面看到具体的做法。
理解在什么时候、在哪里去清除缓存
不要随机清除缓存,也可以心想着释放一些内存而去移除没有使用的纹理。那不是好的代码设计。有时候,它甚至会增加加载次数,并多次引发“间歇内存飙高”。分析你的程序的内存使用,看看内存里面到底有什么,以及什么应该被清除,然后只清除该清除的。
你可以使用方法来观察哪些纹理被缓存了
这个方法的输出如下:(为了清楚起见,我把那些与-hd后缀有关的信息屏蔽掉了)&
上面包含了非常多有用的信息。纹理的大小、颜色深度(bpp)和每一个被缓存的纹理在内存中所占用大小等。这里的“rc”代表纹理的“引用计数”。如果这个引用计数等于1或2的话,那么意味着,这个纹理当前可能不会需要使用了,此时,你可以放心地把它从纹理cache中移除出去。
你只移除你知道在当前场景下不太可能会被使用的纹理(即上面介绍的引用计数为1或2的情况),这是一个明智的做法。另外,只移除那些占用内存大的纹理。如果一个纹理只占几个kb的内存,其它移不移除都没什么太大的影响。(译注:这就和程序优化一样,不要做过多的细节优化,不要过早优化,要找到性能的瓶颈,然后再重点优化,以20%的时间换取80%的效率。过早和过多细节优化对于大多数程序而言,是需要极力避免的)。
SpriteFrames retain textures!
上面提到的例子中,纹理的引用计数可能有点让人看不懂。你会发现,纹理集有很高的retain count,即使你知道这些纹理集中的纹理当前并没有被使用。
你可能忽略了一件事:CCSprteFrame会retain它的纹理。因此,如果你使用了纹理集,你要完全移除它不是那么容易。因为,由这个纹理集产生的sprite frame还是保留在内存中。所以,你必须调用CCSpriteFrameCache的方法,能彻底清除纹理缓存中的纹理集。(译注:记住,不是你调用对象的release方法了,对象的内存就会被释放掉,而是引用计数为0了,内存才会被删除)
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFramesFromTexture:uncachedTexture];
&你也可以使用&,并指定一个纹理集的.plist文件来清除缓存起来的精灵帧(spriteframes).
添加 SpriteFrames 非常耗时, 每次都是!
Note:&这一点只针对cocos2d v1.0有效,而cocos2d v2.x在加载之前会预先判断。
这样看起来有点无知(innocent):
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@&ui.plist&];
但是,要注意,CCSpriteFrameCache并不会去检查一个精灵帧是否已经被缓存起来了!这与CCTextureCache的动作方式有所不同,它每次都会去加载spriteframes.
这个过程到底需要耗费多少时间呢,这取决于你提供的.plist文件中精灵帧的数量。我注意到,只有14帧的plist加载与有280帧的plist加载有着很大的区别。所以,对于精灵帧的加载,你也需要谨慎。
所以,你要避免一些不必要的addSpriteFrames*方法调用。因为那边导致场景切换时产生小的卡顿。
你可以清除任何缓存(比如animation,sprite frames等),但是请不要轻易清除纹理缓存
cocos2d有许多缓存类,比如纹理缓存、精灵帧缓存,动画缓存等。但是,如果你想清理内存的话,精灵帧缓存和动画缓存对内存的占有是非常少的,可以说是极少的。
当然,如果你想从内存中移除一个纹理,你也必须移除与之相关的精灵帧(因为精灵帧会retain纹理)。说白了,不要轻易去移除精灵帧和动画缓存,因为你有可能会使用到一个没有缓存的动画帧对象或者精灵帧对象,那样会导致程序crash。
例外:检查声音文件的内存使用!
声音文件会被缓存起来,然后可以重复播放而不会被中断。由于声音文件一般比较大,特别是,我看到有一些开发者使用没有压缩的声音文件作为游戏的背景音乐,而这些背景音乐文件非常大,它们通常会造成大量的内存消耗。
请使用MP3格式的声音文件。因为使用没有压缩的声音文件既浪费内存又占用程序大小。当你加载完一些游戏音效时,在不需要的时候,记得要卸载掉。在第二篇文章中,我会向大家介绍有于声音文件更多的知识。
如何避免缓存特定的纹理
如果你有一个纹理,你确实不想缓存起来,那怎么办呢?比如,在初始的加载场景中的图片,或者那些用户很少会在意的图片--比如你的非常牛比的致谢场景的图片。
经常容易被误解的一点是,一个纹理显示出来了,那么它就被缓存起来了。如果你从缓存中移除此纹理,那么此时你再移除精灵就会程序崩溃。这个理解不正确。
CCTextureCache只不过是对纹理再添加了一次retain函数的调用,这样,当没有其它对象(比如sprite)持有纹理的引用的时候,纹理仍然会存在内存之间。基于这一点,我们可以立马从缓存中移除出去,这样,当纹理不存需要的时候,马上就会从内存中释放掉。如下代码所示:
bg = [CCSprite spriteWithFile:@&introBG.png&];
// don't cache this texture:
[[CCTextureCache sharedTextureCache] removeTextureForKey:@&introBG.png&];
&你需要记住,当你从CCTextureCache中移除一个纹理的时候,cocos2d下一次在调用spriteWithFile的时候,还是会再加载该纹理的--不管是否有没有一张名字一样的图片正在被其它精灵所使用。因此,如果你不够细心的话,你有可能最后会在内存中加载两张重复的纹理。
有一个例子就是,当你在循环中加载纹理,而这些纹理你并不想缓存起来。这种情况下,你就需要在循环之外去移除此纹理的缓存,否则可能会导致多个纹理被重复加载到内存之中:
NSArray* highscores = [Achievements sharedAchievements].
for (HighscoreData* data in highscores)
NSString* entry = [NSString stringWithFormat:@&%05u&, data.score];
CCLabelAtlas* label = [CCLabelAtlas labelWithString:entry
charMapFile:@&pipizahlen.png&
itemWidth:18
itemHeight:27
startCharMap:'.'];
[labelsNode addChild:label z:10];
// don't hold on to this texture:
[[CCTextureCache sharedTextureCache] removeTextureForKey:@&pipizahlen.png&];
&上面这个例子是我从highscore场景中抠出来的,一旦此场景退出,就不应该持有CCLabelAtlas纹理的引用。因此,我们需要把它从纹理缓存中移除出去。但是,你必须防止重复加载纹理到内存中去。
通过这种方式,我们可以非常方便地清除缓存中的纹理,而且最好是在创建纹理的时候清除,而不要在其它地方,比如dealloc或者索性让purge cache去做这个事。
使用一个Loading 场景
如果你不能预先加载所有的纹理的话,你可以使用一个loading场景,同时显示一个动画来表明加载的进度。这样可以在进入下一个场景之前,让前面一个场景销毁,同时释放它所占用的内存资源。
&实现起来非常简单。这个loading场景调度一个selector,然后每一帧(或者0.1秒也可以)执行一个函数,比如update。除非你前面一个场景有内存泄漏,否则的话,每一次update函数执行的时候,都会把一些引用计数为0的内存资源释放掉。在这个update方法里面,你可以创建新的场景。
这样极大地避免了“间歇性内存飙高”的问题,可以极大地减小内存压力。
在后台加载纹理
CCTextureCache类还支持异步加载资源的功能,利用方法。你可以很方面地给addImageAsync方法添加一个回调方法,这样,当纹理异步加载结束的时候,可以得到通知。
这一点非常重要:你必须等待一个资源加载完毕。否则的话,由于“间歇性内存飙高”,可能会引发下列问题:
1) 程序崩溃
2) 纹理被加载两次!因为异步加载并不能保证加载顺序。
在后台加载其它游戏资源
可是,我们并没有方法来异步加载sprite frames和其它资源。但是,我们可以借助来实现类似的异步加载的功能:
[self performSelectorInBackground:@selector(loadSpriteFrames:) withObject:nil];
&里面的selector方法只接收一个object参数(但是并没有使用)。然后就可以在此这方法里面异步加载资源了,如下所示:
-(void) loadSpriteFrames:(id)object
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@&hilfe.plist&];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@&home.plist&];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@&ui.plist&];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@&gameover.plist&];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@&ui-ingame.plist&];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@&settings.plist&];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@&digits.plist&];
&这样做最大的好处在于,你加载资源的同时,loading场景还可以播放动画,可以添加精灵并运行一些action,这一切可以处理得很平滑。这种优势甚至在单个CPU的机器上面也表现得不错,但是如果你的设备有多个cpu的话效果更佳。
但是,你需要注意,你不能在后台线程加载纹理,你必须使用addImageAsync方法。这是因为纹理必须与公共的OpenGL context在相同的线程中加载。这样,你就必须先异步加载纹理,然后再去后台加载sprite frames.你不能依靠CCSpriteFrameCache在后台线程中加载纹理。
按顺序加载游戏资源
下面的代码,是我采用的异步加载纹理和精灵帧的方法(在另外一个线程中加载:)
假设loadAssetsThenGotoMainMenu方法每一帧都会被触发。assetLoadCount和loadingAsset变量被声明在类接口中,分别 是init和bool类型:
-(void) increaseAssetLoadCount
assetLoadCount++;
loadingAsset = NO;
-(void) loadAssetsThenGotoMainMenu:(ccTime)delta
NSLog(@&load assets %i&, assetLoadCount);
switch (assetLoadCount)
if (loadingAsset == NO)
loadingAsset = YES;
NSLog(@&============= Loading home.png ===============&);
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGB5A1];
[[CCTextureCache sharedTextureCache] addImageAsync:@&home.png&
target:self
selector:@selector(increaseAssetLoadCount)];
if (loadingAsset == NO)
loadingAsset = YES;
[self performSelectorInBackground:@selector(loadSpriteFrames:) withObject:nil];
// extend with more sequentially numbered cases, as needed
// the default case runs last, loads the next scene
[self unscheduleAllSelectors];
MainMenuScene* mainMenuScene = [MainMenuScene node];
[[CCDirector sharedDirector] replaceScene:mainMenuScene];
&当这个方法运行到第一个case语句的时候,为了避免同样的图片被加载多次,我们把loadingAsset标记设置为yes。当纹理加载完后,我们就添加increaseAssetLoadCount(这个数量可以用来显示进度条加载百分比)。后面的case语句还可以加载更多的其它东西,比如声音、字体文件、粒子效果、物理配置文件、关卡信息等。不管加载多少东西,最后的default语句会执行,然后就可以进入MainMenuScene了。
这个方法的通用之处是,你可以通过case与assetLoadCount来异步加载多个纹理,同时又能避免“间歇性内存飙高”的问题。因为每帧调用一次方法的时候,前面纹理加载多出来的临时内存已经被释放掉了。因为当前线程栈顶的autoRelease pool会在每一帧渲染之前被清空。
后记:这里介绍的内容虽然是针对cocos2d-iphone的,但是,绝大部分内容是适合cocos2d-x的。因此,开发者大可放心去试用这些方法,如果大家有更好的优化游戏内存使用的方法,欢迎分享。希望此帖能成为cocos2d内存问题的终极解决方案帖。如果大家觉得我翻译的不错,希望能点一下旁边的推荐按钮。Thanks, enjoy!:)
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:669026次
积分:7331
积分:7331
排名:第2263名
原创:160篇
转载:44篇
译文:12篇
评论:136条
广州前端开发:
杭州前端开发:
杭州嵌入式开发:
文章:18篇
阅读:31561
阅读:31340
(1)(2)(2)(1)(1)(1)(2)(1)(1)(2)(2)(1)(1)(1)(3)(7)(9)(3)(5)(23)(35)(34)(19)(42)(2)(2)(1)(8)(1)(1)

我要回帖

更多关于 使用box2d 的文章

 

随机推荐