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

转载自: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 #include "cocos2d.h"
 2 #include "AssetsManager.h"
 3 #include "cocos-ext.h"
 4 using namespace std;
 5 using namespace cocos2d;
 6 using namespace extension;
 7
 8 #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
 9 #include <dirent.h>
10 #include <sys/stat.h>
11 #endif

继续添加变量和方法名:

1 void updateFiles();
2 void createDownDir();
3 string pathToSave;

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

第三步:在AppDelegate.cpp 中添加如下代码:

 1 static AssetsManager* pAssetsManager;
 2
 3 void AppDelegate::updateFiles(){
 4     createDownDir();
 5
 6     pAssetsManager = new AssetsManager("https://raw.github.com/HimiGame/himigame/master/hello.zip", "https://raw.github.com/HimiGame/himigame/master/version");
 7
 8     if(pAssetsManager->checkUpdate()){
 9         if( pAssetsManager->update() ){//改源码
10
11             CCLuaEngine* pEngine = CCLuaEngine::defaultEngine();
12             CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);
13
14             //首先添加下载文件的目录
15             pEngine->addSearchPath(pathToSave.c_str());
16
17             //继续添加本地hello2的路径到CCLuaEngine中
18             string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello2.lua");
19             pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str());
20
21             //运行下载文件hello.lua
22             string runLua = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");
23             pEngine->executeScriptFile(runLua.c_str());
24
25         }
26     }
27 }
28
29 void AppDelegate::createDownDir(){
30     pathToSave = CCFileUtils::sharedFileUtils()->getWritablePath();
31     pathToSave += "Himi";
32
33     // Create the folder if it doesn‘t exist
34 #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
35     DIR *pDir = NULL;
36
37     pDir = opendir (pathToSave.c_str());
38     if (! pDir)
39     {
40         mkdir(pathToSave.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
41     }
42 #else
43     if ((GetFileAttributesA(pathToSave.c_str())) == INVALID_FILE_ATTRIBUTES)
44     {
45         CreateDirectoryA(pathToSave.c_str(), 0);
46     }
47 #endif
48
49 }

首先介绍createDwomDir函数:

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

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

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

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

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

继续介绍  updateFiles 函数:

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

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

1  AssetsManager::AssetsManager(const char* packageUrl, const char* versionFileUrl)
2
3  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 virtual void update();
2
3 修改成如下:
4
5 virtual bool update();

继续到 AssetsManager.cpp 中update函数进行修改成如下:

 1 修改为:
 2
 3 bool AssetsManager::update()
 4 {
 5     // 1. Urls of package and version should be valid;
 6     // 2. Package should be a zip file.
 7     if (_versionFileUrl.size() == 0 ||
 8         _packageUrl.size() == 0 ||
 9         std::string::npos == _packageUrl.find(".zip"))
10     {
11         CCLOG("no version file url, or no package url, or the package is not a zip file");
12         return false;
13     }
14
15     // Check if there is a new version.
16     if (! checkUpdate()) return false;
17
18     // Is package already downloaded?
19     string downloadedVersion = CCUserDefault::sharedUserDefault()->getStringForKey(KEY_OF_DOWNLOADED_VERSION);
20     if (downloadedVersion != _version)
21     {
22         if (! downLoad()) return false;
23
24         // Record downloaded version.
25         CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_DOWNLOADED_VERSION, _version.c_str());
26         CCUserDefault::sharedUserDefault()->flush();
27     }
28
29     // Uncompress zip file.
30     if (! uncompress()) return false;
31
32     // Record new version code.
33     CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_VERSION, _version.c_str());
34
35     // Unrecord downloaded version code.
36     CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_DOWNLOADED_VERSION, "");
37
38     CCUserDefault::sharedUserDefault()->flush();
39
40     // Set resource search path.
41     setSearchPath();
42
43     // Delete unloaded zip file.
44     string zipfileName = _storagePath + TEMP_PACKAGE_FILE_NAME;
45     if (remove(zipfileName.c_str()) != 0)
46     {
47         CCLOG("can not remove downloaded zip file");
48     }
49     return true;
50 }

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

最后我们来看如下代码:

 1 CCLuaEngine* pEngine = CCLuaEngine::defaultEngine();
 2 CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);
 3
 4 //首先添加下载文件的目录
 5 pEngine->addSearchPath(pathToSave.c_str());
 6
 7 //继续添加本地hello2的路径到CCLuaEngine中
 8 string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello2.lua");
 9 pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str());
10
11 //运行下载文件hello.lua
12 string runLua = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");
13 pEngine->executeScriptFile(runLua.c_str());
14
15
16  

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

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

1 //运行下载文件hello.lua
2             string runLua = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");
3             pEngine->executeScriptFile(runLua.c_str());

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

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

 1 bool AppDelegate::applicationDidFinishLaunching()
 2 {
 3     // initialize director
 4     CCDirector *pDirector = CCDirector::sharedDirector();
 5     pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
 6
 7     // turn on display FPS
 8     pDirector->setDisplayStats(true);
 9
10     // set FPS. the default value is 1.0/60 if you don‘t call this
11     pDirector->setAnimationInterval(1.0 / 60);
12
13     // register lua engine
14 //    CCLuaEngine* pEngine = CCLuaEngine::defaultEngine();
15 //    CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);
16 //
17 //#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
18 //    CCString* pstrFileContent = CCString::createWithContentsOfFile("hello.lua");
19 //    if (pstrFileContent)
20 //    {
21 //        pEngine->executeString(pstrFileContent->getCString());
22 //    }
23 //#else
24 //    std::string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");
25 //    pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str());
26 //    pEngine->executeScriptFile(path.c_str());
27 //#endif
28
29     //删除hello.lua
30     pathToSave="";
31     updateFiles();
32
33     return true;
34 }

