Cocos遊戲設計心得(五)

遊戲索引模塊設計

之前寫了關於modules模塊中的一些模塊代碼,但是最終感覺還是太麻煩了,而卻一部分代碼段,估計你們也是看的雲裏霧裏,所以呢我就不去再貼出代碼段了,在這裏我只列出一些必要的模塊,僅供參考:

 

**`font` 遊戲字體模塊,管理遊戲中使用的字體。**
**`script` 遊戲腳本管理模塊。**
**`model` 遊戲模型管理模塊。**
**`ui` 遊戲UI管理模塊。**
**`uiwidget` 遊戲UI組件管理模塊。**
**`variable` 遊戲變量管理模塊,針對遊戲字符串中的變量。**
**`archive` 遊戲存檔模塊,針對單機或聯網遊戲。**
**`behavior3` 遊戲行爲樹模塊。**
**`effect` 遊戲特效模塊。**
**`map` 遊戲地圖管理模塊。**
**`play` 遊戲劇本管理模塊,主要針對單機RPG遊戲。**
**`control` 遊戲控制模塊,特別針對遊戲控制器,按鍵等。**
**`audio` 遊戲音頻模塊,管理遊戲聲音。**
**`formula` 遊戲公式模塊,針對策劃可以在數據庫配置遊戲的公式,如傷害計算等。**
**`scene` 遊戲場景管理模塊。**

模塊設計目的

由於前面我們在遊戲中使用分包設計,所以傳統編碼方式訪問其他包中的文件是比較麻煩的事,而且還涉及到各個分包加載與否,或者加載先後的問題。這樣一來不就很是不舒爽了嗎?
所以索引模塊的設計就是爲了解決分包文件訪問問題,力求在編碼時和傳統編碼一樣,根本不用考慮分包的問題。

索引模塊的本質

索引模塊有的同學可能問他是不是像鍵值對一樣存在一張表中呢,用到時直接下表訪問那種呢?嗯。。。可以這麼去理解,但是也不完全是這樣的。
索引模塊是爲了解決包文件共享而設計的,但是它並不是文件管理器,或者資源管理器。索引模塊僅僅只是一張表,表中的各個節點記錄着各種數據
**當我們要訪問某一類資源時只要在索引模塊中查找就可以了。這樣一來的確是省了很多的力氣活,爲我們後期的編碼開發提供了一種規範。 **

包文件共享共享的是什麼? 一般我們要共享的不是單個文件,而是一類配置,比如某個包中的怪物需要共享,那麼這些怪的配置、資源、程序就是索引表中的數據。

索引模塊如何工作

每個遊戲包都可以在它的資源目錄中定義索引加載表,以加載當前包的共享配置數據等。資源索引加載表路徑爲Game/<遊戲包>/res/indexes.json,這是一個JSON文件裏面包含了所有加載表,如;
`
{
** "src": "$(respath)/main/indexes/src.json",**
** "res": "$(respath)/main/indexes/res.json",**
** "db_auto": "$(respath)/main/indexes/db_auto.json",**
** "prot_auto": "$(respath)/main/indexes/prot_auto.json"**
}
`
*_auto 以_auto結尾的鍵一般爲遊戲腳本工具生成的,不要去修改它。比如db_auto表示腳本工具生成的數據庫索引文件,prot_auto表示腳本工具生成的協議索引文件。
$(respath)這個是索引模塊的一個環境變量,可以自定義。

具體的src.jsonres.jsondb_auto.jsonprot_auto.json其實就是普通的JSON文件,當然也是可以使用環境變量的。

**索引模塊會把加載表中的索引JSON合併爲一個索引表,當然只要有包文件加載就會把包文件中的索引配置讀取出來合併爲一個表。 **
這樣當我們需要加載一類遊戲配置的時候只要知道索引表中的節點路徑就可以了,當然索引表需要仔細設計,他是很重要的。

索引模塊重要函數

readJson(jsonfile) 讀取JSON文件,遊戲中讀取JSON最好都使用這個函數。因爲遊戲包在打包階段JSON文件可能會有優化,比如直接轉換爲lua文件,或者luac字節碼文件,這對JSON加載速度可能會提升 10 ~ 50 倍。
writeJson(jdata,jsonfile) 寫入JSON文件,這和普通的寫入沒有區別,但是注意,在配置JSON優化的時候,需要避開這些可能會寫入的問題,否則可能會出問題。
readJsonConfig(jsonfile)讀取JSON配置文件,它在讀取了JSON後,還會轉換JSON配置中的環境變量。目前這類文件不能使用JSON優化,主要是出於轉換環境變量的效率考慮。目前還不知道優劣。
getIndex(path)獲得指定路徑的索引配置。
addListener(listener, ipaths, priority)這個可以監聽索引模塊索引變化情況,對於依賴於索引模塊的其他模塊,監聽是很重要的。比如包文件加載和釋放都會存在索引表變動,需要及時更新其他對應模塊。

