1 异步加载资源

  异步加载的执行过程主要在addImageAsync函数中: 判定当前路径对应的纹理是否已经加载,如果未加载,为该纹理创建一个AsyncStruct 数据结构存放该纹理数据,并加入至_requestMutex请求队列中。在创建的用于专门解析纹理的新线程LoadImage中,请求队列中的数据将被逐个解析至AsyncStruct .image中并加入已解析队列_responseQueue中。 在创建的计时器回调函数addImageAsyncCallBack中,将解析得到的image转换为最终的texture。
  上述过程涉及两个线程:主线程GL Thread与子线程Load Thread,过程中两个重要的数据为AsyncStruct 与Image Data。其中AsyncStruct的创建与析构都在主线程中完成,Image Data在Load Thread中创建,在GL Thread中析构。
  

void TextureCache::addImageAsync(const std::string &path,const std::function<void(Texture2D*)>& callback,const std::string& callbackKey)
{
    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)
    {
        if (callback) callback(texture);
        return;
    }

    // 检查当前路径对应的文件是否存在
    if (fullpath.empty() || !FileUtils::getInstance()->isFileExist(fullpath)) {
        if (callback) callback(nullptr);
        return;
    }

    // lazy init
    if (_loadingThread == nullptr)
    {
        // 创建一个新的线程 用于加载纹理
        _loadingThread = new (std::nothrow) std::thread(&TextureCache::loadImage,this);
        _needQuit = false;//线程是否终止的标志
    }

    if (0 == _asyncRefCount)
    {
        // 创建定时器回调 用于将解析的image转为texture并执行回调函数
        Director::getInstance()->getScheduler()->schedule(CC_SCHEDULE_SELECTOR(TextureCache::addImageAsyncCallBack),this,0,false);
    }
    ++_asyncRefCount;

    // 生成async struct,并加入至请求队列
    AsyncStruct *data =
      new (std::nothrow) AsyncStruct(fullpath,callback,callbackKey);

    // add async struct into queue
    _asyncStructQueue.push_back(data); // 该队列在unbind函数中使用 在加载纹理过程中不使用
    _requestMutex.lock();
    _requestQueue.push_back(data);
    _requestMutex.unlock();

    _sleepCondition.notify_one();
}

// 解析资源的线程
void TextureCache::loadImage()
{
    AsyncStruct *asyncStruct = nullptr;
    std::mutex signalMutex;
    std::unique_lock<std::mutex> signal(signalMutex);
    while (!_needQuit)
    {
        // 从请求队列中取出待解析资源
        _requestMutex.lock();
        if (_requestQueue.empty())
        {
            asyncStruct = nullptr;
        }
        else
        {
            asyncStruct = _requestQueue.front();
            _requestQueue.pop_front();
        }
        _requestMutex.unlock();

        if (nullptr == asyncStruct) {
            _sleepCondition.wait(signal);
            continue;
        }

        // load image
        asyncStruct->loadSuccess = asyncStruct->image.initWithImageFileThreadSafe(asyncStruct->filename);

        // push the asyncStruct to response queue
        _responseMutex.lock();
        _responseQueue.push_back(asyncStruct);
        _responseMutex.unlock();
    }
}

