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\n", 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);

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

Quick-Cocos2d-x v3.3 异步加载Spine方案 转的更多相关文章

  1. 请给出异步加载js方案

    请给出异步加载js方案,不少于两种 默认情况javascript是同步加载的,也就是javascript的加载时阻塞的,后面的元素要等待javascript加载完毕后才能进行再加载,对于一些意义不是很 ...

  2. RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2->新增“行政区域管理”,同时大批量树采用异步加载

    行政区划:简称政区,是国家为了进行分级管理而实行的区域划分.中国现行的行政区划实行如下原则:1.全国分为省.自治区.直辖市:2.省.自治区分为自治州.县.自治县.市:3.自治州分为县.自治县.市:4. ...

  3. 【Cocos2d-Js基础教学(5)资源打包工具的使用及资源的异步加载处理】

    TexturePacker是纹理资源打包工具,支持Cocos2dx的游戏资源打包. 如果用过的同学可以直接看下面的资源的异步加载处理 首先为什么用TexturePacker? 1,节省图片资源实际大小 ...

  4. python(4) 小程序-异步加载

    注:处理异步加载需要模拟浏览器登陆,然后用import json,用loads解析 例如:

  5. Jquery zTree结合Asp.net实现异步加载数据

    zTree结合Asp.net实现异步加载数据 实现简单操作 zTree 下载 api 访问 :http://www.ztree.me/v3/main.php 例子中用到json数据转化 newtons ...

  6. 【转】【玩转cocos2d-x之二十三】多线程和同步03-图片异步加载

    原创作品,转载请标明:http://blog.csdn.net/jackystudio/article/details/15334159 cocos2d-x中和Android,Windows都 一样, ...

  7. cocos2dx lua中异步加载网络图片,可用于显示微信头像

    最近在做一个棋牌项目,脚本语言用的lua,登录需要使用微信登录,用户头像用微信账户的头像,微信接口返回的头像是一个url,那么遇到的一个问题就是如何在lua中异步加载这个头像,先在引擎源码里找了下可能 ...

  8. ztree异步加载树节点

    参考文档:https://www.cnblogs.com/tenWood/p/8620708.html ztree api地址:http://www.treejs.cn/v3/api.php 说明:j ...

  9. ztree插件的使用及列表项拖拽的实现(jQuery)+异步加载节点数据

    为了实现如图所示的树状结构图,并使列表项可拖动到盒子里,研究了ztree这个插件的使用,并仔细研究了列表项的拖动事件.完成了预期需求,对jQuery的运用得到了提高.这个插件的功能非常强大,除了基本的 ...

随机推荐

  1. armv8(aarch64)linux内核中flush_dcache_all函数详细分析【转】

    转自:http://blog.csdn.net/qianlong4526888/article/details/12062809 版权声明:本文为博主原创文章,未经博主允许不得转载. /* *  __ ...

  2. Hibernate,Session清理缓存时间点

    当应用程序调用org.hibernate.Transaction的commit()的时候,commit()方法先清理缓存,然后再向数据库提交事务. 当应用程序显示调用Session.flush()方法 ...

  3. PHP建站通过服务器架构及实战的方法

    PHP的环境搭建 PHP的帮助使用和配置文件 PHP的Hello World PHP的库函数调用 PHP的Web程序 PHP的函数和面向对象使用 PHP的数据库访问 Nginx安装和配置访问 Word ...

  4. 搭建HTTP Live Streaming直播系统

    最近,需要将苹果的HTTP Live Streaming系统搭建起来.完全没有头绪,故第一步就是学习. 一.学习资料 官网资料 1. http://developer.apple.com/resour ...

  5. Jquery知识

    1.窗口自适应调整 (设置layout的fit属性值为true即可) //窗口自适应调整 $(function() { windowResize(); //文档载入时加载 $(window).resi ...

  6. Asp.net Vnext ModelBinding

    Model Binding 本文已经同步到<Asp.net Vnext 系列教程 >中] Model binding(绑定)简单来说就是通过遍历ValueProvider(值提供者)获取的 ...

  7. cocos2dx win打包apk

    1.配置环境(未完) 2. eclipse 导入项目之后配置 Builder新建两个.一个是ndk目录下的 ndk-build.cmd  ,一个是自己写的build_native.bat 拷贝资源的 ...

  8. 【转】写一个标准宏MIN,这个宏输入两个参数并返回较小的那个。另外当写下least=MIN(*p++,b)时会发生什么

    解答:#define MIN(A,B) ((A) <= (B) ? (A) : (B)) MIN(*p++, b)会产生宏副作用 剖析:这个面试题主要考察宏定义可以实现函数的功能.但是它终归不是 ...

  9. PoJ(2263),Floyd,最小值中的最大值

    题目链接:http://poj.org/problem?id=2263 题意:题中给出相连通不同城市之间的载货量,要求找到一条从指定起点到终点的路径,并满足载货量最大. #include <io ...

  10. Python_cmd的各种实现方法及优劣(subprocess.Popen, os.system和commands.getstatusoutput)

    http://blog.csdn.net/menglei8625/article/details/7494094