索引模塊很重要

索引模塊應該算是遊戲框架中很重要的模塊,如果沒有這個模塊,其他很多模塊都會變得很難使用。

最後還是想了一下將索引代碼放一些在上面,供大家參考使用

代碼

 

**--[[**
**    索引管理器**
**--]]**

**local THIS_MODULE = ...**
**local C_LOGTAG = "IndexManager"**

**local cjson = cc.safe_require("cjson")**
**local IndexManager = class("IndexManager")**

**-- 獲得單例對象**
**local instance = nil**
**function IndexManager:getInstance()**
**    if instance == nil then**
**        instance = IndexManager:create()**
**    end**
**    return instance**
**end**

**-- 構造函數**
**function IndexManager:ctor()**
**    self._envs          = {}            -- 環境變量**
**    self._indexnodes    = {}            -- 索引節點**
**    self._indexes       = {}            -- 索引表**
**    self._listen        = {             -- 監聽**
**        remove = { entities = {} },     -- 移除配置**
**        loads = {},                     -- 加載配置**
**    }**
**end**

**----------------------------------------------------------**
**--  環境變量**

**-- 設置環境變量**
**function IndexManager:setEnv(key, value)**
**    local fkey = "$(" .. key .. ")"**
**    if self._envs[fkey] then**
**        local msg = string.format("index env %s conflict", key)**
**        if ERROR.INDEX_ENV_CONFLICT then**
**            error(msg)**
**        else**
**            logMgr:warn(C_LOGTAG, "%s", msg)**
**        end**
**    end**
**    self._envs[fkey] = value**
**end**

**-- 設置環境配置**
**function IndexManager:setEnvConfig(envconf)**
**    if envconf then**
**        for key,value in pairs(envconf) do**
**            self:setEnv(key, value)**
**        end**
**    end**
**end**

**-- 獲得環境變量**
**function IndexManager:getEnv(key)**
**    return self._envs["$(" .. key .. ")"]**
**end**

**-- 清除環境變量**
**function IndexManager:clearEnvs()**
**    self._envs = {}**
**end**

**----------------------------------------------------------**
**--  json配置讀寫**

**-- 讀取json文件**
**function IndexManager:readJson(jsonfile)**
**    local jsonpath = fileUtils:fullPathForFilename(jsonfile)**
**    if fileUtils:isFileExist(jsonpath) then**
**        return cjson.decode(fileUtils:getDataFromFile(jsonpath))**
**    end**
**end**

**-- 寫入json文件**
**function IndexManager:writeJson(jdata,jsonfile)**
**    if jdata and jsonfile then**
**        fileUtils:writeStringToFile(cjson.encode(jdata), jsonfile)**
**    end**
**end**

**-- 讀取json配置文件,環境變量替換**
**function IndexManager:readJsonConfig(jsonfile)**
**    logMgr:verbose(C_LOGTAG, "read config json : %s", jsonfile)**
**    local jsonpath = fileUtils:fullPathForFilename(jsonfile)**
**    if fileUtils:isFileExist(jsonpath) then**
**        local newenvs = { **
**            ["$(curpath)"] = io.pathinfo(jsonpath).dirname **
**        }**
**        table.merge(newenvs, self._envs)**

**        local jsondata = fileUtils:getDataFromFile(jsonpath)**

**        jsondata = jsondata:gsub("%$%(%w+%)", newenvs)**

**        return cjson.decode(jsondata)**
**    end**
**end**

**----------------------------------------------------------**
**--  索引操作**

**-- 合併表**
**function IndexManager:mergeTalbe(jdest, jsrc, jpath, listen, jsonpath)**
**    jpath = jpath or ""**
**    for name,value in pairs(jsrc) do**
**        if type(name) == "number" then**
**            name = #jdest + 1**
**        end**
**        local ijpath = (jpath ~= "") and (jpath .. "/" .. name) or name**

**        if type(value) == "table" then**
**            local itable = jdest[name]**
**            if not itable then**
**                itable = {}**
**                jdest[name] = itable**
**            end**
**            self:mergeTalbe(itable, value, ijpath, listen, jsonpath)**
**        else**
**            if jdest[name] then**
**                local msg = string.format("index path %s conflict : %s", ijpath, jsonpath or "[unknown]")**
**                if ERROR.INDEX_PATH_CONFLICT then**
**                    error(msg)**
**                else**
**                    logMgr:warn(C_LOGTAG, "%s", msg)**
**                end**
**            end**
**            jdest[name] = value**
**        end**

**        if listen then self:notifyIndexesLoaded(ijpath, value) end**
**    end**
**end**

