Cocos2dx Lua 热更新_西溪漫步的博客-程序员秘密

技术标签: cocos2dlua 热更新  Cocos2d-x  

转载自:https://blog.csdn.net/u010693827/article/details/55047909

热更新的过程

首先,客户端向服务器发送请求,服务器告诉客户端,没更新啦,你是最新的啦,那就直接跳过喽。但如果是告诉你有更新,那就要告诉我哪些需要更新对吧,你可能需要更新的东西,放在一个文件里,一并发送给客户端,客户端拿到这个文件,就一个一个去向服务器要,最后把要更新的内容都下载到本地了。但是如果下载的资源之前已经存在,会不会出问题啊?
我们以win32平台为例

luagame4.png

这个是工程目录

而对于下载目录,一般是

C:\Users\user\AppData\Local\LuaGame4

但是这又有一个问题,平时我们调用资源都是直接调工程目录下啊,这会你下载到c盘里了,怎么能调用到?
这就涉及到一个优先级的问题了,比如一张图片它的路径是img/sample.png,在工程目录下的全路径就是
E/LuaGame4/res/img/sample.png ,那我们在代码里通常是这么调用的

  1. local sp = cc.Sprite:create("img/sample.png")

  2. self:addChild(sp)

回到之前的问题,现在要更新这张图片,上面说的是下载到

C:\Users\user\AppData\Local\LuaGame4

这个目录下,那我们要用同样的代码就能调用到新资源,只要将这个目录下的资源路径和我们工程下一致,然后将C:\Users\user\AppData\Local\LuaGame4 加入搜索路径,并且将它优先级设置最高,那么就可以调用到了。

具体实现

知道了它的大致工作过程,再来实际操作下,估计就会很清晰了。
在cocos2dx中,使用的是AssetsManagerEx这个类。而对于AssetsManager这个类是不推荐使用的,这个类有很多东西都没考虑到,我们就不深究了。
先上代码


local writablePath = cc.FileUtils:getInstance():getWritablePath()
local storagePath = writablePath .. "new_version"
--将下载目录的src和res作为优先级最高的搜索目录,这样才能保证下载的能覆盖原来的代码
cc.FileUtils:getInstance():addSearchPath(storagePath.."/src/",true)
    cc.FileUtils:getInstance():addSearchPath(storagePath.."/res/",true)
 
    -- 创建AssetsManagerEx对象
    local assetsManagerEx = cc.AssetsManagerEx:create("src/version/project.manifest", storagePath)
    assetsManagerEx:retain()
 
 
    -- 设置下载消息listener
    local function handleAssetsManagerEx(event)
        if (cc.EventAssetsManagerEx.EventCode.ALREADY_UP_TO_DATE == event:getEventCode()) then
            print("已经是最新版本了,进入游戏主界面")
            -- app:enterScene("GameScene")
        end
 
        if (cc.EventAssetsManagerEx.EventCode.NEW_VERSION_FOUND == event:getEventCode()) then
            print("发现新版本,开始升级")
        end
 
 
        if (cc.EventAssetsManagerEx.EventCode.UPDATE_PROGRESSION == event:getEventCode()) then
            print("更新进度=" .. event:getPercent())
        end
 
 
        if (cc.EventAssetsManagerEx.EventCode.UPDATE_FINISHED == event:getEventCode()) then
            print("更新完毕,重新启动")
            app:run()
        end
 
        if (cc.EventAssetsManagerEx.EventCode.ERROR_NO_LOCAL_MANIFEST == event:getEventCode()) then
            print("发生错误:本地找不到manifest文件")
        end
 
        if (cc.EventAssetsManagerEx.EventCode.ERROR_DOWNLOAD_MANIFEST == event:getEventCode()) then
            print("发生错误:下载manifest文件失败")
        end
 
        if (cc.EventAssetsManagerEx.EventCode.ERROR_PARSE_MANIFEST == event:getEventCode()) then
            print("发生错误:解析manifest文件失败")
        end
 
        if (cc.EventAssetsManagerEx.EventCode.ERROR_UPDATING == event:getEventCode()) then
            print("发生错误:更新失败")
        end
    end
 
 
    local dispatcher = cc.Director:getInstance():getEventDispatcher()
    local eventListenerAssetsManagerEx = cc.EventListenerAssetsManagerEx:create(assetsManagerEx, handleAssetsManagerEx)
    dispatcher:addEventListenerWithFixedPriority(eventListenerAssetsManagerEx, 1)
 
 
    -- 检查版本并升级
    assetsManagerEx:update()

先从
-- 创建AssetsManagerEx对象
local assetsManagerEx = cc.AssetsManagerEx:create("src/version/project.manifest", storagePath)

