cocos creator protobuf实践

当creator遇上protobufjs—起步 ----------- https://www.jianshu.com/p/c4b8a8e3077f
使用pomelo例子 https://github.com/eddy2015/ccc-pomelo-chat-client

https://blog.csdn.net/6346289/article/details/76474537 – 不错文档

首先要说的是,我所使用的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”;

扫描二维码关注公众号,回复: 9647565 查看本文章

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的整个流程:

加载proto文件
创建message
encode
decode
欢迎来评论区交流~
2018-09-16
更新一下native的加载代码
callback(null, content);
改成下面
callback(content === “” ? Error(filename + " not exits") : null, content);

发布了205 篇原创文章 · 获赞 39 · 访问量 34万+

猜你喜欢

转载自blog.csdn.net/u013321328/article/details/101156705