Cocos Creator 使用 protobuf


  很早之前就聽說過protobuf,對此協議的評價也一直很高。但是之前接觸到的項目一直沒有使用這個協議,都是直接跟后端約定好數據包格式(協議號+數據包大小),然后將對應的數據結構體轉換為數據字節流進行數據的傳輸。最近接觸的一個新項目,使用到了protobuf協議,項目前端使用的是cocos2dx-lua,正好前段時間在用cocos creator進行項目開發,所以本着在開發中學習的想法,用cocos creator再簡單的將項目實現一邊,主要是加入protobuf協議。

  在搜索引擎中搜索 cocos creator + protobuf ,得到的資料都比較舊,而且我試着參考網上給到的資料,一直失敗,所以還是要根據creator、protobuf的版本來做一定的調整才行。

  本文中使用到的環境是:cocos creator 2.0.10版本,protobuf 6.8.6版本,下載地址

  1、

  下載好protobuf之后,導入到creator中,在提示是否導入為插件的時候選擇是,然后再將允許編輯器加載勾上,不然在編輯過程中一直會提示 protobuf 未定義之類的錯誤:

  2、

  將protobuf導入到編輯器之后,就可以參考官方的例子,來編寫代碼了,首先看一下官方給的proto文件:

// awesome.proto
package awesomepackage;
syntax = "proto3";

message AwesomeMessage {
    string awesome_field = 1; // becomes awesomeField
}

  具體的protobuf語法,可以回頭再去了解一下。這里有幾個關鍵信息后續會用到,包名(awesomepackage)、消息名稱(AwesomeMessage)。將proto文件放到resource目錄(這個目錄是creator規定用於加載動態導入資源的目錄),之前lua這邊是將.proto文件轉成了.pb文件(具體為什么要轉,.pb和.proto有什么不同,還沒了解清楚),所以一開始我也是直接去加載.pb文件,一直加載不了,后來才發現網上的教程都是加載的.proto文件。

  官方給的例子,加載方式是這樣的:

protobuf.load("awesome.proto", function(err, root) {
    if (err)
        throw err;

    // Obtain a message type
    var AwesomeMessage = root.lookupType("awesomepackage.AwesomeMessage");
}

  直接運行的話,會報錯,類似: return callback(Error("status " + xhr.status)); ,參考 這篇文章 ,修改protobuf.js,搜索 function fetch 修改一下:

function fetch(filename, options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = {};
    } else if (!options)
        options = {};

    if (!callback)
        return asPromise(fetch, this, filename, options); // eslint-disable-line no-invalid-this

    // 判斷是否是cocos項目
    if (typeof cc !== "undefined"){
        if (cc.sys.isNative){
            var content = jsb.fileUtils.getStringFromFile(filename)
            callback(content === "" ? Error(filename + " not exits") : null, content)
        }
        else{
            cc.loader.loadRes(filename, cc.TextAsset, function (error, result) {
                if (error) {
                    callback(Error("status " + error))
                } else {
                    callback(null, result);
                }
            })
        }
        return
    }
    // if a node-like filesystem is present, try it first but fall back to XHR if nothing is found.
    if (!options.xhr && fs && fs.readFile)
    ...
}

  主要就是那個判斷是否cocos環境的地方。

  我參考這個方式,試着加載了一下,root有值,但是按照這個例子,root.lookupType 返回的是null,后來參考論壇的教程,改了一下加載方式,直接使用cocos提供的加載資源的api加載:

cc.loader.loadRes(pbFiles, cc.TextAsset, function (err, protos) {
    if (err) {
        bg.Log.e("load proto error ==> ", err)
        return
    }
    let pr = protobuf.parse(tex)
    bg.Log.i(pr)
    let rs = pr.root.lookupType("awesomepackage.AwesomeMessage")
    bg.Log.i(rs)

    let payLoad = { awesome_field:"hello   sdfsefewg"}
    let msg = rs.create(payLoad)
    bg.Log.i("msg ", msg)
    let buf = rs.encode(msg).finish()
    bg.Log.i("buf ", buf)
    let decode = rs.decode(buf)
    bg.Log.i("decode ", JSON.stringify(decode))
})    

  使用這種方案,可以正確的加載到,打印出來的值都是正常的。

  如果有多個.proto文件,則可以用cc.loader.loadResArray批量加載,在加載結果里面,將protobuf.parse解析的結果,以文件名為key存儲起來:

for (let proto of protos) {
    window.protoMessageMap[proto._name] = protobuf.parse(proto)
}

  在多.proto文件下,發、收消息的時候,使用哪個協議,我這里使用的是一個笨方法:

// 發送消息
let protocal = "包名.messageName"
for (let k in protoMessageMap) {
    let obj = protoMessageMap[k]
    let found = obj.root.lookup(protocal)
    if (null != found) {
        let bodyMsg = found.create(data)
        let body = found.encode(bodyMsg).finish()   
break } }
// 接收消息 let protocal = "包名.messageName" for (let k in protoMessageMap) { let obj = protoMessageMap[k] let found = obj.root.lookup(protocal) if (null != found) { let msg = found.decode(pbmsg.body) break } }

  遍歷一邊所有加載進來的 .proto,然后再做后續操作。這個地方有個問題就是,如果消息名重復了,就會有問題。后期再看看有沒有什么解決方案吧。

  另外,creator用websocket進行消息通訊時,接收到的數據包用protobu解包,需要將數據包轉一下格式:

found.decode(new Uint8Array(event.data))

  不然protobuf會報錯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM