cocos creator 加载自定义或者压缩文件的方式

工具

cocos creator 2.1.2
查阅代码目录:[cocos creator 2.1.2项目目录]\resources\engine
	\cocos2d\core\load-pipeline\text-loader.js
	\cocos2d\core\load-pipeline\binary-loader.js
	\resources\builtin\weapp-adapter\wx-downloader.js
	\resources\builtin\weapp-adapter\wx-fs-utils.js
	等等

起因

1. 游戏是关卡类型的
2. 关卡全部使用预制件的形式制作
3. 关卡打包的时候生成的json文件过大
4. 需要对这些文件进行压缩

代码调整思路

1. 在cocos creator中加载的大题流程是
	a. download
	b. loader
	大概这样的形式,那么只要替换调加载的函数就可以了
2. 在download中替换相应的函数
	例如: web直接替换loadJson函数 
			大概:cc.loader.addDownloadHandlers({"json":funcJsonGz});
			cc.loader.addDownloadHandlers({"gz":funcJsonGz});
		wx大需要修改wx-downloader.js中
			readFile函数
	内容大概是:
		a. 判断后缀是否json文件
		b. 使用binary加载
		c. 判断头部是否是压缩文件
		d. 进行解压或者直接返回数据

压缩思路

1. 编写插件在发布结束的时候,读取所有json后缀文件
2. 判断大小进行gz压缩写会原来的文件

压缩

这里选择的是直接对json进行压缩,文件后缀不变,使用pako库

大体代码(针对web和wx包内资源)

// utils.js web使用的加载方式, wx远程加载没有做设计考虑
"use strict";

var Uint8ArrayToString = function(uint8array){
    return new TextDecoder("utf-8").decode(uint8array);
}

//=============================================
var funcJsonGz =  function(item, callback) {
    // 根据不同情况进行判断处理否则问题多多
    var url = item.url;
    if (!CC_EDITOR && CC_BUILD && !CC_PREVIEW){
        url = url.replace(".json", ".gz");
    }
    var errInfo = 'Load binary data failed: ' + url + '';
    var xhr = cc.loader.getXMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = "arraybuffer";
    xhr.onload = function () {
        var arrayBuffer = xhr.response;
        if (arrayBuffer) {
            var result = new Uint8Array(arrayBuffer);

            let outString = "";
            let uerr = null;
            //===================================
            // 判断开头的处理 是否是压缩方式
            if (result[0] == 0x1f && result[1] == 0x8b){
                try {
                    outString = pako.inflate(result, { gzip: true, to: 'string'});
                } catch (error) {
                    cc.log(error);
                    uerr = error;
                }
            }else{
                outString = Uint8ArrayToString(result);
            }
            //===================================
            if (!uerr){
                callback(null, outString);
            }else{
                callback({status:-1, err:uerr,errorMessage:errInfo + " pako.inflate fail "});
            }
        }
        else {
            callback({status:xhr.status, errorMessage:errInfo + '(no response)'});
        }
    };
    xhr.onerror = function(){
        callback({status:xhr.status, errorMessage:errInfo + '(error)'});
    };
    xhr.ontimeout = function(){
        callback({status:xhr.status, errorMessage:errInfo + '(time out)'});
    };
    xhr.send(null);
}



cc.loader.addDownloadHandlers({"json":funcJsonGz});
cc.loader.addDownloadHandlers({"gz":funcJsonGz});

//=========================================
//  Game.js

cc.Class({
    extends: cc.Component,

    properties: {

    },

    start () {
        cc.loader.loadRes("prefabs/node_01", (err, prefab)=>{
            if (err) {
                cc.error(err.message || err);
                return;
            }

            var n = cc.instantiate(prefab);
            this.node.addChild(n);

        });
    },

    // update (dt) {},
});

需要使用的打包插件

// package.json
{
  "name": "gz_tool",
  "version": "0.0.1",
  "description": "The package template for getting started.",
  "author": "Cocos Creator",
  "main": "main.js",
  "scene-script": "scene-walker.js",
  "main-menu": {
    "i18n:MAIN_MENU.package.title/gz_tool/open": {
      "message": "gz_tool:open"
    },
    "i18n:MAIN_MENU.package.title/gz_tool/hello": {
      "message": "gz_tool:say-hello"
    }
  },
  "panel": {
    "main": "panel/index.js",
    "type": "dockable",
    "title": "gz_tool",
    "width": 400,
    "height": 300
  }
}