开始看,首先构建一个AssetsManagerEx:create对象,需要传入两个参数
  • project.manifest路径
  • 下载路径

这里我传入的project.manifest路径是src/version/project.manifest

manifest.png

这是个什么文件呢,打开看看

{
    "packageUrl" : "http://localhost:8080/examples/servlets/update/assets/",
    "remoteManifestUrl" : "http://localhost:8080/examples/servlets/update/version/project.manifest",
    "remoteVersionUrl" : "http://localhost:8080/examples/servlets/update/version//version.manifest",
    "version" : "1.0.0",
    "engineVersion" : "3.x dev",
 
    "assets" : {
        "res/blocks.png" : {
            "md5" : "...."
        }
    },
 
    "searchPaths" : [
    ]
}

这是一个json格式的文件,解释下每个key的意思

key 作用
packageUrl 更新包的url
remoteManifestUrl project.manifest的url
remoteVersionUrl 这个文件和project.manifest一个意思,但是比project.manifest更简洁,待会说
version 版本,是否需要更新就是看他了
engineVersion 引擎版本,写不写无所谓
assets 所有的文件名和他的md5值,在更新的时候会比对本地和远程的md5值,不一致则会更新,否则不更新

来看下version.manifest写了点啥

{
    "packageUrl" : "http://localhost:8080/examples/servlets/update/assets/",
    "remoteManifestUrl" : "http://localhost:8080/examples/servlets/update/version/project.manifest",
    "remoteVersionUrl" : "http://localhost:8080/examples/servlets/update/version//version.manifest",
    "version" : "1.0.2",
    "engineVersion" : "3.x dev",
}

卧槽,这不就是project.manifest的简化版吗?
卧槽,你怎么知道!
既然是简化版,那为什么要弄两个文件,不直接用更详细的project.manifest呢?

这是出于更新流量的考虑,我们在工程目录下会放一个关于整个项目资源的project.manifest的清单文件,在更新的时候,AssetsManagerEx会拿到其中的remoteVersionUrl,先将version.manifest下载下来,比对version,判断是否要更新,如果要更新,再下载较为详细的project.manifest文件,这样做的好处,就是当你的工程比较大的时候,对应的project.manifest也会比较大,如果每次都直接去下载project.manifest,那么就会造成不必要的让费了。

创建完AssetsManagerEx对象之后,要为它注册监听事件,方便我们对更新情况进行把握,比如进度,比如是否出错等等。对于对应的事件,在代码中已经写的很清楚了,这里就不再说了。

之后调用assetsManagerEx:update()开始更新。

完了。
啊?完了?
啊,完了啊
你妹啊

好吧,我知道你还是有点懵逼,这TM我这服务器怎么搞?我这本地要弄些啥啊。。莫得关系,往下看

需要准备的文件

  • 客户端
    对于客户端而言,就是一份project.manifest文件,它记录了所有资源一级代码的md5,方便在第一次更新的时候做比对,一旦有过一次更新之后,以后就不会用这个文件了,而是用下载目录下的project.manifest,不然的话更新了还是白更新
  • 服务器
    服务器上,需要三样东西project.manifest,version.manifest,以及更新包。

搞个简单的tomcat

热更新用的http协议,而写一个http后台,比较容易的方法就是搞个tomcat,不会的朋友可以看下怎么搭建,不算难,有点java基础的话,看两盘文章就知道怎么用了

tomcat.png

这是我tomcat的目录,我建了一个update文件夹,下面有两个子文件夹

  • version

version.png

  • assets

assets.png

文章中就放了一个资源的路径res/blocks.png,他的url就是packageUrl和res/blocks.png做拼接,也就是http://localhost:8080/examples/servlets/update/assets/res/blocks.png

这么一放,就有点感觉了吧?服务器上的目录和你工程目录完全一致,这样的话,只要把下载目录加入到搜索路径里,对于一样的相对路径,就能在下载目录中找到了。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/guominyou/article/details/105843668

智能推荐

使用 DurationFormatUtils 计算时间间隔_KimSoft的博客-程序员秘密

  // 当前时间  Date now = new Date();  // 2010上海世博会开幕时间  Date expo2010BeginDate = DateUtils.parseDate("2010-05-01 23:59:59", new String[] { "yyyy-MM-dd HH:mm:ss" });  // 2010上海世博会闭幕时间  Date expo2010En

opencv图像特效之毛玻璃_opencv毛玻璃效果_zjLOVEcyj的博客-程序员秘密

