cocos creator protobuf实践

首先要说的是,我所使用的creator版本是:


 
creator 版本

在写这篇博客之前,我也曾做了很多搜索,也搜出一些有价值的东西,只不过很多东西都已经过时了,,或者说我们现在有更好的方式去实现我们的功能。

废话不多说,让我们直接开始吧。

1

新建一个Hello World项目,命名为CreatorTest。
先放一边。

2

前往protobufjs开源项目库,我们去下一个最新的release版本。为什么用它?自然是有人一直在更新维护呀,而且用起来还不错!下载地址

 
下载protobufjs

下载完成后,解压,打开它,我们找到这个文件protobuf.js
 
protobuf.js

 

3

打开creator,拖动protobuf.js到项目中,

 
拖动protobuf.js进来

 

也可以在导入的时候选择是否导入为插件,选就可以了。


这里说下为什么要导入为插件:creator在构建时候会将我们编辑器里所有的js脚本都打包到一个project.js的文件中,原生(native)的话就是project.jsc,如果我们的protobuf.js打包进去就会报错了,,所以这里需要导入为插件,这样做是为了避免错误
 
creator 构建的结果

导入为插件后,我们就直接能使用protobuf了。

4

我们把protobufjs官方例子抄过来,顺便做些改动。

创建我们的test1.proto文件:

package PbLobby;
syntax = "proto3";

enum Cmd {
    CMD_BEGIN = 0;
    CMD_KEEPALIVED_C2S      = 10000;   //心跳包测试
    CMD_LOGIN_C2S2C         = 10001;   // 登录
}

message Test1{
    int32 id = 1;//活动ID
    string name = 2;//名字
}

把它放到resources目录下,resources目录如果没有就自己创建一个,如下图

 
test1.proto文件位置

 

修改我们的HelloWorld.js,增加一个函数:

testProtobuf: function () {
        if (cc.sys.isNative) {//在native上加载失败,是因为没有找到目录,我们在testProtobuf函数里面添加一个搜索目录:
            cc.log("jsb.fileUtils=" + jsb.fileUtils);

            //下面这段代码在PC window平台运行没问题,但是在android下面就出问题了
            //jsb.fileUtils.addSearchPath("res\\raw-assets\\resources", true);
            //需要改成这样:
            jsb.fileUtils.addSearchPath("res/raw-assets/resources", true);//坑太多了。。没办法
        }

        var filename1 = "test1.proto";
        // cc.loader.loadRes(filename1, cc.TextAsset, function (error, result) {//指定加载文本资源
        //     cc.log("loadRes error=" + error + ",result = " + result + ",type=" + typeof result);
        //     // callback(null, result);
        // });

        var protobufHere = protobuf;//require("protobuf");//导入为插件,直接使用
        protobufHere.load(filename1, function (err, root) {//Data/PbLobby.proto
            if (err)
                throw err;

            cc.log("root=" + root);
            for (var i in root) {
                cc.log("root." + i + "=" + root[i]);
            }
            //return;

            cc.log("加载protobuf完毕,开始测试protobuf...")

            var cmd = root.lookupEnum("PbLobby.Cmd");
            cc.log(`cmd = ${JSON.stringify(cmd)}`);
            cc.log("CMD_KEEPALIVED_C2S = "+cmd.values.CMD_KEEPALIVED_C2S);

            //lookup 等价于 lookupTypeOrEnum 
            //不同的是 lookup找不到返回null,lookupTypeOrEnum找不到则是抛出异常
            var type1 = root.lookup("PbLobby.Cmd1");
            cc.log("type1 = "+type1);
            var type2 = root.lookup("PbLobby.Test1");
            cc.log("type2 = "+type2);

            // Obtain a message type
            var Test1Message = root.lookupType("PbLobby.Test1");
            cc.log("Test1Message = "+Test1Message);

            // Exemplary payload
            var payload = { id: 1,name:"hello protobuf" };
            //var payload = { ids: 1,name:"hello protobuf" };
            cc.log(`payload = ${JSON.stringify(payload)}`);

            //检查数据格式,测试了下发现没什么卵用
            // Verify the payload if necessary (i.e. when possibly incomplete or invalid)
            // var errMsg = Test1Message.verify(payload);
            // if (errMsg){
            //     cc.log("errMsg = "+errMsg);
            //     throw Error(errMsg);
            // }
                
            //过滤掉一些message中的不存在的字段
            // Create a new message
            var message = Test1Message.create(payload); // or use .fromObject if conversion is necessary
            cc.log(`message = ${JSON.stringify(message)}`);

            // Encode a message to an Uint8Array (browser) or Buffer (node)
            var buffer = Test1Message.encode(message).finish();
            cc.log("buffer1 = "+buffer);
            cc.log(`buffer2 = ${Array.prototype.toString.call(buffer)}`);
            // ... do something with buffer

            // Decode an Uint8Array (browser) or Buffer (node) to a message
            var decoded = Test1Message.decode(buffer);
            cc.log("decoded1 = "+decoded);
            cc.log(`decoded2 = ${JSON.stringify(decoded)}`);
            // ... do something with message

            // If the application uses length-delimited buffers, there is also encodeDelimited and decodeDelimited.

            //一般情况下,也不需要下面的转换
            // Maybe convert the message back to a plain object
            var object = Test1Message.toObject(decoded, {
                longs: String,
                enums: String,
                bytes: String,
                // see ConversionOptions
            });
            cc.log("object = "+JSON.stringify(object));
        });
    },

