Cocos2d-lua自动重新加载SpriteFrames

昨天在iOS上测试清宫Q传,玩了几把突然报错:

[LUA-print] ASSERT FAILED ON LUA EXECUTE: Invalid spriteFrameName :spsheet_1/img_004

当时一副懵逼( ⊙ o ⊙ ),合图spsheet_1肯定是在进入游戏的时候加载了,而且合图内其他的图片都正常显示了,怎么会突然找不到呢?

赶紧翻翻日志,在报错前果然找到了罪魁祸首:

2016-05-03 16:09:34.053 game[24503:15980923] Received memory warning.

让我们看看收到内存警告时都做了什么。

打开AppController.mm

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
     cocos2d::Director::getInstance()->purgeCachedData();
}

再进入CCDirector.cpp

void Director::purgeCachedData(void)
{
    FontFNT::purgeCachedData();
    FontAtlasCache::purgeCachedData();

    if (s_SharedDirector->getOpenGLView())
    {
        // 就是这里了
        SpriteFrameCache::getInstance()->removeUnusedSpriteFrames();
        _textureCache->removeUnusedTextures();

        // 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();
}

嗯,在内存警告时cocos2d-x会清理缓存数据,包括字体、纹理、spriteFrame等。那么问题来了,在游戏进行到一半的时候,突然缓存被清理了,该怎么进行呢?显然cocos2d-x并没有自动恢复机制。

只好自己写一个凑合用一下了。(>﹏<)

原理是这样的:

  • 调用cc.SpriteFrameCache:getInstance():addSpriteFrames(plist)时,记录一下plist里framename和plist的关系,伪代码如下:
local _map = {}
function cacheSpritePlist(plist)
    -- avoid muliple loading of the same plist
    if tableValuesContain(_map, plist) then
        logi("Frames Already added:", plist)
        return
    end
    -- read plist data
    local data = __readPList(plist)
    -- record framename -> plistname
    for fname, _ in pairs(data.frames) do
        self.map[fname] = plist
    end
end
  • 在任何设置sprite frame的地方,比如:
-- 设置sprite的spriteframe
sprite:setSpriteFrame(framename)
-- 或用spriteframe生成sprite
cc.Sprite:createWithSpriteFrameName(framename)
-- 等等

都需要事先检查一下spriteFrame是否存在,如果不存在,则去查找对应的plist,重新加载一遍,伪代码如下:

function reload(framename)
    local plist = _map[framename]
    cc.SpriteFrameCache:getInstance():addSpriteFrames(plist)
end

local SpCache = cc.SpriteFrameCache:getInstance() 
function checkFrame(framename)
    if not SpCache:getSpriteFrame(framename) then
        reload(framename)
    end
end

恩,大概就这样了,可以再加一个cleanup函数,当某个合图不再使用,就把_map里有关这个合图的数据都清理掉,节约内存空间,这块伪代码就不写了。

测试了一下,不会再报错了。

这种问题之前应该也有人遇到过,不知道有没有更好的解决方案。我总感觉这是Cocos2d-x应该背的锅。╮(╯_╰)╭

你可能感兴趣的:(iOS,lua,cocos2d-x)