下面开始运行!需要注意的是我们本地是完全不存在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

【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期),布布扣,bubuko.com

时间: 2024-10-24 21:54:52

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

实习小白::(转) Cocos2d-x 3.0 开发(十五)使用UILayout布局,制作对话界面

1.概述 上一篇我们在编辑器中设计了一个静态的UIScrollView,而通常我们都需要在程序中动态增加信息.插入元素的位置怎么确定?在3.0中UILayout已经实现了基本的布局,一起来看看吧: 2.编辑界面 打开CocoStudio的UIEditor 编辑一个界面,创建一个ScrollView和两个Button.因为ScrollView继承自UILayout,这里我们采用它.不太能搞定的童鞋可参考:Cocos2d-x 3.0 开发(十四)使用UIScrollView 实现大小不同物品拖动展示

Cocos2d-x 3.x 开发(十八)10行代码看自动Batch,10行代码看自动剔除

1.概述 在游戏的运行过程中,图形的绘制是非常大的开销.对于良莠不齐的Android手机市场,绘制优化较好的游戏,可以在更多的手机上运行,因此也是优化的重中之重.图形方面的优化主要体现在减少GUP的绘制次数上.这里我们分别从自动优化渲染批次和绘制剔除两个方面来看新版本在绘制上的优化. 2.自动batch 在Cocos2d-x 3.x中,抛弃了先前手动编写BatchNode,采用自动管理的方式.说起BatchNode,就难免涉及到显卡底层的绘制原理.简单的说,每提交一条绘制指令到显卡都会产生消耗,

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

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

从零开始学ios开发(十二):Table Views(中)UITableViewCell定制

我们继续学习Table View的内容,这次主要是针对UITableViewCell,在前一篇的例子中我们已经使用过UITableViewCell,一个默认的UITableViewCell包含imageView.textLabel.detailTextLabel等属性,但是很多时候这些默认的属性并不能满足需要,其实更多的时候我们想自己制定UITableViewCell的内容,这篇学习的就是制定自己的UITableViewCell. UITableViewCell继承自UIView,因此它可以加载

QT开发(十二)——QT事件处理机制

QT开发(十二)--QT事件处理机制 一.QT事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下和松开 鼠标事件: 鼠标移动,鼠标按键的按下和松开 拖放事件: 用鼠标进行拖放 滚轮事件: 鼠标滚轮滚动 绘屏事件: 重绘屏幕的某些部分 定时事件: 定时器到时 焦点事件: 键盘焦点移动 进入和离开事件: 鼠标移入widget之内,或是移出 移动事件: widget的

Linux系统裁剪之二(Bash脚本编程之十二)

Linux系统裁剪之二(Bash脚本编程之十二) 系统函数库 ·Linux系统的启动流程     1,POST(加电自检) 计算机本身并不会执行程序,它只是一堆破铜烂铁,但是它可以在开机的时候先去载入一段程序,系统在刚刚启动的时候能够实现将某个ROM芯片中的程序映射到CPU能够寻址的地址空间中去,并且让CPU能够执行其中的指令,这些指令大部分都是用来做系统检测的,当检测完成后,如果系统中所有的基本硬件和核心硬件都没有问题的话,接下来就会根据BIOS中设定的系统启动次序(Boot Sequence

嵌入式Linux裸机开发(十二)——iNand简介

嵌入式Linux裸机开发(十二)--iNand简介 一.iNand简介 iNand是SanDisk公司研发的存储芯片,可以看成SD卡或MMC卡芯片化. iNand是SanDisk公司符合eMMC协议的芯片系列名称,内部采用MLC存储颗粒.iNand接口电路设计复杂,功能完善,提供eMMC接口协议,与SoC的eMMC控制器配对通信. 相对MLC NandFlash,iNAND有以下优点: 1.提高性能 A.减少SOC的工作量,节约SOC资源. 如果使用MLC做存储,SOC要参与FLASH的坏块管理

从零开始学ios开发(十二):Table Views(上)

这次学习的控件非常重要且非常强大,是ios应用中使用率非常高的一个控件,可以说几乎每个app都会使用到它,它就是功能异常强大的Table Views.可以打开你的iphone中的phone.Messages.Contacts.Mail.Settings等等等等,这些都用到了Table Views. 在Table Views中,Table是用来显示一系列数据的,每条数据占用且只占用一行(一个table cell),在ios中没有规定table到底可以容纳多少行数据,也就是说,只要内存足够多,tab

微信开发H5十二人牛牛出租源码下载搭建

微信开发H5十二人牛牛出租源码下载搭建h5.fanshubbs.com联系Q1687054422不同于传统的手游商店下载模式,HTML5 手机网页游戏是可以直接运行在微信内置的浏览器里. 先上图,感知一下具体样子: 而我想分享的是我们在具体开发实现过程中,基于微信的Html5 WebApp需要去克服的一些坑:这个小游戏的基本规则是:限定用户每天刮书次数是2次 (自由刮一次和分享后再刮一次),每天都可刮奖为此,我们希望实现的思路首先是限定在只能使用微信中玩,实现代码如下:if (!HttpCont