spriteFrameCache,AnimationCache,TextureCache

关于cocos2dx常用缓存类,spriteFrameCache,AnimationCache,TextureCache,看了网上各路大神写的文章,趁有空把各路大神说的写下来总结下。

TextureCache

1.简介
在游戏中需要加载大量的纹理图片,这些操作都是很耗内存和资源的。

当游戏中有个界面用到的图片非常多,第一次点进这界面时速度非常慢(因为要加载绘制很多图片)出现卡顿,我们可以使用TextureCache提前异步加载纹理,等加载结束,进入到这个界面再使用这些图片速度就会非常快。

Texture2D: 纹理,即图片加载入内存后供CPU和GPU操作的贴图对象。
TextureCache(纹理缓存),用于加载和管理纹理。一旦纹理加载完成,下次使用时可使用它返回之前加载的纹理,从而减少对GPU和CPU内存的占用。
2.sprite类创建例子
当你创建一个精灵,你一般会使用Sprite::create(pszFileName)。假如你去看Sprite::create(pszFileName)的实现方式,你将看到它将这个图片增加到纹理缓存中去了

Sprite* Sprite::create(const std::string& filename)
{
    Sprite *sprite = new Sprite();
    if (sprite && sprite->initWithFile(filename))
    {
        sprite->autorelease();
        return sprite;
    }
    _SAFE_DELETE(sprite);
    return nullptr;
}
bool Sprite::initWithFile(const std::string& filename)
{
    ASSERT(filename.size()>0, "Invalid filename for sprite");
    Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
    if (texture)
    {
        Rect rect = Rect::ZERO;
        rect.size = texture->getContentSize();
        return initWithTexture(texture, rect);
    }
    // don't release here.
    // when load texture failed, it's better to get a "transparent" sprite then a crashed program
    // this->release();
    return false;
}
上面代码显示在控制加载纹理。一旦这个纹理被加载了,在下一时刻就会返回之前加载的纹理引用,并且减少加载的时候瞬间增加的内存。

3.获取TextureCache
在3.0版本中,TextureCache不再作为单例模式使用。作为Director的成员变量,通过以下方式获取

Director::getInstance()->getTextureCache();

4.获取纹理
如果文件名以前没有被加载时,它会创建一个新的Texture2D 对象,它会返回它。它将使用文件名作为key否则,它会返回一个引用先前加载的图像。
TextureCache屏蔽了加载纹理的许多细节;
addImage函数会返回一个纹理Texture2D的引用,可能是新加载到内存的,也可能是之前已经存在的;

Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);

也可以通过getTextureForKey方法来获得这个key所对应的纹理缓存,如果这个Key对应的纹理不存在,那么就返回NULL

Texture2D *texture = Director::getInstance()->getTextureCache()->getTextureForKey(textureKeyName);

5.异步加载纹理
TextureCache类还支持异步加载资源的功能,利用addImageAsync方法。你可以很方面地给addImageAsync方法添加一个回调方法,这样,当纹理异步加载结束的时候,可以得到通知。

你可以选择异步加载方式,这样你就可以为loading场景增加一个进度条。关键代码如下:

TextureCacheTest::TextureCacheTest(): _numberOfSprites(20),_numberOfLoadedSprites(0)
{
    auto size = Director::getInstance()->getWinSize();
    _labelLoading = Label::createWithTTF("loading...", "fonts/arial.ttf", 15);
    _labelPercent = Label::createWithTTF("%0", "fonts/arial.ttf", 15);
    _labelLoading->setPosition(Point(size.width / 2, size.height / 2 - 20));
    _labelPercent->setPosition(Point(size.width / 2, size.height / 2 + 20));
    this->addChild(_labelLoading);
    this->addChild(_labelPercent);
    // load textrues
    Director::getInstance()->getTextureCache()->addImageAsync("Images/HelloWorld.png", _CALLBACK_1(TextureCacheTest::loadingCallBack, this));
    Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini.png", _CALLBACK_1(TextureCacheTest::loadingCallBack, this));
    Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_01.png", _CALLBACK_1(TextureCacheTest::loadingCallBack, this));
    ....
}
void TextureCacheTest::loadingCallBack(cocos2d::Texture2D *texture)
{
    ++_numberOfLoadedSprites;
    char tmp[10];
    sprintf(tmp,"%%%d", (int)(((float)_numberOfLoadedSprites / _numberOfSprites) * 100));
    _labelPercent->setString(tmp);
    if (_numberOfLoadedSprites == _numberOfSprites)
    {
        this->removeChild(_labelLoading, true);
        this->removeChild(_labelPercent, true);
        addSprite();
    }
}

6.释放缓存

CCTextureCache::sharedTextureCache()->removeAllTextures();   //释放到目前为止所有加载的图片
CCTextureCache::sharedTextureCache()->removeUnusedTextures();  //将引用计数为1的图片释放掉
CCTextureCache::sharedTextureCache()->removeTexture();  //单独释放某个图片