// main.js
'use strict';
		
function onBeforeBuildFinish (options, callback) {
	
	
    Editor.log('Building ' + options.platform + ' to ' + options.dest); // 你可以在控制台输出点什么
	Editor.Scene.callSceneScript('gz_tool', 'say-gzstart', options, function (err, val) {
			Editor.log(`say-gzstart rep:${val}`);
	});
    callback();
}

module.exports = {
  load () {
    // execute when package loaded
	Editor.Builder.on('before-change-files', onBeforeBuildFinish);
  },

  unload () {
    // execute when package unloaded
	Editor.Builder.removeListener('before-change-files', onBeforeBuildFinish);
  },

  // register your ipc messages here
  messages: {
    'open' () {
      // open entry panel registered in package.json
      Editor.Panel.open('gz_tool');
    },
    'say-hello' () {
      Editor.log('Hello World!');
      // send ipc message to panel
      //Editor.Ipc.sendToPanel('gz_tool', 'gz_tool:hello');
	  
	  
	  Editor.Scene.callSceneScript('gz_tool', 'say-gzfile', function (err, val) {
			Editor.log(`say-gzfile rep:${val}`);
		});
    },
    'clicked' () {

    }
  },
};


//  scene-walker.js
const path = require('path');
const fs = require('fs');
const zlib = require('zlib');

// 遍历目录
function findPaths(ph, list, ext, filters){
	var pa = fs.readdirSync(ph);
	pa.forEach(function(file,index){
		var info = fs.statSync(ph+"/"+file)
		if(info.isDirectory()){
			findPaths(ph+"/"+file, list, ext, filters);
		}else{
			
			if (!ext || path.extname(file) == ext){
				var onlyname = path.basename(file, ext);
				if (!filters || !filters[onlyname]){
					list.push(ph+"/"+file);
				}
			}
		}	
	})
}
		
module.exports = {
    
	'ouput_deepQuery':function(){
		Editor.assetdb.deepQuery(function (err, results) {
		  results.forEach(function (result) {
			// result.name
			// result.extname
			// result.uuid
			// result.type
			// result.isSubAsset
			// result.children - the array of children result
			Editor.log('===>> xx==>', result.name, result.type);
		  });
		});
		/*
		Editor.assetdb.queryAssets('db://assets/**\/*', 'texture', function (err, results) {
			results.forEach(function (result) {
				// result.url
				// result.path
				// result.uuid
				// result.type
			});
		});
		*/
	},
	'say-gzstart':function(event,options){
		// Editor.log("options", options);
		// 输出目录
		var dest = options.dest;
		var startScene = options.startScene;
		
		var buildResults = options.buildResults;
		var _buildAssets = buildResults._buildAssets;
		var _packedAssets = buildResults._packedAssets;
		
		// 遍历所有json文件
		var jsons = [];
		// 排除开始场景, 需要开始场景中加入json的加密加载方案,否则可能有问题
		// 这里这对wechat 中的开始的两个json配置文件先排除掉
		var filters = {'game':true,'project.config':true};//{startScene:true};
		
		findPaths(dest, jsons, '.json', filters);

		jsons.forEach(jsonPath=>{
			// 替换成gz文件
			var outpath = jsonPath.replace(".json", ".gz");
			var data = fs.readFileSync(jsonPath);
			var buff = zlib.gzipSync(data);
			fs.writeFileSync(outpath, buff);
		});
		
		jsons.forEach(jsonPath=>{
			fs.unlinkSync(jsonPath);
		});
		
		
		// 复制文件和处理头部 这里用于复制一些需要的文件和设置一些信息
//		if (cc.sys.platform == cc.sys.WECHAT_GAME){
		if (options.platform == "wechatgame"){
			/*
			var sdkpath = 'xxxx';
			var  cps = [];
			findPaths(sdkpath, cps);
			var outpath = dest+'/utils';
			if (!fs.existsSync(outpath)){
				fs.mkdirSync(outpath);
			}
			cps.forEach(fp=>{
				var fn = path.basename(fp);
				fs.writeFileSync(outpath + "/" + fn, fs.readFileSync(fp));
			});
			
			// 添加头部
			var gamefn = dest + '/game.js';
			var gamedt = 'require(\'utils/ald-game.js\')\n' + fs.readFileSync(gamefn, 'utf8');
			fs.writeFileSync(gamefn, gamedt);
			*/
		}
		
	},
};