然后在onLoad函数添加代码this.testProtobuf();

 
调用测试函数

 

运行看一下,发现报错了,在这行代码
return callback(Error("status " + xhr.status));
protobuf默认是用XMLHttpRequest去请求文件的,所以我们接下去修改一下源码,这是必须的步骤!

5

修改一下protobuf.js的代码,搜索 function fetch(filename, options, callback),把函数改成这样:

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

                if (typeof cc !== "undefined") {//判断是否是cocos项目

                    if (cc.sys.isNative) {//native
                        var content = jsb.fileUtils.getStringFromFile(filename);
                        callback(content === "" ? Error(filename + " not exits") : null, content);
                    } else {
                        //cc.log("cc.loader load 1 filename=" + filename);
                        //这里可以加载一个url图片 : "Host"+filename
                        // cc.loader.load(filename, function (error, result) {
                        //     cc.log("error1=" + error + ",result = " + result + ",type=" + typeof result);
                        //     // callback(null, result);
                        // });
                        //cc.log("cc.loader load 2");

                        // 这里h5会去加载resources目录下的文件 : "resources/"+ filename
                        // 这里filename一般不用指定扩展名,当然你也可以强制指定
                        cc.loader.loadRes(filename, cc.TextAsset, function (error, result) {
                            //cc.log("error2=" + error + ",result = " + result + ",type=" + typeof result);
                            if (error) {
                                callback(Error("status " + error))
                            } else {
                                callback(null, result);
                            }
                        });
                        //cc.log("cc.loader load 3");
                    }

                    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)
                    return fs.readFile(filename, function fetchReadFileCallback(err, contents) {
                        return err && typeof XMLHttpRequest !== "undefined"
                            ? fetch.xhr(filename, options, callback)
                            : err
                                ? callback(err)
                                : callback(null, options.binary ? contents : contents.toString("utf8"));
                    });

                // use the XHR version otherwise.
                return fetch.xhr(filename, options, callback);
            }

6

点击下图按钮,运行看看


 
运行

我用的是默认chrome浏览器,然后打开Chrome开发者工具看调试信息,下面是调试信息的截图

 
调试信息

 

上图中,我们可以看到,我们已经成功动态加载了test1.proto文件。
buffer1和buffer2用了不同的方式打印,打印的结果完全一样,而这个buffer就是我们需要传递给服务器的数据。
后面的decoded数据,建议用第二种方式打印,这样可以直观的看到具体的数据信息。对比一下,decode出来的数据跟我们一开始创建的数据一致——都是

{"id":1,"name":"hello protobuf"}

7

到这里,我们已经成功的走完了使用proto的整个流程:

  1. 加载proto文件
  2. 创建message
  3. encode
  4. decode


作者:不求甚解_4703
链接:https://www.jianshu.com/p/01223c16279a
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

posted on 2018-12-21 18:12  &大飞  阅读(740)  评论(0编辑  收藏  举报

导航