void TextureCache::addImageAsyncCallBack(float /*dt*/)
{
    Texture2D *texture = nullptr;
    AsyncStruct *asyncStruct = nullptr;
    while (true) // 轮询解析队列中的image 
    {
        // pop an AsyncStruct from response queue
        _responseMutex.lock();
        if (_responseQueue.empty())
        {
            asyncStruct = nullptr;
        }
        else
        {
            asyncStruct = _responseQueue.front();
            _responseQueue.pop_front();

            // 保持_asyncStructQueue 与 _responseQueue的同步
            CC_ASSERT(asyncStruct == _asyncStructQueue.front());
            _asyncStructQueue.pop_front();
        }
        _responseMutex.unlock();

        if (nullptr == asyncStruct) {
            break;
        }

        // check the image has been convert to texture or not
        auto it = _textures.find(asyncStruct->filename);
        if (it != _textures.end()) // 检查当前纹理资源是否已被加载
        {
            texture = it->second;
        }
        else
        {
            // convert image to texture
            if (asyncStruct->loadSuccess)
            {
                Image* image = &(asyncStruct->image);
                // generate texture in render thread
                texture = new (std::nothrow) Texture2D();

                texture->initWithImage(image,asyncStruct->pixelFormat);
                //parse 9-patch info
                this->parseNinePatchImage(image,texture,asyncStruct->filename);
#if CC_ENABLE_CACHE_TEXTURE_DATA
                // cache the texture file name
                VolatileTextureMgr::addImageTexture(texture,asyncStruct->filename);
#endif
                // cache the texture. retain it,since it is added in the map
                _textures.emplace(asyncStruct->filename,texture);
                texture->retain(); // 为何要retain: 下一帧不自动释放该纹理,纹理的释放由程序手动控制
                texture->autorelease();
            }
            else {
                texture = nullptr;
                cclOG("cocos2d: Failed to call TextureCache::addImageAsync(%s)",asyncStruct->filename.c_str());
            }
        }

        // 执行回调函数
        if (asyncStruct->callback)
        {
            (asyncStruct->callback)(texture);
        }

        // release the asyncStruct
        delete asyncStruct;
        --_asyncRefCount;
    }

    if (0 == _asyncRefCount) // 所有异步加载过程均已完成 销毁计时器回调
    {
        Director::getInstance()->getScheduler()->unschedule(CC_SCHEDULE_SELECTOR(TextureCache::addImageAsyncCallBack),this);
    }
}

2 纹理中关键帧的检索

检索之前,首先需要将一整张纹理按照区域切割成各个关键帧,并建立帧名与帧之间的map映射表。addSpriteFramesWithFile完成了这一过程,该函数为已加载完成的纹理数据与对应的plist文件构建映射关系。

void SpriteFrameCache::addSpriteFramesWithFile(const std::string& plist,Texture2D *texture)
{
    if (_loadedFileNames->find(plist) != _loadedFileNames->end())
    {
        return; // We already added it
    }

    std::string fullPath = FileUtils::getInstance()->fullPathForFilename(plist);// plist全路径
    ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(fullPath); // plist是XML文件,cocos将其解析为key-value的map结构

    addSpriteFramesWithDictionary(dict,texture);
    _loadedFileNames->insert(plist);
}

