利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)

时间:2022-08-28 18:55:03

转自: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 中添加如下代码:

先导入所需的头文件:

#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

继续添加变量和方法名:

void updateFiles();
void createDownDir();
string pathToSave;

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

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

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(, 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(), );
}
#endif }

首先介绍createDwomDir函数:

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

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

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

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

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

继续介绍  updateFiles 函数:

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

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

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

. 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函数修改:

virtual void update();

修改成如下:

virtual bool update();

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

bool AssetsManager::update()
{
// 1. Urls of package and version should be valid;
// 2. Package should be a zip file.
if (_versionFileUrl.size() == ||
_packageUrl.size() == ||
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()) != )
{
CCLOG("can not remove downloaded zip file");
}
return true;
}

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

最后我们来看如下代码:

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(, 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文件完整的路径传入,如下:

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

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

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

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 / ); // 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文件!

运行截图如下:

利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)

从如上的运行截图中可以看出,首先我们得到服务器传来的版本号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引擎的示例项目!

附:http://blog.csdn.net/sonikk/article/details/8871403

源码相关文件路径:

C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\extensions\cocos-ext.h

C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\extensions\AssetsManager\AssetsManager.h

C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\extensions\AssetsManager\AssetsManager.cpp

引用方法:

#include "cocos-ext.h"

#include "AssetsManager.h"

using namespace cocos2d::extension;

Additional Include Directories:

C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\extensions

C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\extensions\AssetsManager

Additional Library Directories:

C:\dev\cocos2d-2.1rc0-x-2.1.2-hotfix\Debug.win32

Additinal Dependencies:

libcurl_imp.lib

libExtensions.lib

该AssetsManager的基本流程:

1. 配置需要更新的zip的URL,更新版本号的URL,更新存放的相对路径

2. 从Server获取该zip文件的版本号

3. 对比Client中的UserDefault.xml中current-version-code键的值(当前版本号)是否过期

4. 若Server的版本比Client的新,则通过http请求下载该zip

5. 解压缩该zip文件

6. 下载后通过CCFileUtils的fullPathForFilename方法来获取文件的引用

下载流程:

update():

1. 配置的zip的URL和version的URL必须合法,且非空

2. 检验Server是否存在新版本

3. 读取UserDefault.xml的downloaded-version-code,对比当前版本号,若不相等则进行zip包下载

4. 若下载完成,记录最新的版本号于UserDefault.xml的downloaded-version-code中,并flush刷新

5. 解压缩zip包

6. 若解压成功,记录最新的版本号于UserDefault.xml的current-version-code中,并把downloaded-version-code删除,并flush刷新

7. 设置搜索路径,(先获取搜索路径vector,然后将新的搜索路径插入到该vector中,将该vector重新放入CCFileUtils中)

8. 删除未加载的cocos2dx-update-temp-package.zip文件

