本站文章均为李华明Himi原创,转载务必在明显处注明:(作者新浪微博:@李华明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();
 
 
    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

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

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

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

    转自:http://www.himigame.com/iphone-cocos2dx/1354.html 首先说明一个问题: 为什么要在线更新资源和脚本文件!? 对于此问题,那要说的太多了,简单概括, ...

  2. cocos2d-x游戏引擎核心之十二——3.x新特性

    v3.0 亮点 使用 C++(C++11) 的特性取代了 Objective-C 的特性 优化了 Labels 优化了渲染器(比 v2.2 更快) 新的事件分发机制 物理引擎集成 新的 UI 对象 J ...

  3. Solon Web 开发,十二、统一的渲染控制

    Solon Web 开发 一.开始 二.开发知识准备 三.打包与运行 四.请求上下文 五.数据访问.事务与缓存应用 六.过滤器.处理.拦截器 七.视图模板与Mvc注解 八.校验.及定制与扩展 九.跨域 ...

  4. C和指针 (pointers on C)——第十二章:利用结构和指针

    第十二章 利用结构和指针 这章就是链表.先单链表,后双向链表. 总结: 单链表是一种使用指针来存储值的数据结构.链表中的每一个节点包括一个字段,用于指向链表的下一个节点. 有一个独立的根指针指向链表的 ...

  5. Django(十二)视图--利用jquery从后台发送ajax请求并处理、ajax登录案例

    一.Ajax基本概念 [参考]:https://www.runoob.com/jquery/jquery-ajax-intro.html 异步的javascript.在不全部加载某一个页面部的情况下, ...

  6. JavaWeb学习记录(二十二)——模式字符串与占位符

    一.Java代码案例 @Test    public void test10(){        int planet=7;        String event="a disturban ...

  7. Python开发【十二章】:ORM sqlalchemy

    一.对象映射关系(ORM) orm英文全称object relational mapping,就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却 ...

  8. uGUI使用代码动态添加Button.OnClick()事件(Unity3D开发之十二)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/42705885 ...

  9. 【Qt编程】基于Qt的词典开发系列<十二>调用讲述人

    我们知道,win7系统自带有讲述人,即可以机器读出当前内容,具体可以将电脑锁定,然后点击左下角的按钮即可.之前在用Matlab写扫雷游戏的时候,也曾经调用过讲述人来进行游戏的语音提示.具体的Matla ...

随机推荐

  1. 【转载】关于typedef的用法总结

    不管实在C还是C++代码中,typedef这个词都不少见,当然出现频率较高的还是在C代码中.typedef与#define有些相似,但更多的是不同,特别是在一些复杂的用法上,就完全不同了,看了网上一些 ...

  2. vs2010中臃肿的ipch和sdf文件

    使用VS2010建立C++解决方案时,会生成SolutionName.sdf和一个叫做ipch的文件夹,这两个文件再加上*.pch等文件使得工程变得非常的庞大,一个简单的程序都会占用几十M的硬盘容量, ...

  3. hashtable用法

    import java.util.Hashtable; public class HashTable { public static void main (String[] args) { Hasht ...

  4. PKU 1458 Common Subsequence(最长公共子序列,dp,简单)

    题目 同:ZJU 1733,HDU 1159 #include <stdio.h> #include <string.h> #include <algorithm> ...

  5. JsRender系列-11

    <!DOCTYPE html> <html> <head> <script type="text/javascript" src=&quo ...

  6. JavaScript 隐式转换

    javascript 中的怪癖,js运算符隐式类型转换 x + "" //等价于 String(x) + x //等价于 Number(x),也可以写成x-0 !!x //等价于 ...

  7. Android 监测手机联网状态 wifi、移动数据流量、无联网状态

    手机当完成联网时会发送一个广播,我们只要创建一个广播接收者即可,代码如下: package com.example.NetworkChangeReceiver2; import android.con ...

  8. QAQ高精度模板笔记√

    #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #i ...

  9. Apache与Tomcat整合

    Apache与Tomcat整合   一 Apache与Tomcat比较联系 apache支持静态页,tomcat支持动态的,比如servlet等. 一般使用apache+tomcat的话,apache ...

  10. 深入浅出Mybatis-分页

    http://blog.csdn.net/hupanfeng/article/details/9265341 http://blog.csdn.net/isea533/article/details/ ...