当没有其它对象(比如sprite)持有纹理的引用的时候,纹理仍然会存在内存之间。基于这一点,我们可以立马从缓存中移除出去,这样,当纹理不存需要的时候,马上就会从内存中释放掉。
值得注意的是释放的时机,一般在切换场景的时候释放资源,如果从A场景切换到B场景,调用的函数顺序为B::init()—->A::exit()—->B::onEnter() 。
可如果使用了切换效果,比如CTransitionJumpZoom::transitionWithDuration这样的函数,则函数的调用顺序变为B::init()—->B::onEnter()—->A::exit() 。
而且第二种方式会有一瞬间将两个场景的资源叠加在一起,如果不采取过度,很可能会因为内存吃紧而崩溃。
有时强制释放全部资源时,会使某个正在执行的动画失去引用而弹出异常,可以调用CCActionManager::sharedManager()->removeAllActions();来解决。

SpriteFrameCache

1.简介
   SpriteFrameCache 主要服务于多张碎图合并出来的纹理图片。这种纹理在一张大图中包含了多张小图,直接通过TextureCache引用会有诸多不便,因而衍生出来精灵框帧的处理方式,即把截取好的纹理信息保存在一个精灵框帧内,精灵通过切换不同的框帧来显示出不同的图案。
   SpriteFrameCache的内部封装了一个Map<std::string, SpriteFrame*> _spriteFrames对象。key为帧的名称。SpriteFrameCache一般用来处理plist文件(这个文件指定了每个独立的精灵在这张“大图”里面的位置和大小),该文件对应一张包含多个精灵的大图,plist文件可以使用TexturePacker制作。

2.获取和销毁

  SpriteFrameCache* cache = SpriteFrameCache::getInstance(); //获取
  SpriteFrameCache::destroyInstance();//销毁

3.使用SpriteFrameCache获取指定的精灵帧,创建精灵对象

    SpriteFrameCache *frameCache = SpriteFrameCache::getInstance();
    frameCache->addSpriteFramesWithFile("boy.plist","boy.png");//boy.png里集合了boy1.png,boy2.png这些小图
    auto frame_sp = Sprite::createWithSpriteFrameName("boy1.png");//从SpriteFrameCache缓存中找到boy1.png这张图片.
    this->addChild(frame_sp,2);

SpriteFrameCache vs. TextureCache
SpriteFrameCache精灵框帧缓存。顾名思义,这里缓存的是精灵帧SpriteFrame,它主要服务于多张碎图合并出来的纹理图片。这种纹理在一张大图中包含了多张小图,直接通过TextureCache引用会有诸多不便,因而衍生出来精灵框帧的处理方式,即把截取好的纹理信息保存在一个精灵框帧内,精灵通过切换不同的框帧来显示出不同的图案。
跟TextureCache功能一样,将SpriteFrame缓存起来,在下次使用的时候直接去取。不过跟TextureCache不同的是,如果内存池中不存在要查找的图片,它会提示找不到,而不会去本地加载图片。

  • TextureCache时最底层也是最有效的纹理缓存,缓存的是加载到内存中的纹理资源,也就是图片资源。
  • SpriteFrameCache精灵框帧缓存,缓存的时精灵帧。
  • SpriteFrameCache是基于TextureCache上的封装。缓存的是精灵帧,是纹理指定区域的矩形块。各精灵帧都在同一纹理中,通过切换不同的框帧来显示出不同的图案。

CCSpriteFrameCache 与 CCTextureCache 释放的方法差不多,不赘述了。

AnimationCache

1.先介绍下动画。
Cocos2d-x中,动画的具体内容是依靠精灵显示出来的,为了显示动态图片,我们需要不停切换精灵显示的内容,通过把静态的精灵变为动画播放器从而实现动画效果。动画由帧组成,每一帧都是一个纹理,我们可以使用一个纹理序列来创建动画。

我们使用Animation类描述一个动画,而精灵显示动画的动作则是一个Animate对象。动画动作Animate是精灵显示动画的动作,它由一个动画对象创建,并由精灵执行(这个真的要注意一下,用lua开发的时候可能会大意用lua对象run动画然后出错,一点要注意要用精灵run animate)。

创建方法

  • 手动添加序列帧到Animation类
  • 使用文件初始化Animation类

手动添加

手动添加的方法需要将每一帧要显示的精灵有序添加到Animation类中,并设置每帧的播放时间,让动画能够匀速播放。另外,还要通过setRestoreOriginalFrame来设置是否在动画播放结束后恢复到第一帧。创建好Animation实例后,需要创建一个Animate实例来播放序列帧动画。

    auto animation = Animation::create();
    for( int i=1;i<15;i++)
    {
        char szName[100] = {0};
        sprintf(szName, "Images/grossini_dance_%02d.png", i);
        animation->addSpriteFrameWithFile(szName);
    }
    // should last 2.8 seconds. And there are 14 frames.
    animation->setDelayPerUnit(2.8f / 14.0f);
    animation->setRestoreOriginalFrame(true);
    auto action = Animate::create(animation);
    _grossini->runAction(Sequence::create(action, action->reverse(), NULL));