// 从texture中取出相应区域spriteframe
void SpriteFrameCache::addSpriteFramesWithDictionary(ValueMap& dictionary,Texture2D* texture)
{
    /* Supported Zwoptex Formats: ZWTCoordinatesFormatOptionXMLLegacy = 0,// Flash Version ZWTCoordinatesFormatOptionXML1_0 = 1,// Desktop Version 0.0 - 0.4b ZWTCoordinatesFormatOptionXML1_1 = 2,// Desktop Version 1.0.0 - 1.0.1 ZWTCoordinatesFormatOptionXML1_2 = 3,// Desktop Version 1.0.2+ */

    if (dictionary["frames"].getType() != cocos2d::Value::Type::MAP)
        return;

    ValueMap& framesDict = dictionary["frames"].asValueMap(); // 存放每一帧图片路径、大小、偏移、对应的纹理区域位置等属性
    int format = 0;

    Size textureSize;

    // get the format
    if (dictionary.find("Metadata") != dictionary.end())
    {
        ValueMap& MetadataDict = dictionary["Metadata"].asValueMap(); // Metadata中存放纹理格式、纹理大小、纹理名称等纹理属性
        format = MetadataDict["format"].asInt();
        if(MetadataDict.find("size") != MetadataDict.end())
        {
            textureSize = SizefromString(MetadataDict["size"].asstring());
        }
    }

    // check the format
    CCASSERT(format >=0 && format <= 3,"format is not supported for SpriteFrameCache addSpriteFramesWithDictionary:textureFilename:");

    auto textureFileName = Director::getInstance()->getTextureCache()->getTextureFilePath(texture);
    Image* image = nullptr;
    NinePatchImageParser parser;
    for (auto& iter : framesDict)
    {
        ValueMap& frameDict = iter.second.asValueMap();
        std::string spriteFrameName = iter.first;
        SpriteFrame* spriteFrame = _spriteFrames.at(spriteFrameName);
        if (spriteFrame)
        {
            continue; // 当前帧已加入至关键帧集合 无需处理
        }

        if(format == 0) 
        {
            float x = frameDict["x"].asFloat();
            float y = frameDict["y"].asFloat();
            float w = frameDict["width"].asFloat();
            float h = frameDict["height"].asFloat();
            float ox = frameDict["offsetX"].asFloat();
            float oy = frameDict["offsetY"].asFloat();
            int ow = frameDict["originalWidth"].asInt();
            int oh = frameDict["originalHeight"].asInt();
            // check ow/oh
            if(!ow || !oh)
            {
                cclOGWARN("cocos2d: WARNING: originalWidth/Height not found on the SpriteFrame. AnchorPoint won't work as expected. Regenerate the .plist");
            }
            // abs ow/oh
            ow = std::abs(ow);
            oh = std::abs(oh);
            // create frame
            spriteFrame = SpriteFrame::createWithTexture(texture,Rect(x,y,w,h),false,Vec2(ox,oy),Size((float)ow,(float)oh)
                                                         );
        } 
        else if(format == 1 || format == 2) 
        {
            Rect frame = RectFromString(frameDict["frame"].asstring());
            bool rotated = false;

            // rotation
            if (format == 2)
            {
                rotated = frameDict["rotated"].asBool();
            }

            Vec2 offset = PointFromString(frameDict["offset"].asstring());
            Size sourceSize = SizefromString(frameDict["sourceSize"].asstring());

            // create frame
            spriteFrame = SpriteFrame::createWithTexture(texture,frame,rotated,offset,sourceSize
                                                         );
        } 
        else if (format == 3)
        {
            // get values
            Size spriteSize = SizefromString(frameDict["spriteSize"].asstring());
            Vec2 spriteOffset = PointFromString(frameDict["spriteOffset"].asstring());
            Size spriteSourceSize = SizefromString(frameDict["spriteSourceSize"].asstring());
            Rect textureRect = RectFromString(frameDict["textureRect"].asstring());
            bool textureRotated = frameDict["textureRotated"].asBool();

            // get aliases
            ValueVector& aliases = frameDict["aliases"].asValueVector();

            for(const auto &value : aliases) {
                std::string oneAlias = value.asstring();
                if (_spriteFramesAliases.find(oneAlias) != _spriteFramesAliases.end())
                {
                    cclOGWARN("cocos2d: WARNING: an alias with name %s already exists",oneAlias.c_str());
                }

                _spriteFramesAliases[oneAlias] = Value(spriteFrameName);
            }

            // create frame
            spriteFrame = SpriteFrame::createWithTexture(texture,Rect(textureRect.origin.x,textureRect.origin.y,spriteSize.width,spriteSize.height),textureRotated,spriteOffset,spriteSourceSize);

            if(frameDict.find("vertices") != frameDict.end())
            {
                std::vector<int> vertices;
                parseIntegerList(frameDict["vertices"].asstring(),vertices);
                std::vector<int> verticesUV;
                parseIntegerList(frameDict["verticesUV"].asstring(),verticesUV);
                std::vector<int> indices;
                parseIntegerList(frameDict["triangles"].asstring(),indices);

                polygonInfo info;
                initializepolygonInfo(textureSize,spriteSourceSize,vertices,verticesUV,indices,info);
                spriteFrame->setpolygonInfo(info);
            }
            if (frameDict.find("anchor") != frameDict.end())
            {
                spriteFrame->setAnchorPoint(PointFromString(frameDict["anchor"].asstring()));
            }
        }

        bool flag = NinePatchImageParser::isNinePatchImage(spriteFrameName);
        if(flag)
        {
            if (image == nullptr) {
                image = new (std::nothrow) Image();
                image->initWithImageFile(textureFileName);
            }
            parser.setSpriteFrameInfo(image,spriteFrame->getRectInPixels(),spriteFrame->isRotated());
            texture->addSpriteFrameCapInset(spriteFrame,parser.parseCapInset());
        }
        // add sprite frame
        _spriteFrames.insert(spriteFrameName,spriteFrame);
    }
    CC_SAFE_DELETE(image);
}

