当creator遇上protobufjs|起步

栏目: 编程语言 · IOS · 发布时间: 5年前

内容简介:我一直在探索##1.初始化package.json文件当新建好cocos-js或creator项目,在项目根目录使用

一. 环境准备

我一直在探索 cocos H5正确的开发姿势 ,目前做javascript项目已经离不开 nodejs、npm或grunt等脚手架 工具 了。

##1.初始化package.json文件

 npm init 

当新建好cocos-js或creator项目,在项目根目录使用 npm init 命令,一路回车,将在当前目录创建 package.json 文件用于nodejs三方模块的管理。关于npm的使用细节网络上有很多教程,在此不用细说。

2. protobufjs模块

当creator遇上protobufjs|起步 本人最早在cocos2dx 2.x时代就开始用 protobufjs 模块来操纵protobuf一直到现在。所以下面所有内容都是关于protobufjs在cocos creator中的使用,包括原生平台(cocos2d-js也是大同小异)。

安装protobufjs到项目

 npm install protobufjs@5 –save 

使用npm install命令安装模块,注意我们这里使用的是protobufjs 5.x版本。 虽然protobufjs目前最新的 6.x版本,提供了ts、rpc等功能的支持,但接口变化太大,目前还不太会使用。

安装protobufjs到全局

 npm install -g protobufjs@5 

使用npm install -g 参数将模块安装到全局,目的主要是方便使用protobufjs提供的pbjs命令行工具。pbjs可以将proto原文件转换成json、js等,以提供不同的加载proto的方式,我们可以根据自己的实际情况选择使用。

二. protobufjs用法

下面是demo中定义的Player.proto文件的内容

syntax = "proto3";
package grace.proto.msg;

message Player {
    uint32  id = 1;         //唯一ID  首次登录时设置为0,由服务器分配
    string  name = 2;       //显示名字
    uint64  enterTime = 3;  //登录时间
}

关于proto具体语法细节这里就不多说了,我们重点如何将Player.proto文件中定义的Player对象在js中实例化、属性赋值、序列化、反序列化操作。

1. 静态语言中使用proto文件

在c++/java这类静态语言中使用protobuf通常是使用官方提供的protoc命令将proto文件编译成c++/java代码,像下面这样:

protoc –cpp_out=输出路径 xxx.proto

protoc –java_out=输出路径 xxx.proto

将输出路径的文件导入对应语言的工程中使用。

2. 在creator项目中使用proto文件

当creator遇上protobufjs|起步

javascript是动态语言,可以在运行时产生对象,因此protobufjs提供了更为便捷的动态编译,将proto文件中的对象生成js对象,下面简要讲解一下在creator中具体的使用步骤:

1.加载proto文件并编译生成proto对象

//导入protobufjs模块
let protobuf = require("protobufjs");
//获取一个builder对象
let builder = protobuf.newBuilder();
//使用protobufjs加文件,并与一个builder对象关联
protobuf.protoFromFile('xxx.proto', builder);
protobuf.protoFromFile('yyy.proto', builder);
...
let PB = builder.build('grace.proto.msg');

这步操作主要是使用protobufjs加载、编译proto文件。

2.实例化proto对象与属性赋值

let PB = builder.build('grace.proto.msg')

build函数返回值PB对象中将包含的是在proto中定义所有message对象,现在已经成为js对象,可以被实例化,代码如下:

//实例化Player
let player = new PB.Player();  
//属性赋值
player.name = '张三';             
player.enterTime = Date.now();

3.proto对象的序列化与反序列化

不说废话,还是直接上代码

...
//使用实例对象上的toArrayBuffer函数将对象序列化为二进制数据
let data = player.toArrayBuffer();
//使用类型对象上的decode函数将二进制数据反序列化为实例对象
let otherPlayer = PB.player.decode(data);

如果幸运你可以在web上使用protobuf了, 为什么只是在web上呢,当你把上面的代码运行在jsb环境下的时候,你会体验到悲催的事情正在发生。