在创建Animation实例时会用到以下几个接口:

  • addSpriteFrame,添加精灵帧到Animation实例
  • setDelayUnits,设置每一帧持续时间,以秒为单位
  • setRestoreOriginalFrame,设置是否在动画播放结束后恢复到第一帧
  • clone,克隆一个该Animation实例

文件添加

首先我们来了解下需要用到的AnimationCache类。AnimationCache可以加载xml/plist文件,plist文件里保存了组成动画的相关信息,通过该类获取到plist文件里的动画。

在使用AnimationCache类时会用到以下几个接口:

  • addAnimationsWithFile,添加动画文件到缓存,plist文件
  • getAnimation,从缓存中获取动画对象
  • getInstance,获取动画缓存实例对象

使用文件添加的方法只需将创建好的plist文件添加到动画缓存里面,plist文件里包含了序列帧的相关信息。再用动画缓存初始化Animation实例,用Animate实例来播放序列帧动画。

    auto cache = AnimationCache::getInstance();
    cache->addAnimationsWithFile("animations/animations-2.plist");
    auto animation2 = cache->getAnimation("dance_1");
    auto action2 = Animate::create(animation2);
    _tamara->runAction(Sequence::create(action2, action2->reverse(), NULL));

2.动画缓存(AnimationCache)

通常情况下,对于一个精灵动画,每次创建时都需要加载精灵帧,按顺序添加到数组,再创建对应动作类,这是一个非常烦琐的计算过程。对于使用频率高的动画,比如走路动画,将其加入缓存可以有效降低每次创建的巨大消耗。由于这个类的目的和缓存内容都非常简单直接,所以其接口也是最简单了的,如下所示:

  • static AnimationCache* getInstance(),全局共享的单例
  • void addAnimation(Animation *animation, const std::string& name),添加一个动画到缓存
  • void addAnimationsWithFile(const std::string& plist),添加动画文件到缓存
  • void removeAnimation(const std::string& name),移除一个指定的动画
  • Animation* getAnimation(const std::string& name),获得事先存入的动画

建议:在内存警告时我们应该加入如下的清理缓存操作:

void releaseCaches()
{
AnimationCache::destroyInstance();
SpriteFrameCache::getInstance()->removeUnusedSpriteFrames();
TextureCache::getInstance()->removeUnuserdTextures();
}

值得注意的是清理的顺序,我们推荐先清理动画缓存,然后清理精[灵帧缓存]最后是[纹理缓存按照引用层级由高到低,以保证释放引用有效。
还有个坑是关于removeUnusedxx(),对象析构函数未执行完之前,它的引用计数未必会是1.
此时调用CCTextureCache::sharedTextureCache()->removeUnusedTextures();是没有效果的。因为这个函数只将引用计数为1的纹理释放掉。
以上内容主要引用自 http://www.cocoachina.com/bbs/read.php?tid-200714.html (纹理缓存TextureCache)
http://www.cocoachina.com/bbs/read.php?tid-200359.html (精灵帧缓存SpriteFrameCache)
http://www.cocoachina.com/bbs/read.php?tid-201628.html (动画缓存AnimationCache)
还有其他大神说的一些释放时机和自己踩过的坑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Cocos游戏开发中,常用的设计模式包括MVC模式和管理者模式。MVC模式是指在游戏的设计框架中,大多数程序员遵循的一种开发模式,即模型-视图-控制器模式。这种模式将游戏的逻辑分为三个部分,模型负责处理数据和逻辑,视图负责显示游戏界面,控制器负责处理用户输入和更新模型和视图的状态。在Cocos中,可以使用creator + typeSprite来实现MVC模式\[1\]。 另一个常用的设计模式是管理者模式。在Cocos中,管理者模式用于管理一组相关的对象。例如,TextureCacheSpriteFrameCacheAnimationCache等类都是管理者类,它们提供了访问对象的接口,并通过提供一个键来获取对应的值\[2\]\[3\]。这些管理者类的作用是统一管理游戏中使用的资源,提高游戏的性能。 总结起来,Cocos游戏开发中常用的设计模式包括MVC模式和管理者模式。MVC模式用于分离游戏的逻辑和界面,而管理者模式用于统一管理游戏中使用的资源。这些设计模式可以提高游戏的可维护性和性能。 #### 引用[.reference_title] - *1* [cocos creator中的MVC设计模式](https://blog.csdn.net/qq_40140218/article/details/87907635)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [cocos2dx中常见设计模式](https://blog.csdn.net/u012861978/article/details/107985321)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值