GVKun编程网logo

【Cocos2d-x 3.x】 精灵帧缓存和纹理缓存

8

最近很多小伙伴都在问【Cocos2d-x3.x】精灵帧缓存和纹理缓存这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展cocos2d3.x纹理缓存、cocos2d-iphone–需要

最近很多小伙伴都在问【Cocos2d-x 3.x】 精灵帧缓存和纹理缓存这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展cocos2d 3.x 纹理缓存、cocos2d-iphone – 需要帮助理解精灵和纹理、Cocos2d-x -- 纹理缓存(Texture Cache)、cocos2d-x 3.0 精灵帧缓存(SpriteFrameCache)等相关知识,下面开始了哦!

本文目录一览:

【Cocos2d-x 3.x】 精灵帧缓存和纹理缓存

【Cocos2d-x 3.x】 精灵帧缓存和纹理缓存

转自泰然网(Cocos2d-x 3.x官方文档):精灵帧缓存:http://www.tairan.com/archives/6378/ 纹理缓存:http://www.tairan.com/archives/6432/


精灵帧缓存

简介

SpriteFrameCache 主要服务于多张碎图合并出来的纹理图片。这种纹理在一张大图中包含了多张小图,直接通过TextureCache引用会有诸多不便,因而衍生出来精灵框帧的处理方式,即把截取好的纹理信息保存在一个精灵框帧内,精灵通过切换不同的框帧来显示出不同的图案。


SpriteFrameCache

SpriteFrameCache的内部封装了一个Map_spriteFrames对象。key为帧的名称。SpriteFrameCache一般用来处理plist文件(这个文件指定了每个独立的精灵在这张“大图”里面的位置和大小),该文件对应一张包含多个精灵的大图,plist文件可以使用TexturePacker制作。

SpriteFrameCache的常用接口和TextureCache类似,不再赘述了,唯一需要注意的是添加精灵帧的配套文件一个plist文件和一张大的纹理图。下面列举了SpriteFrameCache常用的方法:


获取单例的SpriteFrameCache对象。sharedSpriteFrameCache方法在3.0中已经弃用:

SpriteFrameCache* cache = SpriteFrameCache::getInstance();

销毁SpriteFrameCache对象:

SpriteFrameCache::destroyInstance();

使用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上的封装缓存的是精灵帧,是纹理指定区域的矩形块。各精灵帧都在同一纹理中,通过切换不同的框帧来显示出不同的图案


纹理缓存

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

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

Texture2D: 纹理,即图片加载入内存后供cpu和GPU操作的贴图对象。
TextureCache(纹理缓存),用于加载和管理纹理。一旦纹理加载完成,下次使用时可使用它返回之前加载的纹理,从而减少对GPU和cpu内存的占用。


常用的方法

当你创建一个精灵,你一般会使用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;
}

上面代码显示在控制加载纹理。一旦这个纹理被加载了,在下一时刻就会返回之前加载的纹理引用,并且减少加载的时候瞬间增加的内存。


获取TextureCache

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

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

获取纹理

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

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

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

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

异步加载纹理

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",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",this));
    Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_01.png",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();
    }
}

清理缓存

removeUnusedTextures则会释放当前所有引用计数为1的纹理,即目前没有被使用的纹理。比如新场景创建好后,使用此方法释放没有使用的纹理非常方便:

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

当没有其它对象(比如sprite)持有纹理的引用的时候,纹理仍然会存在内存之间。基于这一点,我们可以立马从缓存中移除出去,这样,当纹理不存需要的时候,马上就会从内存中释放掉。如下代码所示:

Director::getInstance()->getTextureCache()->removeTextureForKey("Images/grossinis_sister2.png");

当收到"Memory Warning"时,可以调用removeAllTextures()方法。在短期内: 它还将释放一些资源,防止您的应用程序被杀害; 中期: 它将分配更多的资源;从长远来看:它会是相同的:

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

cocos2d 3.x 纹理缓存

cocos2d 3.x 纹理缓存

官方的开发文档就是坑

http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v2/memory/texture-cache/zh.md

就不能提供个版本号?

3.x中纹理缓存的实列获取改为了

Director::getInstance()->getTextureCache()

Director头文件中把TextureCache当成了一个保护成员变量

//texture cache belongs to this director
TextureCache *_textureCache;

Director关于TextureCache的函数有四个

<span>TextureCache* Director::getTextureCache() const
{
    return _textureCache;//返回TextureCache实列对象
}

void Director::initTextureCache()//这个是初始化TextureCache,在Director::init()里调用
{
#ifdef EMSCRIPTEN
    _textureCache = new (std::nothrow) TextureCacheEmscripten();
#else
    _textureCache = new (std::nothrow) TextureCache();
#endif // EMSCRIPTEN
}

void Director::destroyTextureCache()//这个应该是销毁函数,在purgeDirector()里调用,我感觉怎么是向线程的延时,没看太懂
{                                   //purgeDirector()是在displayLinkDirector里调用,这个类继承于Director
    if (_textureCache)
    {
        _textureCache->waitForQuit();
        CC_SAFE_RELEASE_NULL(_textureCache);
    }
}

