Cocos Creator 集成protobuf

  为了在CocosCreator 中使用 protobuf,NRatel走了不少弯路。

  protobufjs开源地址

  一、安装 

  前置条件,安装Node.js 、npm。

  查看候选版本:

npm view protobufjs versions
 
 

  新建一个项目目录,用来转换.proto为.js。执行 npm init -y 初始化项目。

  选择需要的版本安装,(这里用的是6.8.8版):

npm install --save-dev protobufjs@6.8.8
 
 

  执行后,将出现node_modules目录,

  要执行的转换命令文件为:node_modules\.bin\pbjs.cmd, 内容如下:


 
 
  1. ::当前目录是否存在node.exe
  2. @IF EXIST "%~dp0\node.exe" (
  3. ::使用node执行pbjs进行文件转换
  4. "%~dp0\node.exe" "%~dp0\..\protobufjs\bin\pbjs" %*
  5. ) ELSE (
  6. @SETLOCAL
  7. ::将环境变量PATHEXT中的JS删除
  8. @SET PATHEXT=%PATHEXT:;.JS;=;%
  9. ::使用node执行pbjs进行文件转换
  10. node "%~dp0\..\protobufjs\bin\pbjs" %*
  11. )

  实际上它最终是,用 node执行了 node_modules\protobufjs\bin\pbjs文件。

  二、使用

  protobufjs 提供了多种使用方式,但通常主要还是采用 生成静态.js使用和动态加载.proto文件使用这两种方式。

  注意, protobufjs 依赖了  long.jsbytebuffer.js。放入工程即可。

  1.静态方式(推荐!)

  1). 执行命令获取帮助,确认参数含义和用法。避免版本改变导致用法改变导致的错误:

node_modules\.bin\pbjs -h
 
 

  2). 执行命令进行 .proto文件到.js的转换操作:

  注意,要执行的文件从上层目录开始执行时,在windows下为反斜杠间隔。

  这里的版本中的 -t 指定目标格式;-w 指定模块引用规范;-o指定输入输出文件。具体以pbjs -h中的说明为准。