利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)的更多相关文章

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

    本站文章均为李华明Himi原创,转载务必在明显处注明:(作者新浪微博:@李华明Himi) 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/iphone-c ...

  2. Spring MVC程序中得到静态资源文件css&comma;js&comma;图片

    转载自:http://www.blogjava.net/fiele/archive/2014/08/24/417283.html 用 Spring MVC 开发应用程序,对于初学者有一个很头疼的问题, ...

  3. Spring MVC程序中得到静态资源文件css&comma;js&comma;图片文件的路径问题总结

    上一篇 | 下一篇 Spring MVC程序中得到静态资源文件css,js,图片 文件的路径 问题总结 作者:轻舞肥羊 日期:2012-11-26 http://www.blogjava.net/fi ...

  4. 利用bat批量执行脚本文件

    1.读取目录文件 利用bat 的for命令读取中的sql文件 for /r %%c in (0*.sql) do echo %%c %%c 相当于变量 in() 中的为循环的范围 此句的作用是显示当前 ...

  5. Spring MVC程序中怎么得到静态资源文件css&comma;js&comma;图片文件的路径问题

    问题描述 在用springmvc开发应用程序的时候.对于像我一样的初学者,而且还是自学的人,有一个很头疼的问题.那就是数据都已经查出来了,但是页面的样式仍然十分简陋,加载不了css.js,图片等资源文 ...

  6. 利用ROS工具从bag文件中提取图片

    bag文件是ROS常用的数据存储格式,因此要从bag文件中提取数据就需要了解一点ROS的背景知识. 1. 什么是ROS及其优势 ROS全称Robot Operating System,是BSD-lic ...

  7. Web项目中用模板Jsp页面引入所有静态样式脚本文件(js&comma;css等)

    这样的好处是不需要再每个页面中都添加太多的外链接(不会减少请求数量),但对开发会更快捷,如果更改这些文件的位置或名称,只需要更改模板文件,不需要一个一个页面复制粘贴:同时可以为不同jsp页面组创建不同 ...

  8. H5利用formData来上传文件(包括图片,doc&comma;pdf等各种格式)方法小结!

    H5页面中我们常需要进行文件上传,那么怎么来实现这个功能呢??? 我主要谈如下两种方法. (一).传统的form表单方法 <form action="/Home/SaveFile1&q ...

  9. JS脚本文件的位置对页面加载性能影响以及无阻塞脚本(javascript)模式

    JS的阻塞特性:当<script>出现的时候,页面必须等待脚本文件的加载.解析.执行完毕后才能继续进行页面的渲染.不管脚本文件是以内联形式还是外部引入的形式出现在<script&gt ...

随机推荐

  1. java之Date

    1.java时间处理 package com.bmkit.util.date; import java.text.DateFormat; import java.text.ParseException ...

  2. 五个有用的jquery小技巧

    1.禁用鼠标右键 $(document).ready(function() { $(document).bind("contextmenu", function(e) { retu ...

  3. HTTP权威指南一

    HTTP——因 特网的多媒体信使 每天, 都有数以亿万计的 JPEG 图片. HTML 页面. 文本文件. MPEG 电影. WAV音频文件. Java 小程序和其他资源在因 特网 上游弋. HTTP ...

  4. &period;net System&period;TypeInitializationException 类型初始值设定项引发异常

    错误原因:静态字段连接数据库内容错误(应为Integrated Security=True) 果然解决! 来自:http://blog.csdn.net/zhang1597116/article/de ...

  5. 使用Ant自动化发布web工程

    通常在web应用程序需要上线或测试时通常需要部署到类似于tomcat.jboss.weblogic或webspare这些web服务器中,为避免手动部署带来的操作繁琐.易出错等问题,这里使用ant进行标 ...

  6. Directed Roads

    Directed Roads 题目链接:http://codeforces.com/contest/711/problem/D dfs 刚开始的时候想歪了,以为同一个连通区域会有多个环,实际上每个点的 ...

  7. JAVA中的设计模式一(单例模式)

    单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个 ...

  8. BZOJ2212或洛谷3521 &lbrack;POI2011&rsqb;ROT-Tree Rotations

    BZOJ原题链接 洛谷原题链接 线段树合并裸题. 因为交换子树只会对子树内部的逆序对产生影响,所以我们计算交换前的逆序对个数和交换后的个数,取\(\min\)即可. 对每个叶子节点建一棵动态开点线段树 ...

  9. steam pipeGUI

    SteamPipe GUI 工具 如果您运行的是 Windows,并且更喜欢使用 GUI 工具来帮助生成配置文件并上传您的生成版本,您可以使用 Steamworks SDK 的工具文件夹中的 Stea ...

  10. 智行火车票免费加速到VIP最高速抢票(不用朋友积攒或者购买加速包)

    更新: 2018.11.07, 昨天我买火车票,已经不行了,这个bug已经没有了,被修复了, 望大家知悉!!! 智行火车票免费加速到VIP最高速抢票(不用朋友积攒或者购买加速包) 1)下过单后选择抢到 ...