zoukankan      html  css  js  c++  java
  • Quick-Cocos2d-x v3.3 异步加载Spine方案 转

    Quick-Cocos2d-x v3.3 异步加载Spine方案

    浩月难求也与2015-03-25 15:06:3441 次阅读

    背景

    项目中使用了Quick-Cocos2d-x 3.3,由于Spine各个功能相当强大,所以使用了Spine作为骨骼动画,由于Spine并非cocos官方支持,所以在一些问题上支持性不是那么好,其中如何异步加载Spine就是一个问题。

    比如游戏中经常遇到这么一个问题:在主战斗场景中,需要加载大量的图片、声音、Spine动画等,如果我们等到需要用到的时候再去加载,由于文件I/O是比较耗时的,有可能就会在游戏过程造成卡顿的现象,尤其在低端配置手机中体现尤为明显,这就会造成游戏体验不佳。

    对于这个问题呢,一种比较常用的解决方案:在进入主战斗之前,就会预加载所有战斗中需要用到的资源,并且缓存起来,等到真到需要用到游戏资源的时候,直接去读取缓存中的数据,那就是我们经常看到的Loading条。

    难点

    1、异步加载 

    Cocos本身已经支持了一些资源的异步加载回调,使用多线程做的,每加载完一个资源,都可以有异步回调作相应的处理(比如显示加载进度)。但是 对于一些cocos官方不支持的资源,比如Spine等,就需要自己实现异步加载回调。

    自己实现我知道有两种方法:

    一种就是和cocos官方的一样,也用多线程做,这个没什么好说的。 

    一种就是叫做”同步异步化“,开启一个每帧都update函数,在update()里加载一次资源,用同步的方式实现异步加载的资源。

    “同步异步化”在Lua中还有另一种方法可以实现,就是用Lua的协程,更加的方便。

    我自己使用的是使用的update这种方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     function LoadingLayer:update(ts)   -  --- 每帧加载一次资源
        local pngNum = table.nums(ResMng._pngList)
        local plistNum = table.nums(ResMng._plistList)
        local soundNum = table.nums(ResMng._soundList)
        local spineNum = table.nums(ResMng._spineList)
        local curLoadNum = ResMng._curLoadNum
     
        -- 加载资源顺序
        -- 1、加载PNG
        if 0 <= curLoadNum and curLoadNum < pngNum then
            ResMng.loadPng()
     
        -- 2、加载PLIST
        elseif pngNum <= curLoadNum and curLoadNum < pngNum + plistNum then
            ResMng.loadPlist()
     
        -- 3、加载SOUND    
        elseif pngNum + plistNum <= curLoadNum and curLoadNum < pngNum + plistNum + soundNum then
            ResMng.loadSound()
     
        -- 4、加载Spine
        elseif pngNum + plistNum + soundNum <= curLoadNum and curLoadNum < pngNum + plistNum + soundNum + spineNum then
            ResMng.loadSpine()
     
        else
            print("所有资源预加载完成")
     
            ResMng.resortSpine()
     
            self:replaceScene()
     
        end 
    end


    2、Spine缓存 

    Cocos官方都对一些常用的资源支持了缓存,如TextureCache、AnimationCache、SpirteFrameCache、AudioCache,但是由于Spine并非官方支持,所以对于Spine的缓存需要自己实现,对于Spine缓存实现方式,也有两种方法可以参考: 

    1、在C++层,实现一个和官方类似的缓存方案,叫做SpineCache,然后增加tolu层代码,这种方式优雅、自然,但是代价比较大。

    2、在Lua层手动实现一个简易的缓存方案。

    这里我当然是使用的第二种方案了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    -- 加载一次Spine图
    function ResMng.loadSpine()
        local spineInfo = self._spineList[self._curLoadIndex]
        assert(spineInfo, "加载Spine索引出错" .. self._curLoadIndex)
     
        local spineAnimation = require("app.common.factory.sp_animation").new()
        spineInfo.animation = spineAnimation:createWithID(spineInfo.id)
        spineInfo.animation:getNode():retain()
     
        self._curLoadNum = self._curLoadNum + 1
        if self._curLoadIndex == #self._spineList then
            self._curLoadIndex = 1
        else
            self._curLoadIndex = self._curLoadIndex + 1
        end
    end
     
    -- 从缓存中获取Spine
    function ResMng.getSpineByID(spineID)
        -- 如果不存在此资源,先加载到缓存
        if not self._spineList[spineID] then
            print("缓存文件:", spineID)
            local spineAnimation = require("app.common.factory.sp_animation"):new()
            self._spineList[spineID] = spineAnimation:createWithID(spineID)
            self._spineList[spineID]:getNode():retain()
        end
     
        local newSpine = require("app.common.factory.sp_animation"):new()
        return newSpine:createWithData(self._spineList[spineID])
    end

    3、导出createWithData到Lua层调用 

    Spine Runtime,加载Spine有两种方式: 

    一个是createWithFile, 使用的是文件IO方式,这个相当耗时 

    一个是createWithData,使用的是已经加载好的Spine的骨骼数据,这种方法速度比较快。 

    但是Quick默认两个方法都不自动导出,都是用的手写代码导出,且只手动导出createWithFile, 如果我们需要使用createWithData,这时就需要手动导出了,导出代码如下: 

    LuaSkeletonAnimation.h

    1
    2
    3
    4
    5
    6
    7
    8
    class LuaSkeletonAnimation: public spine::SkeletonAnimation {
    public:
        static LuaSkeletonAnimation* createWithFile (const char* skeletonDataFile, const char* atlasFile, float scale = 1);
        static LuaSkeletonAnimation* createWithData(SkeletonAnimation* spineData);
        LuaSkeletonAnimation (const char* skeletonDataFile, const char* atlasFile, float scale = 1);
        LuaSkeletonAnimation(SkeletonAnimation* spineData);
        virtual ~LuaSkeletonAnimation();
    };

    LuaSkeletonAnimation.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    LuaSkeletonAnimation::LuaSkeletonAnimation (const char* skeletonDataFile, const char* atlasFile, float scale)
    : spine::SkeletonAnimation(skeletonDataFile, atlasFile, scale)
    {
    }
    LuaSkeletonAnimation::LuaSkeletonAnimation(SkeletonAnimation* spineData)
    : spine::SkeletonAnimation(spineData)
    {
    }
    LuaSkeletonAnimation* LuaSkeletonAnimation::createWithFile (const char* skeletonDataFile, const char* atlasFile, float scale)
    {
        LuaSkeletonAnimation* node = new (std::nothrow) LuaSkeletonAnimation(skeletonDataFile, atlasFile, scale);
        node->autorelease();
        return node;
    }
    LuaSkeletonAnimation* LuaSkeletonAnimation::createWithData(SkeletonAnimation* spineData)
    {
        LuaSkeletonAnimation* node = new (std::nothrow) LuaSkeletonAnimation(spineData);
        node->autorelease();
        return node;
    }

    lua_cocos2dx_spine_manual.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    //add by chenhao on 2015/03/24
    //手动添加Spine tolua代码,解决Spine缓存问题 
    static int lua_cocos2dx_CCSkeletonAnimation_createWithData(lua_State* L)
    {
        if (nullptr == L)
            return 0;
        int argc = 0;
        #if COCOS2D_DEBUG >= 1
            tolua_Error tolua_err;
            if (!tolua_isusertable(L, 1, "sp.SkeletonAnimation", 0, &tolua_err)) goto tolua_lerror;
        #endif
        argc = lua_gettop(L) - 1;
        if (1 == argc)
        {
        #if COCOS2D_DEBUG >= 1
            if (!tolua_isusertable(L, 1, "sp.SkeletonAnimation", 0, &tolua_err))
            {
                goto tolua_lerror;
            }
        #endif
            SkeletonAnimation* spineData = (SkeletonAnimation*)tolua_tousertype(L, 2, 0);
            auto tolua_ret = LuaSkeletonAnimation::createWithData(spineData);
            int nID = (tolua_ret) ? (int)tolua_ret->_ID : -1;
            int* pLuaID = (tolua_ret) ? &tolua_ret->_luaID : NULL;
            toluafix_pushusertype_ccobject(L, nID, pLuaID, (void*)tolua_ret, "sp.SkeletonAnimation");
            return 1;
        }
        luaL_error(L, "'createWithFile' function of SkeletonAnimation has wrong number of arguments: %d, was expecting %d ", argc, 2);
        #if COCOS2D_DEBUG >= 1
            tolua_lerror:
            tolua_error(L, "#ferror in function 'createWithData'.", &tolua_err);
        #endif
        return 0;
    }

    并且在extendCCSkeletonAnimation()中,添加下面这行代码。

    1
    tolua_function(L, "createWithData", lua_cocos2dx_CCSkeletonAnimation_createWithData);

    为了减少导出自定义类, 我对C++层createWithData的参数类型作了一些修改,但是不影响。改为了:

    1
    static SkeletonAnimation* createWithData(SkeletonAnimation* spineData);

    以上都是本人自己实现摸索过程中, 探索出来的一些方法,有什么问题大家可以提出来,我好修改

  • 相关阅读:
    前端基础之BOM和DOM
    前端基础之JavaScript
    前端基础之CSS
    Black And White HDU
    Robot Motion HDU
    A Knight's Journey POJ
    Find a way HDU
    排序 HDU
    Dungeon Master POJ
    Network Saboteur POJ
  • 原文地址:https://www.cnblogs.com/rexzhao/p/4366229.html
Copyright © 2011-2022 走看看