【COCOS2DX-LUA 脚本开发之十二】HYBRID模式-利用ASSETSMANAGER实现在线更新脚本文件LUA、JS、图片等资源(免去平台审核周期)

本站文章均为 李华明Himi 原创,转载务必在明显处注明: 
转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/iphone-cocos2dx/1354.html

首先说明一个问题:

为什么要在线更新资源和脚本文件!?

对于此问题,那要说的太多了,简单概括,如果你的项目已经在google play 或Apple Store 等平台上架了,那么当你项目需要做一些活动或者修改前端的一些代码等那么你需要重新提交一个新版本给平台,这时候你的上架时候是个不确定的时候,具体什么时候能上架,主要跟平台有关,你再着急,也没有用的。

那么如果你的项目是使用脚本语言进行编写的,例如lua,js等等,那么一旦你有需要更新你的项目,你完全可以通过从服务器下载最新的脚本和资源来实现在线更新,免去很多烦恼,至少更新再也不需要平台的审核来限制了不是么~(有些平台是禁止在线更新资源方式的,但是你懂得)

那么如何在项目中实现在线更新呢?则是本章具体需要跟大家分享的教程啦。

(有童鞋问我,单机怎么办?   一般自己搭个服务器,专用于在线更新。不过一般单机不这么做,这套下载更新主要用于网游 )

下面进入本章的重要内容:

在cocos2dx 2.x 引擎的扩展包(extensions)中有一个 AssetsManager

AssetsManager 主要功能就是下载资源到本地,并帮你解压!

如果大家还不知道这个类,那么可以先到cocos2dx引擎的http:///Users/slater/Documents/cocos2d-2.1rc0-x-2.1.2-hotfix/samples/Cpp/AssetsManagerTest 目录下运行示例。

 (注:当前Himi使用的是cocos2dx-2.1.2hotfix版本这个示例在我的mac os无法正常运行)

下面Himi新建个项目来详细讲解AssetsManager:

Himi这里拿lua项目进行,首先创建一个新的cocos2dx-lua 的项目:

第一步:将项目中Resoures目录下的 hello.lua 删除!

     第二步:在AppDelegate.h 中添加如下代码:

先导入所需的头文件:

1

2

3

4

5

6

7

8

9

10

11

#include "cocos2d.h"

#include "AssetsManager.h"

#include "cocos-ext.h"

using namespace std;

using namespace cocos2d;

using namespace extension;

 

#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)

#include <dirent.h>

#include <sys/stat.h>

#endif

继续添加变量和方法名:

1

2

3

    void updateFiles();

    void createDownDir();

    string pathToSave;

pathToSave 变量用于保存下载的路径!用于添加到  CCLuaEngine 引擎中,这样便于CCLuaEngine查找Lua文件!

 第三步:在AppDelegate.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

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

static AssetsManager* pAssetsManager;

 

void AppDelegate::updateFiles(){

    createDownDir();

 

    pAssetsManager = new AssetsManager("https://raw.github.com/HimiGame/himigame/master/hello.zip", "https://raw.github.com/HimiGame/himigame/master/version");

 

    if(pAssetsManager->checkUpdate()){

        if( pAssetsManager->update() ){//改源码

 

            CCLuaEngine* pEngine = CCLuaEngine::defaultEngine();

            CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);

 

            //首先添加下载文件的目录

            pEngine->addSearchPath(pathToSave.c_str());

 

            //继续添加本地hello2的路径到CCLuaEngine中

            string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello2.lua");

            pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str());

 

            //运行下载文件hello.lua

            string runLua = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");

            pEngine->executeScriptFile(runLua.c_str());

 

        }

    }

}

 

void AppDelegate::createDownDir(){

    pathToSave = CCFileUtils::sharedFileUtils()->getWritablePath();

    pathToSave += "Himi";

 

    // Create the folder if it doesn't exist

#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)

    DIR *pDir = NULL;

 

    pDir = opendir (pathToSave.c_str());

    if (! pDir)

    {

        mkdir(pathToSave.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);

    }

#else

if ((GetFileAttributesA(pathToSave.c_str())) == INVALID_FILE_ATTRIBUTES)

{

CreateDirectoryA(pathToSave.c_str(), 0);

}

#endif

 

}

首先介绍createDwomDir函数:

(注:所有连接都是Hmi在GitHub服务器中的,大家可以所以访问)!

此函数主要用于在项目目录下新建一个文件夹,到底创建到哪里,你不用管,交给如下函数:

CCFileUtils::sharedFileUtils()->getWritablePath();

上面这个函数能从ios、android平台自动找到可写入的路径!

createDwomDir 函数中  pathToSave += “Himi”;  主要作用是在getWritablePath()路径后自定义一个目录名!需要不需要都可以的,如果想创建个,那就自定义即可,名字无所谓思密达。