// 基于frameName检索得到SpriteFrame
SpriteFrame* SpriteFrameCache::getSpriteFrameByName(const std::string& name)
{
    SpriteFrame* frame = _spriteFrames.at(name);
    if (!frame)
    {
        // try alias dictionary
        if (_spriteFramesAliases.find(name) != _spriteFramesAliases.end())
        {
            std::string key = _spriteFramesAliases[name].asstring();
            if (!key.empty())
            {
                frame = _spriteFrames.at(key);
                if (!frame)
                {
                    cclOG("cocos2d: SpriteFrameCache: Frame aliases '%s' isn't found",key.c_str());
                }
            }
        }
        else
        {
            cclOG("cocos2d: SpriteFrameCache: Frame '%s' isn't found",name.c_str());
        }
    }
    return frame;
}

3 lua层的接口调用

local createAni = function ( texture )
    -- 回调函数时将传入已加载的纹理
    local testSp = cc.Sprite:createWithTexture(texture)
    self:addChild(testSp)

    local ani = cc.Animation:create()
    ani:setDelayPerUnit(0.12)
    ani:setRestoreOriginalFrame(true) -- 动画停止时显示为起始图片
    for i=1,num do
        local frameName = getFrameName(i)
        local frame = cc.SpriteFrameCache:getInstance():getSpriteFrameByName(frameName)
        if frame then
            ani:addSpriteFrame(frame)
        end
        -- 关键帧全部加载完毕 播放动画
        if i == num then
            self:stopAllActions()
            local action = cc.Animation:create(ani)
            self:runAction(action)
        end
    end
end
local textureName = getTextureName() or "" -- 禁止向cocos接口中传入nil 避免宕机
cc.Director:getInstance():getTextureCache():addImageAsync(textureName,createAni)

Cocos2dx 异步加载纹理的更多相关文章

  1. CocosCreator如何实现划过的位置显示纹理

    这篇文章主要介绍了CocosCreator纹理shader的一些知识,想了解shader的同学,一定要看下,并且亲自动手实践

  2. WebGL2系列之不可变纹理

    总结以上是DEVMAX为你收集整理的WebGL2系列之不可变纹理全部内容。如果觉得DEVMAX网站内容还不错,欢迎将DEVMAX网站推荐给好友。

  3. Cocos2dx 异步加载纹理

    1异步加载资源异步加载的执行过程主要在addImageAsync函数中:判定当前路径对应的纹理是否已经加载,如果未加载,为该纹理创建一个AsyncStruct数据结构存放该纹理数据,并加入至_requestMutex请求队列中。在创建的用于专门解析纹理的新线程LoadImage中,请求队列中的数据将被逐个解析至AsyncStruct.image中并加入已解析队列_responseQueue中。在创建的计时器回调函数addImageAsyncCallBack中,将解析得到的image转换为最终的textur

  4. cocos2d-x 3D shader的纹理坐标是上下颠倒的

    前几天写了个3Dshader,把我坑了个惨。不知道是为什么,cocos2d-x将3Dshader中的纹理坐标作了上下颠倒,而2D坐标却是正常的。对此我很是不解。