注意

上面的代码if (options.platform == "wechatgame")

wx中相应的修改

// [cocos creator 2.1.2项目目录]\resources\builtin\weapp-adapter
// wx-downloader.js
const pako = require('../src/assets/scripts/utils/pako.min.js');


var Uint8ArrayToString = function(array){
	var out, i, len, c;
    var char2, char3;
 
    out = "";
    len = array.length;
    i = 0;
    while(i < len) {
    c = array[i++];
    switch(c >> 4)
    { 
      case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
        // 0xxxxxxx
        out += String.fromCharCode(c);
        break;
      case 12: case 13:
        // 110x xxxx   10xx xxxx
        char2 = array[i++];
        out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
        break;
      case 14:
        // 1110 xxxx  10xx xxxx  10xx xxxx
        char2 = array[i++];
        char3 = array[i++];
        out += String.fromCharCode(((c & 0x0F) << 12) |
                       ((char2 & 0x3F) << 6) |
                       ((char3 & 0x3F) << 0));
        break;
    }
    }
 
    return out;
    //return new TextDecoder("utf-8").decode(array);
}

function readFile (item, callback) {
    var url = item.url;
	var bJson = cc.path.extname(url) == '.json';
	if (bJson && url.indexOf("project.config.json") < 0 && url.indexOf('game.json') < 0){
		url = url.replace(".json", ".gz");
	}
	
    var func = wxFsUtils.readText;
	if (getFileType(item.type) === FileType.BIN || bJson) 
		func = wxFsUtils.readArrayBuffer;
	
    var result = func(url, function (err, data) {
        if (err) {
            callback(err);
            return;
        }
		
        if (data) {
            item.states[cc.loader.downloader.id] = cc.Pipeline.ItemState.COMPLETE;
			
			let outString = "";
			let uerr = null;
				
			if (item.type == 'json' || bJson){
				var result = new Uint8Array(data);
				//===================================	
				if (result[0] == 0x1f && result[1] == 0x8b){
					try {
						outString = pako.inflate(result, { gzip: true, to: 'string'});
					} catch (error) {
						cc.log("error",error);
						uerr = error;
					}
				}else{
					outString = Uint8ArrayToString(result);
				}
			}else{
				outString = data;
			}
			
			callback(uerr, outString);
        }
        else {
            callback(new Error("Empty file: " + url));
        }
    });
    if (result) callback(result);
}

var map = {
......
'json' : FileType.BIN,
.....
}


//  wx-fs-utils.js
// 排除wechat中的初始两个json配置文件

function getRealPath(filePath){
	if (filePath.indexOf('game.json') > 0 || filePath.indexOf('project.config.json') > 0){
		return filePath;
	}
	if (cc.path.extname(filePath) == '.json'){
		filePath = filePath.replace('.json', '.gz');
	}
	
	return filePath;
}

function exists (filePath, callback) {
	filePath = getRealPath(filePath);
    var result = checkFsValid();
    if (result) return result;
    fs.access({
        path: filePath,
        success: callback ? function () {
            callback(true);
        } : undefined,
        fail: callback ? function () {
            callback(false);
        } : undefined,
    });
}

最后是整个项目的下载地址
包含

  1. 基本的加载压缩
  2. 压缩插件
  3. wx-download.txt => wx-download.js 在 other目录
  4. wx-fs-utils.txt => wx-fs-utils.js 在 other目录

https://github.com/hcqmaker/sharething/blob/master/gz_exmaple_v1.0.7z

你可能感兴趣的:(游戏设计,工具)