zoukankan      html  css  js  c++  java
  • 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会报错。

  • 相关阅读:
    『PyTorch』第四弹_通过LeNet初识pytorch神经网络_下
    『PyTorch』第三弹重置_Variable对象
    『PyTorch』第二弹重置_Tensor对象
    『PyTorch』第一弹_静动态图构建if逻辑对比
    『TensorFlow』DCGAN生成动漫人物头像_下
    『Python』pycharm常用设置
    『TensorFlow』通过代码理解gan网络_中
    『TensorFlow』TFR数据预处理探究以及框架搭建
    『Python』库安装
    『TensorFlow』读书笔记_TFRecord学习
  • 原文地址:https://www.cnblogs.com/zhong-dev/p/11045619.html
Copyright © 2011-2022 走看看