三. 拯救cocos-jsb上的protobufjs

为什么在原生上运行就挂掉了呢?要理解这个问题需要对nodejs\ 浏览器\cocos-jsb这三个javascript的运行宿主环境有一定的了解。

我之前的文章提到过在选择nodejs模块时,要注意是否同时支持nodejs和web,只要是纯js的模块在cocos中一般都可以随便用,比如async、undersocre、lodash等。

protobufjs这个模块是可以很好的在浏览器和nodejs环境上运行的。但运行在cocos-jsb上就会出问题,首先我们要定位到出问题的关键代码:

protobuf.protoFromFile('xxx.proto', builder);

1. 问题分析

从protobuf.protoFromFile函数名上看就知道是要进行文件的加载,一想到文件加载,就涉及到文件操作的api,我们来整理一下不同平台上的文件接口:

宿主平台 文件接口 说明
浏览器 XMLHttpRequest 浏览器中动态加载资源、文件等AJAX操作的基础
nodejs fs.readFile / fs.readFileSync nodejs上的文件操作模块,底层由c/c++实现
cocos-jsb jsb.fileUtils.getStringFromFile cocos-js提供的读取文件内容接口,在不台平台(ios\android\windows)由不同底层api实现

看到这里相信很多人已经明白为什么在cocos-jsb上会有问题了,我们再来读一下protobufjs源码,证实下我们的分析。

2. 分析protobufjs源码

当creator遇上protobufjs|起步 找到protobufjs加载文件的主要代码,下面我为源码加上了注释,请认真读一下注释内容:

Util.fetch = function(path, callback) {
    //检查callback参数,callback参数决定是否为异步加载
    if (callback && typeof callback != 'function')
        callback = null;

    //运行环境是否为nodejs
    if (Util.IS_NODE) {
        //加载nodejs的文件系统模块
        var fs = require("fs");  
        //检查是否有callback,存在使用fs.readFile异步函数读取文件内容
        if (callback) {
            fs.readFile(path, function(err, data) {
                if (err)
                    callback(null);
                else
                    callback(""+data);
            });
        } else
            //使用fs.readFileSync同步函数读取文件内容 
            try {
                return fs.readFileSync(path);
            } catch (e) {
                return null;
            }
    } else {
        //当不为nodejs运行环境使用XmlHttpRequest加载文件
        var xhr = Util.XHR();
        //根据callbcak参数是否存在,使用异步还是同步方式
        xhr.open('GET', path, callback ? true : false);
        // xhr.setRequestHeader('User-Agent', 'XMLHTTP/1.0');
        xhr.setRequestHeader('Accept', 'text/plain');
        if (typeof xhr.overrideMimeType === 'function') xhr.overrideMimeType('text/plain');
        //通过XmlHttpRequest.onreadystatechange事件函数异步获取文件数据
        if (callback) {
            xhr.onreadystatechange = function() {
                if (xhr.readyState != 4) return;
                if (/* remote */ xhr.status == 200 || /* local */ (xhr.status == 0 && typeof xhr.responseText === 'string'))
                    callback(xhr.responseText);
                else
                    callback(null);
            };
            if (xhr.readyState == 4)
                return;
            //调用send方法发起AJAX请求
            xhr.send(null);
        } else {
            ////调用send方法发起AJAX请求,同步获取文件数据
            xhr.send(null);
            if (/* remote */ xhr.status == 200 || /* local */ (xhr.status == 0 && typeof xhr.responseText === 'string'))
                return xhr.responseText;
            return null;
        }
    }
};

从上面的代码可以看出protobufjs库是为浏览器和nodejs准备的,根本就没考虑过cocos-jsb的存在(吐槽:建议cocos官方提供的接口能模仿nodejs这样能少很多事),所以要在cocos-jsb中使用protobufjs 其中的一个办法 就是修改protobufjs的源码,如下:

Util.fetch = function(path, callback) {
    if (callback && typeof callback != 'function')
        callback = null;
    //将平台检查代码改为cocos提供的接口
    if (cc.sys.isNative) {
        //文件读取使用cocos-jsb提供的函数
        try {
            let data = jsb.fileUtils.getStringFromFile(path);
            cc.log(`proto文件内容: {data}`);
            return data;
        } catch (e) {
            return null;
        }
    } else {
        //web端无需修改,略
        ...
};

我们用cocos的接口将代码修改一下,加载问题就被化解了,问题真的被解决了吗?

不好意思,除了上面要代码外还有一处代码需要修改,源码如下:

BuilderPrototype["import"] = function(json, filename) {
    var delim = '/';

    // Make sure to skip duplicate imports

    if (typeof filename === 'string') {
        //这里又出现了平台检查
        if (ProtoBuf.Util.IS_NODE)
            // require("path")是加载nodejs的path模块,resolve
            filename = require("path")['resolve'](filename);
        if (this.files[filename] === true)
            return this.reset();
        this.files[filename] = true;

    } else if (typeof filename === 'object') { // Object with root, file.

        var root = filename.root;
        //这里还要修改
        if (ProtoBuf.Util.IS_NODE)
            root = require("path")['resolve'](root);
        if (root.indexOf("\\") >= 0 || filename.file.indexOf("\\") >= 0)
            delim = '\\';
        var fname;
         //这里还要修改
        if (ProtoBuf.Util.IS_NODE)
            fname = require("path")['join'](root, filename.file);
        else
            fname = root + delim + filename.file;
        if (this.files[fname] === true)
            return this.reset();
        this.files[fname] = true;
    }
    ...
}

这里我就不再贴修改代码了,大家自行解决。

四 为protobuf继续填坑

本来写到这里,问题大多已经解决了, 但此时,如果你满怀信心地使用改造后的protobufjs源码,将你的代码运行起来那一刻,我相信绝大多数人会一脸蒙逼。

当creator遇上protobufjs|起步

妈的根本就不行!!看了好多字,好不容易读到这里,不仅在模拟器上跑不起来,在web上同样也跑不起来。

怎么办,为了彻底解决问题,我还得继续写下去。

####1. 了解creator动态加载资源的方法

请大家思考一个问题,creator项目中的一张图片,在web与cocos-jsb上他们的文件路径会一样吗?直接使用protobuf.protoFromFile(‘xxx.proto’)去加载一个proto文件会成功吗?

cocos文档中说过要动态加载一个图片资源需要将文件存放在assets/resources目录下,使用如下方法加载:

cc.loader.loadRes('resources/xxx')

尝试将proto文件存放在resources/pb/目录下,用使用以下代码:

protobuf.protoFromFile('resources/pb/xxx.proto')

同样会得到失败的提示,该如何办呢?怎么才能获得正确的资源路径?

算了,不买关子了,写累了直接出答案吧!

protobuf.protoFromFile(cc.url.raw('resources/pb/xxx.proto'));

cc.url.raw这个函数在浏览器、模拟器、手机上会返回不同的资源路径,这才是真正的资源路径,这下代码应该可以正常运行起来了。

####2. 更好的解决法办

我一直在探索cocos H5正确的开发方式,虽然通过修改protobufjs源码的方法可以来解决在cocos-jsb上运行的问题,但这并不是唯一的解决方案。

我这里编写了一个creator + protobufjs的demo没有使用上述方案,地址如下:

 https://github.com/ShawnZhang2015/grace 

如何在不修改protobufjs源码的情况下让代码运行起来,以及使用pbjs工具预编译proto文件为JSON和js文件的用法,请继续观注我的系列文章《探索cocosH5正确的开发姿势》!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ant Colony Optimization

Ant Colony Optimization

Marco Dorigo、Thomas Stützle / A Bradford Book / 2004-6-4 / USD 45.00

The complex social behaviors of ants have been much studied by science, and computer scientists are now finding that these behavior patterns can provide models for solving difficult combinatorial opti......一起来看看 《Ant Colony Optimization》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

html转js在线工具
html转js在线工具

html转js在线工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具