继续介绍  updateFiles 函数:

此函数中,首先我们调用 createDwomDir 函数用于创建我们新的写入目录,并且将目录保存到pathToSave变量中。

然后我们创建了一个 AssetsManager 实例,这里要静态。AssetsManager创建函数有两种,如下:

1

2

3

1. AssetsManager::AssetsManager(const char* packageUrl, const char* versionFileUrl)

 

2. AssetsManager::AssetsManager(const char* packageUrl, const char* versionFileUrl, const char* storagePath)

首先看第一种创建函数:

参数1 :  packgeUrl: 表示需要下载更新的zip包的url地址

参数2 : versionFileUrl :表示获取当前服务器版本号的rul,用于匹配客户端是否需要更新!

第二种创建方式多了一个参数: storagePath 表示我们的自定义包名,如createDwomDir函数中的pathToSave += “Himi” 一句功能一样。

而在AssetsManager类中封装了很多方法,例如检查是否需要更新、更新下载文件、获取packageUrl等。具体方法可看AssetsManager源码!

pAssetsManager->checkUpdate() :通过得到服务器返回的版本号与本地版本号进行匹配如不一致则返回true,反之返回false。

(注:大家可以通过版本号对比,做其他功能,比如更新提示等)

一旦通过判断checkUpdate函数返回true,我们即可调用AssetsManager中的update进行文件更新!

这里要注意:由于当前AssetsManager的源码中并没有给予我们判断文件下载成功的函数!因此Himi与AssetsManager作者联系,我们可以更改update函数让其返回bool类型即可!

(注:update 函数中对版本、下载文件、解压、存储最新版本号等做了判断,因此当此函数返回true,则完成一切操作)

修改方式如下:首先我们到源码AssetsManager.h中将如下upate函数修改:

1

2

3

4

5

virtual void update();

 

修改成如下:

 

virtual bool update();

继续到 AssetsManager.cpp 中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

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

修改为:

 

bool AssetsManager::update()

{

    // 1. Urls of package and version should be valid;

    // 2. Package should be a zip file.

    if (_versionFileUrl.size() == 0 ||

        _packageUrl.size() == 0 ||

        std::string::npos == _packageUrl.find(".zip"))

    {

        CCLOG("no version file url, or no package url, or the package is not a zip file");

        return false;

    }

 

    // Check if there is a new version.

    if (! checkUpdate()) return false;

 

    // Is package already downloaded?

    string downloadedVersion = CCUserDefault::sharedUserDefault()->getStringForKey(KEY_OF_DOWNLOADED_VERSION);

    if (downloadedVersion != _version)

    {

        if (! downLoad()) return false;

 

        // Record downloaded version.

        CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_DOWNLOADED_VERSION, _version.c_str());

        CCUserDefault::sharedUserDefault()->flush();

    }

 

    // Uncompress zip file.

    if (! uncompress()) return false;

 

    // Record new version code.

    CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_VERSION, _version.c_str());

 

    // Unrecord downloaded version code.

    CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_DOWNLOADED_VERSION, "");

 

    CCUserDefault::sharedUserDefault()->flush();

 

    // Set resource search path.

    setSearchPath();

 

    // Delete unloaded zip file.

    string zipfileName = _storagePath + TEMP_PACKAGE_FILE_NAME;

    if (remove(zipfileName.c_str()) != 0)

    {

        CCLOG("can not remove downloaded zip file");

    }

    return true;

}

当我们做了如此的修改后,那么当文件下载完成后则会返回true!

最后我们来看如下代码:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

            CCLuaEngine* pEngine = CCLuaEngine::defaultEngine();

            CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);

 

            //首先添加下载文件的目录

            pEngine->addSearchPath(pathToSave.c_str());

 

            //继续添加本地hello2的路径到CCLuaEngine中

            string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello2.lua");

            pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str());

 

            //运行下载文件hello.lua

            string runLua = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");

            pEngine->executeScriptFile(runLua.c_str());

 

 

首先我们将文件更新下来的路径通过setScriptEngine添加到 CCLuaEngine中,然后将hello2.lua 的路径也添加到CCLuaEngine的搜索途径中,这样一来 CCLuaEngine 会从我们设置的这两个路径中去找我们在lua中require的对应lua文件!这一步设置必须设置!因为CCLuaEngine不像cocos2dx那样自动帮我们找文件路径!CCLuaEngine 是不存在路径的,所以我们要手动设置CCLuaEngine搜索路径,以便找到对应的lua文件!

也正是因为CCLuaEngine不会自动帮我们找文件路径,因此我们运行lua脚本时,必须将运行的脚本lua文件完整的路径传入,如下:

1

2

3

//运行下载文件hello.lua

            string runLua = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");

            pEngine->executeScriptFile(runLua.c_str());

      下面我们开始书写测试代码:

在AppDelegate.cpp中的  applicationDidFinishLaunching 函数中注释一些代码并且添加测试代码,修改后的 applicationDidFinishLaunching 函数内容如下:

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

bool AppDelegate::applicationDidFinishLaunching()

{

    // initialize director

    CCDirector *pDirector = CCDirector::sharedDirector();

    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());

 

    // turn on display FPS

    pDirector->setDisplayStats(true);

 

    // set FPS. the default value is 1.0/60 if you don't call this

    pDirector->setAnimationInterval(1.0 / 60);

 

    // register lua engine

//    CCLuaEngine* pEngine = CCLuaEngine::defaultEngine();

//    CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);

//

//#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

//    CCString* pstrFileContent = CCString::createWithContentsOfFile("hello.lua");

//    if (pstrFileContent)

//    {

//        pEngine->executeString(pstrFileContent->getCString());

//    }

//#else

//    std::string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");

//    pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str());

//    pEngine->executeScriptFile(path.c_str());

//#endif

 

    //删除hello.lua

    pathToSave="";

    updateFiles();

 

    return true;

}

下面开始运行!需要注意的是我们本地是完全不存在hello.lua文件的,所以一旦我们运行成功出现画面说明已经利用AssetsManager成功的在线下载了hello.lua文件!

运行截图如下:

 

 

从如上的运行截图中可以看出,首先我们得到服务器传来的版本号2.1.1,然后进行checkUpdate函数,此函数是从本地的存储文件找是否有版本号,如果没有那么就默认为可下载,如果有则会对比,如不一致则进行更新。

那么当文件完整下载下来之后update函数则自动会我们在本地保存最新从服务器拿到的版本号!紧接着update函数还为我们进行了对zip文件的解压,解压成功后会自动删除zip包!

因此如果大家运行过自己的这个项目成功下载运行了,那么下载运行请删除项目后再运行,因为第一次的成功运行已经将最新版本号记录保存下来了,你也可以通过修改服务器版本号或者删除项目的存储文件。

 

总结本文的教程:

第一:     CCLuaEngine 引擎是不会自动帮我们找文件的,所以你一旦有一个新的运行脚本的路径,一定要通过  CCLuaEngine的 addSearchPath函数告知!这样的话,当你的一个lua文件采用require其他脚本文件,CCLuaEngine就会在你 addSearchPath的路径中进行查找!

第二: 如果你想让自己项目自带的脚本与下载脚本同时使用,例如自己项目有a.lua 其中a.lua 中包含一句代码: requireb  “b”   ,而b.lua是你通过在线更新下载下来的。那么a.lua 和 b.lua的路径都要通过 addSearchPath 设置下各自的路径。

第三: lua engine应该也是支持搜索路径的优先级的,所以你可以通过控制pEngine->addSearchPath()的调用顺序,从而控制当你本地项目与下载更新同时拥有同一个名字的脚本等资源,可以优先选择使用哪个!

第四: 在AppStore 规定不允许在主游戏线程中进行联网,然后我们使用的AssetsManager的下载更新却是在联网下载,所以大家要使用异步来做!另外及时没有这条规定我想童鞋们也不会让联网放主游戏线程中吧!

第五:AssetsManager 中还有其他的功能,更多的功能请大家自己看cocos2dx引擎的示例项目!

 

最后给出Himi的示例项目下载地址:

           OLUpdateFilesByHimi   :    http://vdisk.weibo.com/s/ycZU1

 

:Himi:    本篇就到这里,希望对各位没有做过、正要做此功能的童鞋们提供帮助!

时间: 2024-09-03 12:57:30

【COCOS2DX-LUA 脚本开发之十二】HYBRID模式-利用ASSETSMANAGER实现在线更新脚本文件LUA、JS、图片等资源(免去平台审核周期)的相关文章

微信公众平台开发(十二) 发送客服消息

原文:微信公众平台开发(十二) 发送客服消息 一.简介 当用户主动发消息给公众号的时候(包括发送信息.点击自定义菜单.订阅事件.扫描二维码事件.支付成功事件.用户维权),微信将会把消息数据推送给开发者,开发者在一段时间内(目前修改为48小时)可以调用客服消息接口,通过POST一个JSON数据包来发送消息给普通用户,在48小时内不限制发送次数.此接口主要用于客服等有人工消息处理环节的功能,方便开发者为用户提供更加优质的服务. 二.思路分析 官方文档中只提供了一个发送客服消息的接口,开发者只要POS