import cv2import numpy as npimport randomimg = cv2.imread("E:/code/conputer_visual/data/0.jpg", 1)cv2.imshow("pre", img)imginfo = img.shapeheight = imginfo[0]width = imginfo[1]dst = np.zeros((height, width, 3), np.uint8)mm = 4for m in range(0, he

BZOJ 3224: Tyvj 1728 普通平衡树 or 洛谷 P3369 【模板】普通平衡树-Splay树模板题_第八个猴子的博客-程序员秘密

3224: Tyvj 1728 普通平衡树Time Limit:10 SecMemory Limit:128 MBSubmit:22483Solved:10130[Submit][Status][Discuss]Description您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删...

javascript 代码_如何使您JavaScript代码简单易读_cumi6497的博客-程序员秘密

javascript 代码by Arthur Arakelyan 通过亚瑟·阿拉克利安(Arthur Arakelyan) 如何使您JavaScript代码简单易读 (How to keep your JavaScript code simple and easy to read)There are many ways to solve the same problem, but some ...

SwiftUI从入门到实战第2章第12节:Shape-Path_李发展的博客-程序员秘密

相关课程:http://hdjc8.com/hdjc/swiftUI/使用Path,并配合move和addLine绘制自定义的图形。本节课演示路径的绘制,通过Path路径可以绘制极为复杂的图形,我们在第八章使用路径绘制了漂亮的折线图表,但是本节只讲解基本路径的绘制。示例代码://首先添加一个路径,在闭包语句里进行路径形状的定义。Path { path in //将路径的起点移到此处的坐标位置,也就是水平坐标为30,垂直坐标为0的位置。 path.move(to: CGPoin

随便推点

Simulink永磁同步电机控制仿真系列六:使用电压电流模型的位置估计_深入浅出说电机的博客-程序员秘密

Simulink永磁同步电机控制仿真系列六:使用电压电流模型的位置估计引言1、电压电流模型简介1.1.αβ坐标系下的电压方程1.2. αβ坐标系下的磁链方程1.3. 磁链很重要2、建模实现2.1、理论与现实的差异2.2 、仿真验证而已2.3 、误差校正3、转子位置观测器3.1、搭建一个真正的观测器3.2、看看效果引言上一篇文章中提到了使用滑膜观测器通过估计反电动势实现转子位置解算,本质上,反电动势由转子磁链旋转而产生,直接观测转子磁链同样能够得到转子位置。本文将基于永磁同步电机的电压电流模型,直接对转子

eclipse导入多模块modules的maven项目_eclipse 怎么导入多模块项目_小百菜的博客-程序员秘密

1、在 eclipse 中检出项目右键 --》Import --》Git --》 Projects from Git --》 Clone URL --》 http://XXX.git --》Next --》Directory(选择代码存放位置)--》勾选Import as general project --》Finish2、转为Maven项目选中检出的项目右键 --》 ...

半数集问题_GuoZLH的博客-程序员秘密

问题描述:给定一个自然数n,由n 开始可以依次产生半数集set(n)中的数如下。(1) n∈set(n);(2) 在n 的左边加上一个自然数,但该自然数不能超过最近添加的数的一半;(3) 按此规则进行处理,直到不能再添加自然数为止。例如,set(6)={6,16,26,126,36,136}。半数集set(6)中有6 个元素。注意半数集是多重集。对于给定的自然数n,计算

input button 同在一行 水平对齐、缝隙问题整理_input和button放在同一行_橙橙鲁的博客-程序员秘密

问题1: input和button中间有缝隙<style>.search_input_bar .search_input{ border: 1px solid #DBDBDB; border-radius: 2px 0 0 2px; height: 30px; width: 220px; line-height: 30px; pad...

数据结构常见算法原理讲解100篇(一)-递归和分治算法原理及案例应用_普通网友的博客-程序员秘密

01.递归每谈到递归,我们总会免不了联系到斐波那契(Fibonacci)数列,当然也不可忽视,斐波那契数列确实是一个很好的例子。但在现实当中,我们只有在迫不得已的情况下才使用递归,因为递归本身的效率并不理想,但他的思想却值得我们留存在记忆之中。题目一:写一个函数,输入n,求斐波那契数列的第n项。我们先一起看一下该题目的递归实现,从而学会写递归的三要素://第一要素:明确你这个函数想要干什么//函数功能:计算斐波那契数列的第n项longlongFibonacci(unsi...

Tensorflow layers.fully_connected 参数(自用)_淇迹的博客-程序员秘密

  def fully_connected(inputs,                    num_outputs,                    activation_fn=nn.relu,                    normalizer_fn=None,                    normalizer_params=None,               ...

推荐文章

热门文章

相关标签