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);

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

  • 相关阅读:
    rs
    stm32f767 usoc3
    stm32f767 RTT 日志
    stm32f767 标准库 工程模板
    stm32f767 HAL 工程模板
    docker tab 补全 linux tab 补全
    docker anconda 依赖 下载 不了
    docker run 常用 指令
    linux scp 命令
    Dockerfile 常用参数说明
  • 原文地址:https://www.cnblogs.com/rexzhao/p/4366229.html
Copyright © 2011-2022 走看看