【COCOS2DX-LUA 脚本开发之十】使用LUA CJSON库进行ENCODE与DECODE操作完成对JSON数据转化

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/lua-game/1337.html 本篇介绍如何在lua中对数据进行json的encode与decode,这里Himi采用cjson进行.首先简单介绍下cjson: Lua CJSON 是 Lua 语言提供高性能的 JSON 解析器和编码器,其性能比纯 Lua 库要高 10 到 20 倍.Lua CJSON 完全支持 UTF-8 ,无需依赖

【COCOS2DX-LUA 脚本开发之十四】解决自定义CPP类通过TOLUA++ BINDING LUACOCOS2D后编译到ANDROID运行黑屏(没有调用自定义CPP类)的问题!

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/lua-game/1388.html 唉,首先说点闲话 – -.Himi搞了不短的时间,这个问题一直没有解决,最后终于在张大(cocos2dx引擎开发者之一 张小明)的指导下解决了此问题. 本章基于上一篇  [COCOS2DX-LUA 脚本开发之十三]  与之前的项目整合 [Cocos2d-X(2.x) 游戏开发系列之二]cocos2dx最新

【IOS-COCOS2D-X 游戏开发之十二】自定义COCOS2DX摇杆(增强JOYSTICK),增加摇杆跟随用户触点作为摇杆坐标,让摇杆不再死板!

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/iphone-cocos2dx/721.html 对于虚拟摇杆在游戏开发中必不可少,Android方面的是由Himi自己实现封装的,大家可以移步到这里查看详细实现机制: [Android游戏开发二十四]360°平滑游戏摇杆(触屏方向导航)  那么在Cocos2d引擎已提供此摇杆类(Joystick),所以Himi也就懒得重写了,但是Cocos

Android简明开发教程十二:引路蜂二维图形库简介及颜色示例

AndroidGraphics2DTutorial定义了应用的主Activity,下面就可以开始写每个具体的二维绘图示例.不同的例子将尽量采用 不同的UI控件:Menu,Content Menu,Dialog,Custom Dialog,Button等等.例子采用了引路蜂二维图形库,引路蜂二维图形 库Graphics 2D API实现了移动平台(Java ME,Blackberry,iPhone,Android,Windows Phone)上图形引擎,它能够以一种统一的方 式处理各种基本图形(S

Kinect for Windows SDK开发入门(十二)语音识别 上

Kinect的麦克风阵列在Kinect设备的下方.这一阵列由4个独立的水平分布在Kinect下方的麦克风组成.虽然每一个麦克风都捕获相同的音频信号,但是组成阵列可以探测到声音的来源方向.使得能够用来识别从某一个特定的方向传来的声音.麦克风阵列捕获的音频数据流经过复杂的音频增强效果算法处理来移除不相关的背景噪音.所有这些复杂操作在Kinect硬件和Kinect SDK之间进行处理,这使得能够在一个大的空间范围内,即使人离麦克风一定的距离也能够进行语音命令的识别. 在Kinect第一次作为Xbox3

微信公众帐号开发教程(十二) 符号表情的发送(下)

引言及文章概要 第11篇文章给出了Unified版本的符号表情(emoji表情)代码表,并且介绍了如何 在微信公众帐号开发模式下发送emoji表情,还在文章结尾出,卖了个关子:"小q机器人中使用的一些符号表 情,在微信的符号表情选择栏里根本找不到,并且在上篇文章给出的符号表情代码表(Unified版)中也没有 ,那这些表情是如何发送的呢?"如下面两张图所示的符号表情"情侣"和"公共汽车". 本文主要介绍以下内容:1)如 何在微信上使用更多的符号表

仿酷狗音乐播放器开发日志十二——播放列表的实现六

转载请说明出处,谢谢~~      前天和昨天只顾看电视剧了,没有写程序,好在把<孤岛飞鹰>看完了,这下在可以安心写代码了 ^_^     上次说道还要开发分组控件,分组控件的开发过程要比其他两个控件要简单得多,因为他的功能相对要少,而且自身的状态不像音乐播放项目哪个多,所以外观的编写比较简单,主要的功能就是所以一个容器来盛放音乐播放项目CMusicListItemUI控件.     他同样是继承自CTreeNodeUI控件,然后我重写了DoEvent函数,过滤了几个消息的处理,让他的行为和原

Windows 8风格应用开发入门 十二 SearchContract概述及原理

Search Contract概述 使用过Windows 8操作系统的开发者都知道什么是Charms(超级按钮).其中在Charms中包含了一个Search,微软称之为Search Contact,我们可以在应用中调用Search Contract相应的API来实现应用搜索功能. 使用Search Contract可以从系统的任何位置进入到我们的应用进行搜索,意思就是不管我们在操作系统上面做任何事情都可以随时使用Search Contract对第三方应用进行应用内搜索. Search Contr