cocos creator protobuf实践
首先要说的是,我所使用的creator版本是:

在写这篇博客之前,我也曾做了很多搜索,也搜出一些有价值的东西,只不过很多东西都已经过时了,,或者说我们现在有更好的方式去实现我们的功能。
废话不多说,让我们直接开始吧。
1
新建一个Hello World项目,命名为CreatorTest。
先放一边。
2
前往protobufjs开源项目库,我们去下一个最新的release版本。为什么用它?自然是有人一直在更新维护呀,而且用起来还不错!下载地址

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

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

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

导入为插件后,我们就直接能使用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
目录如果没有就自己创建一个,如下图

修改我们的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
作者:不求甚解_4703
链接:https://www.jianshu.com/p/01223c16279a
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)