Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> cocos lua 實現熱更新

cocos lua 實現熱更新

編輯:關於Android編程

第一次搞熱更新,這裡記錄一下全過程,方面以後查看。

原理:每次登陸游戲利用cocos的assetManager從服務器拉去當前最新的兩個文件。 一個是version.mainifest,一個project.mainifest. 這兩個文件都是xml的描述文件。一個包含了版本信息,第二個包含了游戲所有資源的MD5碼。首先通過version文件對比本地的版本是否相同,如果不相同,再通過跟本地的project文件對比MD5碼來判斷哪些文件需要重新下載,替換資源。

步驟:

1.首先要進行測試先下載一個HFS服務器,這個服務器方便易學。每次將最新項目全部拖進服務器中,用GeneateManifest.py文件自動生成version 和 project文件。

2.編寫Geneate文件。

這個文件是一個python。目的是生成對應的version和project文件。project文件可以幫你給每個資源生成獨一無二的MD5碼,相當於每個資源的標記。下面是一段python文件的代碼,非常簡單。

#!/usr/bin/env python
#coding:utf-8
import os
import json
import hashlib
import subprocess


assetsDir = {
    "searchDir" : ["src", "res"],
    "ignorDir" : ["cocos", "obj","version"]
}

versionConfigFile = "res/config/version_info.json"  #版本信息的配置文件路徑

versionManifestPath = "res/version/version.manifest"    #由此腳本生成的version.manifest文件路徑

projectManifestPath = "res/version/project.manifest"    #由此腳本生成的project.manifest文件路徑


class SearchFile:
    def __init__(self):
        self.fileList = []

        for k in assetsDir:
            if (k == "searchDir"):
                for searchdire in assetsDir[k]:                 
                    self.recursiveDir(searchdire)

    def recursiveDir(self, srcPath):
        ''' 遞歸指定目錄下的所有文件'''
        dirList = []    #所有文件夾  

        files = os.listdir(srcPath) #返回指定目錄下的所有文件,及目錄(不含子目錄)

        for f in files:         
            #目錄的處理
            if (os.path.isdir(srcPath + '/' + f)):              
                if (f[0] == '.' or (f in assetsDir["ignorDir"])):
                    #排除隱藏文件夾和忽略的目錄
                    pass
                else:
                    #添加非需要的文件夾                                  
                    dirList.append(f)

            #文件的處理
            elif (os.path.isfile(srcPath + '/' + f)):               
                self.fileList.append(srcPath + '/' + f) #添加文件

        #遍歷所有子目錄,並遞歸
        for dire in dirList:        
            #遞歸目錄下的文件
            self.recursiveDir(srcPath + '/' + dire)

    def getAllFile(self):
        ''' get all file path'''
        return tuple(self.fileList)

def GetSvnCurrentVersion(): 
    popen = subprocess.Popen(['svn', 'info'], stdout = subprocess.PIPE)    
    while True:
        next_line = popen.stdout.readline()         
        if next_line == '' and popen.poll() != None:
            break

        valList = next_line.split(':')      
        if len(valList)<2:
            continue
        valList[0] = valList[0].strip().lstrip().rstrip(' ')
        valList[1] = valList[1].strip().lstrip().rstrip(' ')

        if(valList[0]=="Revision"):
            return valList[1]
    return ""


def CalcMD5(filepath):
    """generate a md5 code by a file path"""
    with open(filepath,'rb') as f:
        md5obj = hashlib.md5()
        md5obj.update(f.read())
        return md5obj.hexdigest()


def getVersionInfo():
    '''get version config data'''
    configFile = open(versionConfigFile,"r")
    json_data = json.load(configFile)
    configFile.close()
    json_data["version"] = json_data["version"] + '.' + str(GetSvnCurrentVersion())
    return json_data


def GenerateversionManifestPath():
    ''' 生成大版本的version.manifest'''
    json_str = json.dumps(getVersionInfo(), indent = 2)
    fo = open(versionManifestPath,"w")  
    fo.write(json_str)  
    fo.close()


def GenerateprojectManifestPath():
    searchfile = SearchFile()
    fileList = list(searchfile.getAllFile())
    project_str = {}
    project_str.update(getVersionInfo())
    dataDic = {}
    for f in fileList:      
        dataDic[f] = {"md5" : CalcMD5(f)}

    project_str.update({"assets":dataDic})
    json_str = json.dumps(project_str, sort_keys = True, indent = 2)

    fo = open(projectManifestPath,"w")  
    fo.write(json_str)  
    fo.close()


if __name__ == "__main__":
    GenerateversionManifestPath()
    GenerateprojectManifestPath()

3.利用cocos assetManager來從服務器獲取文件並且進行資源的替換(這裡所謂的替換並不是真正的替換,利用了Fileutils->searchPath()設置資源文件讀取的優先級。也就是老資源和代碼並沒有刪除,而是捨棄不用。)

--region *.lua
--Date
--此文件由[BabeLua]插件自動生成

local AssetsManager = class("AssetsManager",function ()
    return cc.LayerColor:create(cc.c4b(20, 20, 20, 220))
end)

function AssetsManager:ctor()
    self:onNodeEvent("exit", handler(self, self.onExitCallback))
    self:initUI()
    self:setAssetsManage()
end

function AssetsManager:onExitCallback()
    self.assetsManagerEx:release()
end

function AssetsManager:initUI()

    local hintLabel = cc.Label:createWithTTF("正在更新...", CONFIG.TTF_FONT_2, 20)
        :addTo(self)
        :move(600, 80)

    local progressBg = display.newSprite("sprites/hyd_progress_bg.png")    
        :addTo(self)
        :move(600, 40)

    self.progress = cc.ProgressTimer:create(display.newSprite("sprites/hyd_progress.png"))
        :addTo(progressBg)
        :move(380, 19)
    self.progress:setType(cc.PROGRESS_TIMER_TYPE_BAR)
    self.progress:setBarChangeRate(cc.p(1, 0))
    self.progress:setMidpoint(cc.p(0.0, 0.5))
    self.progress:setPercentage(0) 

    --觸摸吞噬
    self.listener = cc.EventListenerTouchOneByOne:create()
    self.listener:setSwallowTouches(true)
    local onTouchBegan = function (touch, event)
        return true
    end

    self.listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)
    cc.Director:getInstance():getEventDispatcher():addEventListenerWithSceneGraphPriority(self.listener, self)   
end

function AssetsManager:setAssetsManage()
    --創建可寫目錄與設置搜索路徑
    local storagePath = cc.FileUtils:getInstance():getWritablePath() .. "NewRes/" 
    local resPath = storagePath.. '/res/'
    local srcPath = storagePath.. '/src/'
    if not (cc.FileUtils:getInstance():isDirectoryExist(storagePath)) then         
        cc.FileUtils:getInstance():createDirectory(storagePath)
        cc.FileUtils:getInstance():createDirectory(resPath)
        cc.FileUtils:getInstance():createDirectory(srcPath)
    end
    local searchPaths = cc.FileUtils:getInstance():getSearchPaths() 
    table.insert(searchPaths, 1, storagePath)  
    table.insert(searchPaths, 2, resPath)
    table.insert(searchPaths, 3, srcPath)
    cc.FileUtils:getInstance():setSearchPaths(searchPaths)

    self.assetsManagerEx = cc.AssetsManagerEx:create("version/project.manifest", storagePath)    
    self.assetsManagerEx:retain()

    local eventListenerAssetsManagerEx = cc.EventListenerAssetsManagerEx:create(self.assetsManagerEx, 
       function (event)
           self:handleAssetsManagerEvent(event)
       end)

    local dispatcher = cc.Director:getInstance():getEventDispatcher()
    dispatcher:addEventListenerWithFixedPriority(eventListenerAssetsManagerEx, 1)

    --檢查版本並升級
    self.assetsManagerEx:update()
end

function AssetsManager:handleAssetsManagerEvent(event)    
    local eventCodeList = cc.EventAssetsManagerEx.EventCode    

    local eventCodeHand = {

        [eventCodeList.ERROR_NO_LOCAL_MANIFEST] = function ()
            print("發生錯誤:本地資源清單文件未找到")
        end,

        [eventCodeList.ERROR_DOWNLOAD_MANIFEST] = function ()
            print("發生錯誤:遠程資源清單文件下載失敗")  --資源服務器沒有打開,
            self:downloadManifestError()
        end,

        [eventCodeList.ERROR_PARSE_MANIFEST] = function ()
             print("發生錯誤:資源清單文件解析失敗")
        end,

        [eventCodeList.NEW_VERSION_FOUND] = function ()
            print("發現找到新版本")
        end,

        [eventCodeList.ALREADY_UP_TO_DATE] = function ()
            print("已經更新到服務器最新版本")            
            self:updateFinished()
        end,

        [eventCodeList.UPDATE_PROGRESSION]= function ()
            print("更新過程的進度事件")
            self.progress:setPercentage(event:getPercentByFile())
        end,

        [eventCodeList.ASSET_UPDATED] = function ()
            print("單個資源被更新事件")
        end,

        [eventCodeList.ERROR_UPDATING] = function ()
            print("發生錯誤:更新過程中遇到錯誤")
        end,

        [eventCodeList.UPDATE_FINISHED] = function ()
            print("更新成功事件")
            self:updateFinished()
        end,

        [eventCodeList.UPDATE_FAILED] = function ()
            print("更新失敗事件")
        end,

        [eventCodeList.ERROR_DECOMPRESS] = function ()
            print("解壓縮失敗")
        end
    }
    local eventCode = event:getEventCode()    
    if eventCodeHand[eventCode] ~= nil then
        eventCodeHand[eventCode]()
    end  
end

function AssetsManager:updateFinished()
    self:setVisible(false)
    self.listener:setEnabled(false)
end

function AssetsManager:downloadManifestError()
    self:setVisible(false)
    self.listener:setEnabled(false)
end

return AssetsManager


--endregion

4.遇到的問題: 測試的時候服務器設置的IP輸出總是不對,跟自己設置的IP有出入!原因是自己只設置了本地的IP地址,但是服務器的IP地址沒有更改。 按照正常邏輯,按照本地提供的IP地址訪問服務器成功並且下載了version和project文件。 發現需要跟新的時候,這個時候是按照下載下來的project提供的IP地址去下載資源。由於服務器projec的IP根本都是錯誤的。 會導致資源下載失敗!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved