COCOS2DX引擎深入四————图片的异步加载
2015-01-09 18:42
253 查看
游戏刚启动时,最常见的画面就是进度条了。而进度条展示的都是资源的加载进度。包括图片资源的。
cocos2dx引擎的整体流程都是基于单线程的,但是加载图片资源是个很耗时的操作,如果放在主线程中执行,很容易让画面卡顿。
所以cocos2dx提供了异步加载图片资源的方法。先看看test里怎么用的,再看看具体如何实现。
首先就是加载你的资源
然后通过注册回调函数,将进度展现到你的游戏当中
整个过程是异步的,不会让游戏产生卡顿的情况。
再看看具体是怎么实现的。
首先先在texture缓存里查找,如果找到了,说明之前加载进来了,直接调用回调。没找到则另开一个线程加载图片,并将两个图片路径和回调加入队列_asyncStructQueue中
这个线程在取出_imageInfoQueue队列里每个图片的路径信息,从而构建图片资源信息。因为是在另一个线程里进行的,所以不会卡顿。接下来就剩把图片信息传给opengl了。而这个过程耗时就在这,把大量的图片信息一次性传给opengl,这是相当耗时的操作,这会是画面出现卡顿。所以cocos2dx采用每一帧让opengl处理一张图片的方法,
这个回调在addImageAsync第一次被调用时就被注册了,定时器的fps跟主线程的一样。 texture->initWithImage(image);执行的就是glTexImage2D这个耗时的操作。
cocos2dx引擎的整体流程都是基于单线程的,但是加载图片资源是个很耗时的操作,如果放在主线程中执行,很容易让画面卡顿。
所以cocos2dx提供了异步加载图片资源的方法。先看看test里怎么用的,再看看具体如何实现。
首先就是加载你的资源
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(Vec2(size.width / 2, size.height / 2 - 20)); _labelPercent->setPosition(Vec2(size.width / 2, size.height / 2 + 20)); this->addChild(_labelLoading); this->addChild(_labelPercent); // load textrues Director::getInstance()->getTextureCache()->addImageAsync("Images/HelloWorld.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_01.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_02.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_03.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_04.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_05.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_06.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_07.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_08.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_09.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_10.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_11.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_12.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_13.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_14.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/background1.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/background2.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/background3.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("Images/blocks.png", CC_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(); } }
整个过程是异步的,不会让游戏产生卡顿的情况。
再看看具体是怎么实现的。
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(schedule_selector(TextureCache::addImageAsyncCallBack), this, 0, false); } ++_asyncRefCount; // generate async struct AsyncStruct *data = new AsyncStruct(fullpath, callback); // add async struct into queue _asyncStructQueueMutex.lock(); _asyncStructQueue->push(data); _asyncStructQueueMutex.unlock(); _sleepCondition.notify_one(); }
首先先在texture缓存里查找,如果找到了,说明之前加载进来了,直接调用回调。没找到则另开一个线程加载图片,并将两个图片路径和回调加入队列_asyncStructQueue中
void TextureCache::loadImage() { AsyncStruct *asyncStruct = nullptr; while (true) { std::queue<AsyncStruct*> *pQueue = _asyncStructQueue; _asyncStructQueueMutex.lock(); if (pQueue->empty()) { _asyncStructQueueMutex.unlock(); if (_needQuit) { break; } else { std::unique_lock<std::mutex> lk(_sleepMutex); _sleepCondition.wait(lk); continue; } } else { asyncStruct = pQueue->front(); pQueue->pop(); _asyncStructQueueMutex.unlock(); } Image *image = nullptr; bool generateImage = false; auto it = _textures.find(asyncStruct->filename); if( it == _textures.end() ) { _imageInfoMutex.lock(); ImageInfo *imageInfo; size_t pos = 0; size_t infoSize = _imageInfoQueue->size(); for (; pos < infoSize; pos++) { imageInfo = (*_imageInfoQueue)[pos]; if(imageInfo->asyncStruct->filename.compare(asyncStruct->filename) == 0) break; } _imageInfoMutex.unlock(); if(infoSize == 0 || pos == infoSize) generateImage = true; } if (generateImage) { const std::string& filename = asyncStruct->filename; // generate image image = new Image(); if (image && !image->initWithImageFileThreadSafe(filename)) { CC_SAFE_RELEASE(image); CCLOG("can not load %s", filename.c_str()); continue; } } // generate image info ImageInfo *imageInfo = new ImageInfo(); imageInfo->asyncStruct = asyncStruct; imageInfo->image = image; // put the image info into the queue _imageInfoMutex.lock(); _imageInfoQueue->push_back(imageInfo); _imageInfoMutex.unlock(); } if(_asyncStructQueue != nullptr) { delete _asyncStructQueue; _asyncStructQueue = nullptr; delete _imageInfoQueue; _imageInfoQueue = nullptr; } }
这个线程在取出_imageInfoQueue队列里每个图片的路径信息,从而构建图片资源信息。因为是在另一个线程里进行的,所以不会卡顿。接下来就剩把图片信息传给opengl了。而这个过程耗时就在这,把大量的图片信息一次性传给opengl,这是相当耗时的操作,这会是画面出现卡顿。所以cocos2dx采用每一帧让opengl处理一张图片的方法,
void TextureCache::addImageAsyncCallBack(float dt) { // the image is generated in loading thread std::deque<ImageInfo*> *imagesQueue = _imageInfoQueue; _imageInfoMutex.lock(); if (imagesQueue->empty()) { _imageInfoMutex.unlock(); } else { ImageInfo *imageInfo = imagesQueue->front(); imagesQueue->pop_front(); _imageInfoMutex.unlock(); AsyncStruct *asyncStruct = imageInfo->asyncStruct; Image *image = imageInfo->image; const std::string& filename = asyncStruct->filename; Texture2D *texture = nullptr; if (image) { // generate texture in render thread texture = new Texture2D(); texture->initWithImage(image); #if CC_ENABLE_CACHE_TEXTURE_DATA // cache the texture file name VolatileTextureMgr::addImageTexture(texture, filename); #endif // cache the texture. retain it, since it is added in the map _textures.insert( std::make_pair(filename, texture) ); texture->retain(); texture->autorelease(); } else { auto it = _textures.find(asyncStruct->filename); if(it != _textures.end()) texture = it->second; } if (asyncStruct->callback) { asyncStruct->callback(texture); } if(image) { image->release(); } delete asyncStruct; delete imageInfo; --_asyncRefCount; if (0 == _asyncRefCount) { Director::getInstance()->getScheduler()->unschedule(schedule_selector(TextureCache::addImageAsyncCallBack), this); } } }
这个回调在addImageAsync第一次被调用时就被注册了,定时器的fps跟主线程的一样。 texture->initWithImage(image);执行的就是glTexImage2D这个耗时的操作。
相关文章推荐
- 扩展于RCLabel的支持异步加载网络图片的富文本引擎的设计
- Android 异步加载图片+线程池+缓存(详细介绍,深入了解)
- 【Cocos2dx3.x Lua】图片异步加载
- 深入剖析Android之ListView从网络异步加载图片
- 实例讲解Android中如何实现图片的异步加载功能
- 异步加载图片
- Android实现ListView异步加载图片
- 异步加载图片
- Winform DataGridView中利用WebClient异步加载显示网络地址的图片
- 实例讲解Android中如何实现图片的异步加载功能
- Android实现ListView异步加载图片
- Android ListView异步加载图片
- js 实现图片预加载(js操作 Image对象属性complete ,事件onload 异步加载图片)
- C#中PictureBox异步加载图片
- Android实现ListView异步加载图片
- 转载:Android实现ListView异步加载图片
- silverlight中顺序/倒序异步加载多张图片
- android中listView实现异步加载网络图片
- silverlight中顺序/倒序异步加载多张图片
- js 实现图片预加载 (js操作 Image对象属性complete ,事件onload 异步加载图片)