void Director::purgeCachedData(void)//这个是清空无用的缓存
{
 FontFNT::purgeCachedData();//1
 FontAtlasCache::purgeCachedData();//2

 if (s_SharedDirector->getopenGLView())
 {
 SpriteFrameCache::getInstance()->removeUnusedSpriteFrames();//3
 _textureCache->removeUnusedTextures();//4

 // Note: some tests such as ActionsTest are leaking refcounted textures
 // There should be no test textures left in the cache
 log("%s\n",_textureCache->getCachedTextureInfo().c_str());
 }
 FileUtils::getInstance()->purgeCachedEntries();//5
}</span>
真正用到的应该是Director::getInstance()->getTextureCache()


关于TextureCache类的方法

<span>void TextureCache::addImageAsync(const std::string &path,const std::function<void(Texture2D*)>& callback)//异步加载方式,第一个是文件路径,第二个是回调函数
{
    Texture2D *texture = nullptr;

    std::string fullpath = FileUtils::getInstance()->fullPathForFilename(path);

    auto it = _textures.find(fullpath);
    if( it != _textures.end() )
        texture = it->second;

    if (texture != nullptr)
    {
        callback(texture);
        return;
    }

    // lazy init
    if (_asyncStructQueue == nullptr)
    {             
        _asyncStructQueue = new queue<AsyncStruct*>();
        _imageInfoQueue   = new deque<ImageInfo*>();        

        // create a new thread to load images
        _loadingThread = new std::thread(&TextureCache::loadImage,this);

        _needQuit = false;
    }

    if (0 == _asyncRefCount)
    {
        Director::getInstance()->getScheduler()->schedule(CC_SCHEDULE_SELECTOR(TextureCache::addImageAsyncCallBack),this,false);
    }

    ++_asyncRefCount;

    // generate async struct
    AsyncStruct *data = new (std::nothrow) AsyncStruct(fullpath,callback);

    // add async struct into queue
    _asyncStructQueueMutex.lock();
    _asyncStructQueue->push(data);
    _asyncStructQueueMutex.unlock();

    _sleepCondition.notify_one();
}
关于addImage
</span>
a:Texture2D * TextureCache::addImage(const std::string &path)

b:Texture2D* TextureCache::addImage(Image *image,const std::string &key)

对a来说默认的key是文件名称
对b来说可以自定义key
a和b这两种方式都返回加载好的纹理,所以要进行用Texture2D类型的变量进行赋值。
回收可以用void TextureCache::removeTexture(Texture2D* texture)或
void TextureCache::removeTextureForKey(const std::string &textureKeyName)来进行
获取可以用Texture2D* TextureCache::getTextureForKey(const std::string &textureKeyName) const



SpriteFrame的用法基本没变。

技术比较水,努力中

cocos2d-iphone – 需要帮助理解精灵和纹理

cocos2d-iphone – 需要帮助理解精灵和纹理

我最近开始关注cocos2d游戏开发.

精灵和纹理有什么区别?
也许我可以通过那里的“位图”.什么是位图?

它们似乎都与2D图像相同.

解决方法

纹理是设备可以在屏幕上绘制的内存中图像.

精灵实际上在屏幕上绘制纹理,或者只是纹理的特定矩形.精灵可以缩放,旋转,定位,倾斜,着色(着色)等.

多个精灵可以共享相同的纹理.无论有多少精灵使用相同的纹理,纹理都只会加载到内存中.此外,使用CCSpriteBatchNode,您可以“批量”使用相同纹理的所有精灵的绘图,以获得更好的性能.

位图是计算机图像的通用术语,其中每个像素由一个或多个位表示.还有在Windows上流行的图像格式BMP.这些天大多数人只会说“图像”,因为还有其他形式的“位图”不是图像.例如,在AI代码中,您经常使用位图(位数组)来表示AI的状态信息或用于游戏世界所有区域的寻路算法.即世界上的每个区域都可能有一个“阻塞”位或一个“资源”位,可以帮助AI做出决策.

另见维基百科:

> Texture Mapping
> Bitmap

Cocos2d-x -- 纹理缓存(Texture Cache)

Cocos2d-x -- 纹理缓存(Texture Cache)

简介

纹理缓存是将纹理缓存起来方便之后的绘制工作。每一个缓存的图像的大小,颜色和区域范围都是可以被修改的。这些信息都是存储在内存中的,不用在每一次绘制的时候都发送给GPU。

CCTextureCache

Cocos2d通过调用CCTextureCache或者CCSpriteFrameCache来缓存精灵的纹理。

当这个精灵调用CCTextureCache 或 CCSpriteFrameCache的方法的时候,cocos2dx将使用纹理缓存来创建一个CCSprite。所以你可以预先将纹理加载到缓存中,这样你在场景中使用的时候就非常方便了。怎么样加载这些纹理就看你自己的想法。例如,你可以选择异步加载方式,这样你就可以为loading场景增加一个进度条。

当你创建一个精灵,你一般会使用CCSprite::create(pszFileName)。假如你去看CCSprite::create(pszFileName)的实现方式,你将看到它将这个图片增加到纹理缓存中去了:

bool CCSprite::initWithFile(const char *pszFilename)
{
    CCAssert(pszFilename != NULL,"Invalid filename for sprite");
    CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(pszFilename);
    if (pTexture)
    {
        CCRect rect = CCRectZero;
        rect.size = pTexture->getContentSize();
        return initWithTexture(pTexture,rect);
    }
    // don't release here.
    // when load texture Failed,it's better to get a "transparent" sprite than a crashed program
    // this->release(); 
    returnfalse;
}

上面代码显示一个单例在控制加载纹理。一旦这个纹理被加载了,在下一时刻就会返回之前加载的纹理引用,并且减少加载的时候瞬间增加的内存。(详细API请看CCTextureCache API)

CCSpriteFrameCache

CCSpriteFrameCache单例是所有精灵帧的缓存。使用spritesheet和与之相关的xml文件,我们可以加载很多的精灵帧到缓存中,那么之后我们就可以从这个缓存中创建精灵对象了。

和这个xml相关的纹理集一般是一个很大的图片,里面包含了很多小的纹理。下面就是一个纹理集的例子:

有三种方式来加载纹理集到CCSpriteFrameCache中:

  • 加载一个xml(plist)文件
  • 加载一个xml(plist)文件和一个纹理集
  • 通过CCSpriteFrame和一个精灵帧的名字

具体完整API请看CCSpriteFrameCache API。

样例:

CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache(); 
cache->addSpriteFramesWithFile(“family.plist”,“family.png”);

使用缓存的原因就是减少内存,因为当你使用一个图片创建一个精灵的时候,如果这个图片不在缓存中,那么就会将他加载到缓存中,当你需要用相同的图片来新建精灵的时候,就可以直接从缓存中取得,而不用再去新分配一份内存空间。

CCSpriteFrameCache vs. CCSpriteBatchNode

  • 最好是尽可能的使用spritesheets (CCSpriteBatchNodes)。这样的方式是减少draw的调用次数。Draw的调用是非常耗时的。每一个batchnode调用一次draw就可以绘制上面所有的节点,而不是每一个节点的draw都单独调用一次,
  • CCSpriteBatchNode渲染所有的子节点只需要一次,只需要调用一次draw。那就是为什么你需要把精灵加载batch node的原因,因为可以统一一起渲染。但是只有这个精灵使用的纹理包含在batch node中的才可以添加到batch node上,因为batch node一次只渲染这相同的纹理集。
  • 假如你把精灵添加到其他的节点上。那么每一个精灵就会调用自己的draw函数,batch node就没起作用了。
  • CCSpriteBatchNode也是一个常用节点。你可以从场景中像其他节点一样移除掉。纹理集和精灵帧都被缓存在CCTextureCache 和 CCSpriteFrameCache单例中。假如你想要从内存中移除纹理集和精灵帧,那么你不得不通过缓存类来完成这个工作。

cocos2d-x 3.0 精灵帧缓存(SpriteFrameCache)

cocos2d-x 3.0 精灵帧缓存(SpriteFrameCache)

SpriteFrameCache 主要服务于多张碎图合并出来的纹理图片。这种纹理在一张大图中包含了多张小图,直接通过TextureCache引用会有诸多不便,因而衍生出来精灵框帧的处理方式,即把截取好的纹理信息保存在一个精灵框帧内,精灵通过切换不同的框帧来显示出不同的图案。

SpriteFrameCache
SpriteFrameCache的内部封装了一个Map<std::string,SpriteFrame*> _spriteFrames对象。key为帧的名称。SpriteFrameCache一般用来处理plist文件(这个文件指定了每个独立的精灵在这张“大图”里面的位置和大小),该文件对应一张包含多个精灵的大图,plist文件可以使用TexturePacker制作。
SpriteFrameCache的常用接口和TextureCache类似,不再赘述了,唯一需要注意的是添加精灵帧的配套文件一个plist文件和一张大的纹理图。下面列举了SpriteFrameCache常用的方法:(详细API请看SpriteFrameCache API)
获取单例的SpriteFrameCache对象。sharedSpriteFrameCache方法在3.0中已经弃用
复制代码
  1. SpriteFrameCache* cache = SpriteFrameCache::getInstance();

销毁SpriteFrameCache对象。
复制代码 SpriteFrameCache::destroyInstance();

使用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上的封装。缓存的是精灵帧,是纹理指定区域的矩形块。各精灵帧都在同一纹理中,通过切换不同的框帧来显示出不同的图案。

    关于【Cocos2d-x 3.x】 精灵帧缓存和纹理缓存的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于cocos2d 3.x 纹理缓存、cocos2d-iphone – 需要帮助理解精灵和纹理、Cocos2d-x -- 纹理缓存(Texture Cache)、cocos2d-x 3.0 精灵帧缓存(SpriteFrameCache)的相关知识,请在本站寻找。

    本文标签:

    上一篇cocos2d-x c++使用误区记录(cocos能用c++吗)

    下一篇用cocos2d-x模拟单摆运动的程序(单摆运动代码)