随机推荐

  1. 【cocos2d-x 3.x 学习笔记】对象内存管理

    Cocos2d-x的内存管理cocos2d-x中使用的是上面的引用计数来管理内存,但是又增加了一些自己的特色。cocos2d-x中通过Ref类来实现引用计数,所有需要实现内存自动回收的类都应该继承自Ref类。下面是Ref类的定义:在cocos2d-x中创建对象通常有两种方式:这两中方式的差异可以参见我另一篇博文“对象创建方式讨论”。在cocos2d-x中提倡使用第二种方式,为了避免误用第一种方式,一般将构造函数设为protected或private。参考资料:[1]cocos2d-x高级开发教程2.3节[

  2. 利用cocos2dx 3.2开发消灭星星六如何在cocos2dx中显示中文

    由于编码的不同,在cocos2dx中的Label控件中如果放入中文字,往往会出现乱码。为了方便使用,我把这个从文档中获取中文字的方法放在一个头文件里面Chinese.h这里的tex_vec是cocos2dx提供的一个保存文档内容的一个容器。这里给出ChineseWords,xml的格式再看看ChineseWord的实现Chinese.cpp就这样,以后在需要用到中文字的地方,就先include这个头文件然后调用ChineseWord函数,获取一串中文字符串。

  3. 利用cocos2dx 3.2开发消灭星星七关于星星的算法

    在前面,我们已经在GameLayer中利用随机数初始化了一个StarMatrix,如果还不知道怎么创建星星矩阵请回去看看而且我们也讲了整个游戏的触摸事件的派发了。

  4. cocos2dx3.x 新手打包APK注意事项!

    这个在编译的时候就可以发现了比较好弄这只是我遇到的,其他的以后遇到再补充吧。。。以前被这两个问题坑了好久

  5. 利用cocos2dx 3.2开发消灭星星八游戏的结束判断与数据控制

    如果你看完之前的,那么你基本已经拥有一个消灭星星游戏的雏形。开始把剩下的两两互不相连的星星消去。那么如何判断是GameOver还是进入下一关呢。。其实游戏数据贯穿整个游戏,包括星星消除的时候要加到获得分数上,消去剩下两两不相连的星星的时候的加分政策等,因此如果前面没有做这一块的,最好回去搞一搞。

  6. 利用cocos2dx 3.2开发消灭星星九为游戏添加一些特效

    needClear是一个flag,当游戏判断不能再继续后,这个flag变为true,开始消除剩下的星星clearSumTime是一个累加器ONE_CLEAR_TIME就是每颗星星消除的时间2.连击加分信息一般消除一次星星都会有连击信息和加多少分的信息。其实这些combo标签就是一张图片,也是通过控制其属性或者runAction来实现。源码ComboEffect.hComboEffect.cpp4.消除星星粒子效果消除星星时,为了实现星星爆裂散落的效果,使用了cocos2d提供的粒子特效引擎对于粒子特效不了

  7. 02 Cocos2D-x引擎win7环境搭建及创建项目

    官网有搭建的文章,直接转载记录。环境搭建:本文介绍如何搭建Cocos2d-x3.2版本的开发环境。项目创建:一、通过命令创建项目前面搭建好环境后,怎样创建自己的Cocos2d-x项目呢?先来看看Cocos2d-x3.2的目录吧这就是Cocos2d-x3.2的目录。输入cocosnew项目名–p包名–lcpp–d路径回车就创建成功了例如:成功后,找到这个项目打开proj.win32目录下的Hello.slnF5成功了。

  8. 利用cocos2dx 3.2开发消灭星星十为游戏添加音效项目源码分享

    一个游戏,声音也是非常的重要,其实cocos2dx里面的简单音效引擎的使用是非常简单的。我这里只不过是用一个类对所有的音效进行管理罢了。Audio.hAudio.cpp好了,本系列教程到此结束,第一次写教程如有不对请见谅或指教,谢谢大家。最后附上整个项目的源代码点击打开链接

  9. 03 Helloworld

    程序都有一个入口点,在C++就是main函数了,打开main.cpp,代码如下:123456789101112131415161718#include"main.h"#include"AppDelegate.h"#include"cocos2d.h"USING_NS_CC;intAPIENTRY_tWinMain{UNREFERENCED_ParaMETER;UNREFERENCED_ParaMETER;//createtheapplicationinstanceAppDelegateapp;return

  10. MenuItemImage*图标菜单创建注意事项

    学习cocos2dx,看的是cocos2d-x3.x手游开发实例详解,这本书错误一大把,本着探索求知勇于发现错误改正错误的精神,我跟着书上的例子一起调试,当学习到场景切换这个小节的时候,出了个错误,卡了我好几个小时。

返回
顶部