node_modules\.bin\pbjs -t static-module -w commonjs -o protores.js *.proto
 
 

  3). 示例:

  .proto源文件。


 
 
  1. // 日志
  2. package log;
  3. // 提交评论
  4. message comment_C {
  5. optional string msg = 1; // 评论内容
  6. }

  可能需要修改protores.js文件顶部 对protibuf.js的引用(修改引用路径,改为自己使用的版本)。


 
 
  1. //var $protobuf = require("protobufjs/minimal");
  2. var $protobuf = require( "protobuf");

  将使用方法封装为更通用、更易用的方式。


 
 
  1. let protores = require( "protores");
  2. let Pbjs6 = class Pbjs6 {
  3. //packageName: package名
  4. //msgTypeName: 消息类型名
  5. static Encode(packageName, msgTypeName, data) {
  6. var msgType = protores[packageName][msgTypeName];
  7. var msg = msgType.create(data);
  8. var bytes = msgType.encode(msg).finish();
  9. return bytes;
  10. }
  11. static Decode(packageName, msgTypeName, bytes) {
  12. var msgType = protores[packageName][msgTypeName];
  13. var msg = msgType.decode(bytes);
  14. var data = msgType.toObject(msg, {
  15. longs: Number, //long默认转换为Number类型
  16. enums: String,
  17. bytes: String,
  18. // see ConversionOptions
  19. });
  20. return data;
  21. }
  22. }
  23. module.exports = Pbjs6;

  测试:


 
 
  1. let Pbjs6 = require( "Pbjs6");
  2. let bytes = Pbjs6.Encode( "log", "comment_C", { msg: "NRatel" });
  3. cc.log( "bytes: ", bytes); //Uint8Array(14) [10, 12, 110, 105, 101, 104, 111, 110, 103, 113, 105, 97, 110, 103]
  4. let data = Pbjs6.Decode( "log", "comment_C", bytes);
  5. cc.log( "data: ", data); //{ msg: "NRatel" }

  2.动态方式(不推荐!)

  为什么要动态加载?,为了包体越小越好(微信小游戏中包体要求4M的限制)。

  为什么不推荐?,因为 protobufjs6.x 在微信小游戏中不可用(其内部使用Es6的Function。而微信禁止了动态生成代码的行为); protobufjs5.x 虽然可用,但无法处理import了其他proto文件的proto文件。

  protobufjs6.x的动态用法:


 
 
  1. let Assets = require( "Assets");
  2. let protobuf6 = require( "protobuf"); //6.x的protobufjs
  3. let Pbjs6 = class Pbjs6 {
  4. static s_ProtoRootMap = new Map();
  5. static LoadAll(protoDir) {
  6. return new Promise( (resolve, reject) => {
  7. //二次封装的ccc加载目录的方法
  8. Assets.LoadDir_ReturnWithUrls(protoDir).then( (object) => {
  9. let { resArray, urls } = object;
  10. for ( let index in resArray) {
  11. let path = urls[index];
  12. let key = path.substr(path.lastIndexOf( '/') + 1, path.length);
  13. //生成protoRoot并放入Map, 每个proto文件对应一个protoRoot
  14. let protoRoot = protobuf6.parse(resArray[index]).root;
  15. this.s_ProtoRootMap.set(key, protoRoot);
  16. }
  17. return resolve();
  18. });
  19. });
  20. }
  21. static Encode(packageName, msgTypeName, data) {
  22. //根据packageName找到对应的protoRoot
  23. let root = this.s_ProtoRootMap.get(packageName);
  24. cc.assert( typeof (root) != "undefined" && root != null, "未找到该protoRoot, 请确保已提前加载, packageName: ", packageName);
  25. //根据protoRoot和msgTypeName找到消息类型。
  26. let msgType = root.lookupType(msgTypeName);
  27. //根据消息类型检查数据
  28. let error = msgType.verify(data);
  29. cc.assert(error == null, "data数据类型检查失败!", error);
  30. //根据实际数据创建消息提,并encode为bytes
  31. let msg = msgType.create(data);
  32. let bytes = msgType.encode(msg).finish();
  33. return bytes;
  34. }
  35. static Decode(packageName, msgTypeName, bytes) {
  36. //根据packageName找到对应的protoRoot
  37. let root = this.s_ProtoRootMap.get(packageName);
  38. cc.assert( typeof (root) != "undefined" && root != null, "未找到该protoRoot,请确保已提前加载, packageName: ", packageName);
  39. //根据protoRoot和msgTypeName找到消息类型。
  40. var msgType = root.lookupType(msgTypeName);
  41. //根据实际bytes解析出原始数据
  42. var msg = msgType.decode(bytes);
  43. var data = msgType.toObject(msg, {
  44. longs: Number,
  45. enums: String,
  46. bytes: String,
  47. // see ConversionOptions
  48. });
  49. return data;
  50. }
  51. };
  52. module.exports = Pbjs6;

  protobuf5.x的动态用法:


 
 
  1. let Assets = require( "Assets");
  2. let protobuf5 = require( "protobuf"); //5.x的protobufjs
  3. let Pbjs5 = class Pbjs5 {
  4. static s_ProtoRootMap = new Map();
  5. static LoadAll(protoDir) {
  6. return new Promise( (resolve, reject) => {
  7. //二次封装的ccc加载目录的方法
  8. Assets.LoadDir_ReturnWithUrls(protoDir)
  9. .then( (object) => {
  10. let { resArray, urls } = object;
  11. for ( let index in resArray) {
  12. let path = urls[index];
  13. let key = path.substr(path.lastIndexOf( '/') + 1, path.length);
  14. let root = protobuf5.loadProto(resArray[index]).build(key);
  15. this.s_ProtoRootMap.set(key, root);
  16. }
  17. return resolve();
  18. });
  19. });
  20. }
  21. // 快捷式Encode
  22. // 传入msg对应的data
  23. static Encode(packageName, msgTypeName, data) {
  24. let root = this.s_ProtoRootMap.get(packageName);
  25. cc.assert( typeof (root) === "object" && root != null, "未找到protoPackage:" + packageName);
  26. let Message = root[msgTypeName];
  27. cc.assert( typeof (Message) === "function" && Message.$type.className === "Message", "未找到Message定义, packageName: " + packageName + ", msgTypeName: " + msgTypeName);
  28. let msg = new Message();
  29. for ( const p in data) {
  30. if (data.hasOwnProperty(p)) {
  31. msg.set(p, data[p], false);
  32. }
  33. }
  34. let bytes = new Uint8Array(msg.encode().toBuffer());
  35. return bytes;
  36. }
  37. // 面向对象式Encode。
  38. // 在callback中 对msg 的字段逐个 set 进行Encode。
  39. // 可以调用set(key, value), 也可以直接调用set_字段名(value), 字段命名规则为:同时支持下划线格式和驼峰格式。
  40. static EncodeOO(packageName, msgTypeName, callback) {
  41. let root = this.s_ProtoRootMap.get(packageName);
  42. cc.assert( typeof (root) === "object" && root != null, "未找到protoPackage:" + packageName);
  43. let Message = root[msgTypeName];
  44. cc.assert( typeof (Message) === "function" && Message.$type.className === "Message", "未找到Message定义, packageName: " + packageName + ", msgTypeName: " + msgTypeName);
  45. let msg = new Message();
  46. msg = callback(msg);
  47. let bytes = new Uint8Array(msg.encode().toBuffer());
  48. return bytes;
  49. }
  50. static Decode(packageName, msgTypeName, bytes) {
  51. let root = this.s_ProtoRootMap.get(packageName);
  52. cc.assert( typeof (root) === "object" && root != null, "未找到protoPackage:" + packageName);
  53. let Message = root[msgTypeName];
  54. cc.assert( typeof (Message) === "function" && Message.$type.className === "Message", "未找到Message定义, packageName: " + packageName + ", msgTypeName: " + msgTypeName);
  55. let msg = Message.decode(bytes);
  56. return msg;
  57. }
  58. };
  59. module.exports = Pbjs5;