**-- 重構索引**
**function IndexManager:rebuildIndexes()**
**    self._indexes = {}**
**    for node,index in pairs(self._indexnodes) do**
**        self:mergeTalbe(self._indexes, index)**
**    end**
**end**

**-- 獲得指定索引**
**function IndexManager:getIndex(path)**
**    local value = self._indexes**
**    for _,pnode in ipairs(path:split('/')) do**
**        if not value then **
**            return nil**
**        end**
**        value = value[pnode]**
**    end**
**    return value**
**end**

**-- 加載索引文件**
**function IndexManager:loadIndexFile(file, node)**
**    node = node or file**
**    local newindex = self:readJsonConfig(file)**

**    local nodeindex = self._indexnodes[node]**
**    if not nodeindex then**
**        self._indexnodes[node] = newindex**
**    else**
**        self:mergeTalbe(nodeindex, newindex)**
**    end**

**    self:mergeTalbe(self._indexes, newindex, "", true, file)**
**end**

**-- 移除索引節點**
**function IndexManager:removeIndexNode(node)**
**    if self._indexnodes[node] then**
**        self._indexnodes[node] = nil**
**        self:rebuildIndexes()**
**        self:notifyIndexesRemoved()**
**    end**
**end**

**-- 加載索引文件配置**
**function IndexManager:loadIndexFileConfig(confile, node)**
**    local indexconf = self:readJsonConfig(confile)**
**    if indexconf then**
**        for _,indexfile in pairs(indexconf) do **
**            self:loadIndexFile(indexfile, node or confile)**
**        end**
**    end**
**end**

**-- 清除全部索引**
**function IndexManager:clearIndexes()**
**    self._indexnodes    = {}**
**    self._indexes       = {}**
**    self:notifyIndexesRemoved()**
**end**

**----------------------------------------------------------**
**--  索引監聽**

**-- 添加監聽器**
**function IndexManager:addListener(listener, ipaths, priority)**
**    self:removeListener(listener)**

**    local lentity = {**
**        listener = listener,**
**        priority = priority or 0,**
**    }**

**    -- 更新移除監聽器**
**    self._listen.remove.entities[listener] = lentity**
**    self._listen.remove.queue = table.values(self._listen.remove.entities)**
**    table.sort(self._listen.remove.queue, function (a, b)**
**        return a.priority < b.priority**
**    end)**

**    -- 更新加載路徑監聽器**
**    if ipaths then**
**        for _,ipath in ipairs(ipaths) do**
**            if ipath:byte(#ipath) == 47 then    -- /**
**                ipath = ipath:sub(1,-2)**
**            end**
**            local loadconf = self._listen.loads[ipath]**
**            if not loadconf then**
**                loadconf = { entities = {} }**
**                self._listen.loads[ipath] = loadconf**
**            end**
**            loadconf.entities[listener] = lentity**
**            loadconf.queue = table.values(loadconf.entities)**
**            table.sort(loadconf.queue, function (a, b)**
**                return a.priority > b.priority**
**            end)**
**        end**
**    end**
**end**

**-- 移除監聽器**
**function IndexManager:removeListener(listener)**
**    if self._listen.remove.entities[listener] then**
**        -- 移除移除表**
**        self._listen.remove.entities[listener] = nil**
**        self._listen.remove.queue = table.values(self._listen.remove.entities)**
**        table.sort(self._listen.remove.queue, function (a, b)**
**            return a.priority < b.priority**
**        end)**

**        -- 移除路徑表**
**        for _,loadconf in pairs(self._listen.loads) do**
**            if loadconf.entities[listener] then**
**                loadconf.entities[listener] = nil**
**                loadconf.queue = table.values(loadconf.entities)**
**                table.sort(loadconf.queue, function (a, b)**
**                    return a.priority > b.priority**
**                end)**
**            end**
**        end**
**    end**
**end**

**-- 清空監聽器**
**function IndexManager:clearListeners()**
**    self._listen = { **
**        remove = { entities = {} },**
**        loads = {},**
**    }   **
**end**

**-- 通知索引加載**
**function IndexManager:notifyIndexesLoaded(ipath, ivalue)**
**    local loadconf = self._listen.loads[ipath]**
**    if loadconf then**
**        for _,lentity in ipairs(loadconf.queue or {}) do**
**            lentity.listener:onIndexesLoaded(ipath, ivalue)**
**        end**
**    end**
**end**

**-- 通知索引移除**
**function IndexManager:notifyIndexesRemoved()**
**    for _,lentity in ipairs(self._listen.remove.queue or {}) do**
**        lentity.listener:onIndexesRemoved()**
**    end**
**end**

**return IndexManager**

(未完待續)
留下個人博客以供學習使用:taoqy666.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章