cocosCreator 打造Excel表格数据驱动系统

前言:cocosCreator数据类型解析方案学习。
版本:2.4.x。

简述:日常游戏开发过程中,表格配置常常转换成Json、xml、txt等格式文件进行加载解析,独自开发或维护他人项目时当加载进来的数据由于缺少类型判断,用起来很不方便,不符合ts中类型的识别;所以这篇文章主要解决如下几点:
1、表格如何转换成json、xml、txt等格式性文件。
2、通过加入类型转换,能很好的识别文件类型。

一、excel转换到txt文件

更常见的为json文件,毕竟json序列化和适配性更强一些,比如适配一些界面工具会更友好一些。不过无所谓我个人习惯使用txt作为数据文件,因为我可以只设计两种类型string和number便可满足我整个游戏的使用。

我经常使用的2款工具,不过这次我们只使用第一个:

  1. node-xlsx; => excel转换为json,txt等非常好用。
  2. quickType; =>我常用json转ts类类型。不过这个工具已经不在维护可能会有些bug。

我已经编写好xlsxTofile.js脚本,我们只需要通过node执行该脚本格式如下:

node xlsxTofile.js /Users/xxxx/Desktop/Table

结果:
1、Table是存放excel的文件夹,无论该文件夹下如何嵌套只识别*.xlsx文件。
2、xlsxTofile.js所在路径下自动生成2个文件夹tableData和tableType,分别存放生成的.txt和.ts文件。

我们在使用该脚本的时候对excel的格式要求:
1、表格中第一行为字段属性说明|第二行为字段属性,也是按照该行为基准存在则统计不存在则不统计数据|第三行为类型,只有string和number便够用了。
2、表格中每个sheet的名字便是我们生成文件的名字,有几个sheet便生成几份。比如:sheet名字GouMai,则生成的GouMai.txt和GouMaiData.ts文件请添加图片描述
生成的txt数据
请添加图片描述
生成的ts数据
请添加图片描述
xlsxTofile.js脚本代码如下:

let fs = require('fs');
let chalk = require('chalk');
let xlsx = require('node-xlsx');
let fsExtra = require('fs-extra');
let walkSync = require('walk-sync');

const outPathTxt = './tableData';
const outPahtTs =  './tableType';

function writeTypeFileOfTxt(_listData) {
  //按照表格中的第二行为基准计算
  let flagArray = _listData.data[1]; //第二行

  let outStr = '';
  let singleData = [];
  let nameStr = _listData.name; //文件名称
  let tempData = _listData.data; //文件内容
  for (let values of tempData) {
    singleData.length = 0;
    flagArray.forEach((value, index) => {
      if (value) {
        singleData.push(values[index]);
      }
    });
    outStr += singleData.join('`') + '\r\n';
  }

  //写入文件
  let writeStream = fs.createWriteStream(outPathTxt + '/' + nameStr + '.txt', { encoding: 'utf8' });
  writeStream.write(outStr);
  writeStream.end();

  writeStream.on('finish', function () {
    console.count('fileCount')
  });
}

function writeTypeFileOfTs(_listData) {
  let descArray = _listData.data[0]; //第一行
  let flagArray = _listData.data[1]; //第二行
  let typeArray = _listData.data[2]; //第三行

  let nameStr = _listData.name; //文件名称
  let outStr = '';
  let outStr1 = `export default class ${nameStr + 'Data'} {` + '\r\n';
  let outStr2 = '}';
  flagArray.forEach((value, index) => {
    if (value) {
      outStr += '\t' + '/// <summary>' + '\r\n\t' + '/// ' + descArray[index] + '\r\n\t' + '/// <summary>' + '\r\n\t' + flagArray[index] + ': ' + typeArray[index] + ';' + '\r\n';
    }
  })

  let writeStream = fs.createWriteStream(outPahtTs + '/' + nameStr + 'Data.ts', { encoding: 'utf8' });
  writeStream.write(outStr1 + outStr + outStr2);
  writeStream.end();
}

function setFileToDirectory(_listDatas) {
  for (let i = 0, j = _listDatas.length; i < j; i++) {
    //写入txt
    writeTypeFileOfTxt(_listDatas[i]);
    //写入ts
    writeTypeFileOfTs(_listDatas[i]);
  }
}

function repeatCheck(listDatas) {
  let tempArr = [];
  listDatas.forEach((value, index) => {
    if (tempArr.indexOf(value.name) != -1) {
      console.log(chalk.red(value.name + '名称重复!'))
    }
    else {
      tempArr.push(value.name);
    }
  })
}


let listDatas = [];
let sourcePath = process.argv[2];
if (sourcePath) {
  //获取表格路径
  let tablePaths = walkSync(sourcePath, { globs: ['**/*.xlsx'] });

  //根据路径解析表格数据存入数组
  tablePaths.forEach((value, index) => {
    listDatas = listDatas.concat(xlsx.parse(sourcePath + '/' + value));
  });

  //名称重复检测
  repeatCheck(listDatas);
  //console.log('listDatas', listDatas)

  //创建文件夹
  fsExtra.removeSync(outPathTxt);
  fsExtra.removeSync(outPahtTs);
  fs.mkdirSync(outPathTxt);
  fs.mkdirSync(outPahtTs);

  //生成.txt和ts文件
  setFileToDirectory(listDatas);
}
else {
  console.log('缺少路径参数!')
}


二、cocosCrator中如何读取

开发中我们常用的一种方式就是将数据在进入游戏前整理好加入内存中(加载的过程我们可以制作一个进度条界面进行数据解析进度的显示),在游戏中便可以随时的获取相对比较方便。
这里我们的思路:
1、通过动态加载的方式加入到内存当中。
2、解析数据成我们想要的结构,存入容器中。
3、通过容器获取我们想要的整体数据或单行数据。
4、通过引入类型的识别,开发过程更顺手。

注意:这些东西应该去遵循内存管理机制,比如你数据做成远程资源包处理完数据你就可以释放掉bundle。本地加载也同样释放掉。减少内存占用~后面有时间我们可以整理关于资源释放的内容。

项目中主要两个脚本:
LoadTable.ts负责数据的解析工作
Test.ts主要用于测试加载的数据。(如下图)
请添加图片描述

LoadTable.ts数据解析代码:

const { ccclass, property } = cc._decorator;

export enum TableType {
    Table_Type_GouMai = 0,
    Table_Type_JiaGe = 1
}

class TableCaheMap {
    /**
     * 所有的数据
     */
    public _tableArray: any = [];
    /**
     * 每行的数据
     */
    public _tableMap: Map<string, any> = new Map();
}

@ccclass
export default class LoadTable extends cc.Component {
    public static Instance: LoadTable = null;
    public static tableCaheMap: Map<TableType, TableCaheMap> = new Map();
    private tableDataPaths: any[] = [];  //存放txt路径

    onLoad() {
        LoadTable.Instance = this;
    }

    start() {
        this.initTablePath();
        this.schedule(this.startDataAnalysis);
    }

    private initTablePath() {
        this.tableDataPaths[TableType.Table_Type_GouMai] = 'tableData/GouMai';
        this.tableDataPaths[TableType.Table_Type_JiaGe] = 'tableData/JiaGe';
    }

    private curIndex: number = 0;
    private startDataAnalysis() {
        cc.resources.load(this.tableDataPaths[this.curIndex], cc.TextAsset, (err: Error, file: cc.TextAsset) => {
            if (err) {
                this.curIndex++;
                if (this.curIndex >= this.tableDataPaths.length) {
                    this.unschedule(this.startDataAnalysis);
                    return;
                }
            }
            else {
                if (LoadTable.tableCaheMap.has(this.curIndex as TableType) == false) {
                    LoadTable.tableCaheMap.set(this.curIndex as TableType, new TableCaheMap());
                }
                let tableCaheMap = LoadTable.tableCaheMap.get(this.curIndex as TableType);
                //按照第二行的属性为字段标识
                //对tableCaheMap进行赋值
                let textArray: string[] = file.text.split('\r\n');
                let flagArray: string[] = textArray[1].split('`');
                let typeArray: string[] = textArray[2].split('`');
                for (let i = 3, j = textArray.length; i < j; i++) {
                    let descObj: any = {};
                    flagArray.forEach((value, index) => {
                        let dataArray: string[] = textArray[i].split('`');
                        if (typeArray[index] == 'number') {
                            descObj[flagArray[index]] = Number(dataArray[index]);
                        }
                        else if (typeArray[index] == 'string') {
                            descObj[flagArray[index]] = dataArray[index];
                        }
                        tableCaheMap._tableMap.set(dataArray[0], descObj)
                    })
                    tableCaheMap._tableArray[i - 3] = descObj;
                }

                //判断是否停止调度器
                this.curIndex++;
                if (this.curIndex >= this.tableDataPaths.length) {
                    this.unschedule(this.startDataAnalysis);
                    return;
                }
            }
        });
    }

    //获取idKey数据
    public searchTable(tableType: TableType, idKey: string): any {
        let tableCaheMap: TableCaheMap = LoadTable.tableCaheMap.get(tableType);
        if (tableCaheMap == null) {
            return null;
        }

        return tableCaheMap._tableMap.get(idKey);
    }

    //获取全部数据
    public searchTables(tableType: TableType): any {
        let tableCaheMap: TableCaheMap = LoadTable.tableCaheMap.get(tableType);
        if (tableCaheMap == null) {
            return null;
        }

        return tableCaheMap._tableArray;
    }
}

请添加图片描述

表格文件夹:Table
脚本文件夹:test
项目工程:TableAnalysis。
上传gitee路径:https://gitee.com/songhuiyuan/table-analysis.git
下载可直接用来执行node命令进行测试,如有问题请评论留言~

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值