(https://blog.csdn.net/NRatel/article/details/84251138).
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Cocos Creator是一个跨平台的游戏开发引擎,而Protobuf是一种数据序列化协议。如果你想在Cocos Creator中使用Protobuf,你可以按照以下步骤进行操作: 1. 下载并安装Protobuf编译器:你可以从Protobuf的官方网站上下载适用于你的操作系统的编译器。 2. 定义你的消息格式:创建一个`.proto`文件,其中定义了你的消息结构。例如,你可以定义一个`Person`消息类型,包含姓名和年龄等字段。 3. 编译消息定义:使用Protobuf编译器将你的`.proto`文件编译成目标语言(如JavaScript)的文件。例如,使用以下命令将`.proto`文件编译为JavaScript: ``` protoc --js_out=import_style=commonjs,binary:. your_proto_file.proto ``` 4. 在Cocos Creator中使用:将生成的JavaScript文件导入到Cocos Creator项目中,并使用它来序列化和反序列化你的消息。你可以使用`protobuf.js`库来处理Protobuf消息对象。 ``` const protobuf = require('protobufjs'); const YourProtoFile = require('path_to_generated_js_file'); // 创建消息对象 const message = new YourProtoFile.Person(); message.name = "John"; message.age = 25; // 序列化消息对象 const buffer = YourProtoFile.Person.encode(message).finish(); // 反序列化消息对象 const parsedMessage = YourProtoFile.Person.decode(buffer); ``` 这样,你就可以在Cocos Creator中使用Protobuf来序列化和反序列化消息对象了。记得根据你的实际消息定义和需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值