内容简介:同步美术、策划资源是日常开发中极为频繁的事情,shawn借用Web前端的一些思想和工具,将Grunt自动化框架引入Cocos Creator项目,可以实现相对高效地将图片、动画、配置、音效等游戏资源导入到客户端工程中。在开始之前先用简单介绍一下Grunt是什么:
同步美术、策划资源是日常开发中极为频繁的事情,shawn借用Web前端的一些思想和工具,将Grunt自动化框架引入Cocos Creator项目,可以实现相对高效地将图片、动画、配置、音效等游戏资源导入到客户端工程中。
grunt介绍
在开始之前先用简单介绍一下Grunt是什么:
为什么选择使用Grunt做自动化工具,我简单总结下面几点:
1. 使用JavaScript语言,与Cocos Creator开发使用相同的语言,减少学习成本
2. 插件丰富,6000+(本篇文章只介绍两个grunt-sync和grunt-shell)
3. 使用json配置插件完成任务,没有太多逻辑条件,使用简单容易上手,当配置好一个模块后,其它模块可以依葫芦画瓢,策划人员也可以上手配置
4. Grunt基于Nodejs,可以借用大量插件与npm模块实现各种复杂需求
5. 跨平台
安装grunt与插件
首先,使用npm安装全局grunt-cli工具:
>npm install grunt-cli -g
然后在Cocos Creator项目根目录初始化npm的包管理文件package.json:
>npm init
输入npm init后一路回车,然后在项目中安装grunt npm模块:
>npm install grunt --save-dev
grunt只是一个自动化框架,我们这里还需要安装上面说的两个插件
>npm install grunt-sync --save-dev //文件同步插件 >npm install grunt-shell --save-dev //shell插件
Grunt任务模块目录结构
安装好Grunt的命令行、插件后,在项目根目录创建Gruntfile.js文件,这是Grunt自动任务的入口文件。同时在根项目新建一个tools目录,用于存放各种与项目有关的 工具 或脚本,再添加一个grunt-task目录,用于存放具体的grunt任务配置脚本,请看下图:
上图中xxx-task.js就是各子模块的自动化任务。
Gruntfile
shawn在早期使用Grunt时,是将所有任务都编写在Gruntfile.js文件,当模块越来越多,维护起来越来越困难,因此将不同模块的自动化任务独立开来,在Gruntfile.js进行统一加载和任务注册,下面看下Gruntfile文件的内容:
//引入rd模块读取文件 let rd = require('rd'); //获./tools/grunt-task目录下取所有文件 let taskScripts = rd.readFileSync('./tools/grunt-task'); module.exports = (grunt) => { //我们这里使用了grunt-shel&grunt-sync插件 //下面 shell 与sync对象分别用于收集两种任务配置 let shell = {}; let sync = {}; //将grunt设置为全局变量 global.grunt = grunt; //require所有任务模块,放入tasks数组 let tasks = []; taskScripts.forEach((script) => { let task = require(script); if (task.init) { //让task对象自己填充sync和shell内容 task.init(sync, shell); tasks.push(task); } }); //配置sync、shell两大任务 grunt.initConfig({ sync, shell, }); //注册grunt-shell插件,用于执行外部shell命令 grunt.loadNpmTasks('grunt-shell'); //注册grunt-sync插件,用于本地文件同步 grunt.loadNpmTasks('grunt-sync'); //注册自定义的grunt任务 tasks.forEach(task => task.registerTask()); };
对上面代码简单说明:
1. 加载tools/grunt-task下所有任务脚本
2. 为每个任务对象传入sync、shell两个任务集合对象,各任务模块在sync、shell对象中配置自己的任务内容。
3. 使用了两个grunt插件:grunt-shell、grunt-sync
4. 最后调用所有task.registerTask()将命令注册到grunt命令行
在命令控制台上执行grunt –help会看到我们所编写的自动化任务,下图是shawn曾经项目中创建的Grunt任务:
文件同步任务
你现在去执行 grunt --help
还是空的,还没有注册具体的任务,前面介绍过,Grunt是使用插件 + JSON配置的方式来创建任务,我们看一个子模块美术资源为例:
资源仓库与 客户端Assets 按上图所示建立对应关系,其中绿色箭头是文件同步操作,其中headIcon目录中的图片,在项目中是动态加载的,需要同步到assets/resources/game1/texture目录下,这些操作我们可以使用grunt-sycn来完成。
其中比较特别是atlas目录,里面是经过分类需要合并图集的图片,文件合并后放到客户端项目assets/game1/texture/atlas目录,这个操作shawn是借用TexturePacker命令行工具 + Nodejs脚本来完成。
UI资源同步
梳理好了资源目录结构,现在我们将上流程编写成grunt同步任务,创建一个game1-task.js的文件,内容如下:
let path = require('path'); //获取grunt参数,是否模拟执行,不真实复制文件 let pretend = grunt.option('pretend'); //获取资源根路径,资源路径是定义在单独的define.js文件 let { UI_ROOT_PATH,ANI_ROOT_PATH } = require('./define'); //定义模块名,也就是在assets下的目录名 const moduleName = 'game1'; //定义同步任务 const syncTask = { //同步UI任务:普通图片、背景图片 'sync-ui': { files: [ //同步UI图片 { //设置过滤器,排除atlas、headIcon、svn、隐藏文件 src: ['**', '!atlas/', '!headIcon/', '!**/.*', '!**/.svn', '!**/.svn/**'], dest: `./assets/${moduleName}/texture/ui`, //目标路径,将文件同步到此处 cwd: path.join(UI_ROOT_PATH, moduleName, 'ui') //美术UI资源目录 }, //同步headIcon { //排除 atlas 目录及下面的子目录、文件 src: ['**', '!**/.*', '!**/.svn', '!**/.svn/**'], //同步到resources/moduleName/texture目录下 dest: `./assets/resources/${moduleName}/texture`, cwd: path.join(UI_ROOT_PATH, moduleName, 'ui', 'headIcon') }, //同步背景图片 { src: ['**', '!**/.*', '!**/.svn', '!**/.svn/**'], dest: `./assets/${moduleName}/texture/bg`, cwd: path.join(UI_ROOT_PATH, moduleName, 'bg') } ], verbose: true, // 显示日志 pretend: false, // 模拟输出 updateAndDelete: true, // 删除dst冗余文件 compareUsing: 'md5', // 可选'mtime/md5' ignoreInDest: ['**/*.meta', '**/*.pac', '**/.svn/**'], // 不删除.meta文件 }, }
上面代码中sync-ui就是一个同步任务,其中files数组中配置同步目录,每一个数组元素包含三个字段:
1. src: 文件过滤器
2. dest: 目标路径,同步到那里去,以当前Gruntfile文件为相对路径
3. cwd: 源路径,从那里去复制文件,同样以Gruntfile文件为相对路径
然后是同步选项:
1. verbose: 打印日志输出,这对我们检查路径是否正确非常有用,建议设置为true
2. pretend: 同步模拟,当值为真时,配合verbose使用只会显示要同步的文件,不会真实写入或删除文件目标文件
3. updateAndDelete:删除冗余文件,比如你动随意放入一个图片到客户端ui目录,当执行ui同步资源,这个文件并未在资源仓库中,它会被同步删除掉
4. compareUsing:文件比较策略,可选项”md5″与“mtime”,建议使用md5保证正确性
5. ignoreInDest:指定同步时不删除那些文件,这个选项非常有用,我们都知道Cocos Creator会为每个文件生成同名.meta文件,这里一定要注意,不能被同步掉了,除了meta文件外,还有自动图集、svn等文件。下面是执行grunt-shell命令的效果:
⮀ grunt up-hall --pretend Running "sync:hall-ui" (sync) task Copying ../../../hall/ui/hall_btn_jlb.png -> assets/hall/texture/ui/hall_btn_jlb.png Copying ../../../hall/ui/hall_btn_kefu.png -> assets/hall/texture/ui/hall_btn_kefu.png Copying ../../../hall/ui/hall_btn_mail.png -> assets/hall/texture/ui/hall_btn_mail.png Copying ../../../hall/ui/hall_btn_set.png -> assets/hall/texture/ui/hall_btn_set.png Copying ../../../hall/ui/hall_btn_share.png -> assets/hall/texture/ui/hall_btn_share.png Copying ../../../hall/ui/hall_Btn_shop.png -> assets/hall/texture/ui/hall_Btn_shop.png Copying ../../../hall/ui/hall_btn_yxsj.png -> assets/hall/texture/ui/hall_btn_yxsj.png Copying ../../../hall/ui/hall_img_dianchi.png -> assets/hall/texture/ui/hall_img_dianchi.png Copying ../../../hall/ui/hall_img_dianliang.png -> assets/hall/texture/ui/hall_img_dianliang.png Copying ../../../hall/ui/hall_img_gold.png -> assets/hall/texture/ui/hall_img_gold.png Copying ../../../hall/ui/hall_img_head_00.png -> assets/hall/texture/ui/hall_img_head_00.png Copying ../../../hall/ui/hall_img_head_01.png -> assets/hall/texture/ui/hall_img_head_01.png Copying ../../../hall/ui/hall_img_laba.png -> assets/hall/texture/ui/hall_img_laba.png Copying ../../../hall/ui/hall_img_red.png -> assets/hall/texture/ui/hall_img_red.png Copying ../../../hall/ui/hall_img_xinhao.png -> assets/hall/texture/ui/hall_img_xinhao.png Copying ../../../hall/ui/hall_mask_bottom.png -> assets/hall/texture/ui/hall_mask_bottom.png Unlinking assets/resources/hall/texture/headimg/default.png because it was removed from src. Unlinking assets/resources/hall/texture/ui/ttz_bg_toast.png because it was removed from src. Removing dir assets/resources/hall/texture/ui because not longer in src.
上面可以看到以Copying开头的是文件复制信息,使用verbose参数,它显示了从那儿复制文件那儿,Unlinking是删除文件,同样显示了被删除的文件路径。
动画资源同步
上面讲了UI资源的同步, 对于动画资源我们处理方式有些不同,因此需要单独创建一个同步任务:
const syncTask = { 'sync-ui': { ... } 'sync-ani': { files: [ //同步UI { src: ['**', '**/!.DS_Store', '!**/.svn', '!**/.svn/**', '!**/.gitignore'], //过滤器 dest: `./assets/{moduleName}/animations`, cwd: path.join(ANI_ROOT_PATH, moduleName, 'animations') }, ], verbose: true, // 显示日志 pretend: pretend || false, // 模拟输出 updateAndDelete: true, // 删除dst冗余文件 compareUsing: 'md5', // 可选'mtime/md5' ignoreInDest: ['**/.svn/**', '.DS_Store', '**/.gitignore'], // 不删除.svn下文件 }, } }
动画同步与UI同步最大的差别在于,ignoreInDes同步选项不能忽略meta文件。在shawn的项目中,动画是由美术人员在独立的Cocos Creator工程中编辑的,美术人员可以在动画工程中使用Cocos Creator动画编辑器或Spine、DragonBones等动画资源,使用Prefab进行整合,客户端主要依赖美术提供的动画prefab文件以及动画名字,动画同步任务需要将所有动画资源全部同步到客户端项目中,其中包括所有的meta文件。
这里可能会有一个小小的风险,就是动画工程中的meta文件与客户端界面中的meta文件发生UUID冲突,这种冲突的可能性是完全存在的,但在shawn一年半十多个子模块的动画项目中暂时还未遇到过,冲突的概率非常低。
图集合并同步
在UI目录中有一个类特殊的图片,需要做成图集提高游戏渲染性能,在一个游戏项目初期由于UI风格不稳定或使用临时图片,让美术同学经常去合并图集是一个效率较低的事情。因此shawn将需要合并图集的文件放入atlas的子目录中,由程序调用TexturePacker的命令行工具,以atlas子目录为单位生成图集,直接存入客户端模块atlas目录。
图集合并并完全是动态的,shawn编写了一个Node脚本,用于遍历atlas下的子目录文件,生成图集文件,然后再使用grunt-shell插件进行整合,看下面代码:
//TexturePacker图集合并工具 let tpimg = require('../tpimage'); //shell任务 const shellTask = { //TexturePacker合并图片 'tp-img': { command() { //工作路径,资源仓库atlas let cwd = path.join(UI_ROOT_PATH, `${moduleName}/ui/atlas`); //目标路径,图集文件保存到这里 let dst = `./assets/${moduleName}/texture/ui/atlas`; //使用编写的Node脚本生成TexturePacker命令数组 let commands = tpimg.tpdirs({ cwd, dst }); //模拟输出命令 if (pretend) { console.log(`cwd:${cwd}, dst:${dst}`); console.log(commands.join('\n')); return ''; } //返回命令字符串,由grunt-shell插件执行 return commands.join('&&'); } }, }
下面是任务执行时的效果:
两次执行,每一次执行时生成了icon.png、icon.plist,马上再次执行,提示未发生改变没有再重新生成图集,这比我们手动打图或使用Cocos Creator的自动图集效率要高。
资源仓库更新
上面介绍了美术UI、动画、图集等资源的同步,但一个完整的模块资源同步,还需要涉及到对资源仓库的更新,具体操作就是用git或svn将资源仓库更新到最新状态,下面看使用grunt-shell命令更新资源仓库:
//svn更动画资源 'svn-ui': { command: [ `svn up ${UI_ROOT_PATH}/${moduleName}/ui`, `svn up ${UI_ROOT_PATH}/${moduleName}/bg` ].json('&&') }, //svn更新动画资源 'svn-ani': { //如果使用nosvn参数,返回空字符串,跳过svn更新 command: nosvn ? '' : `svn up ${ANI_ROOT_PATH}/${moduleName}/animation`, }
有时会遇到svn暂时不可用,或有同学未安装svn命令行工具,会导致任务失败,可以shawn这里设置了一个nosvn的参数,用于控制是否跳过svn操作,直接返回了一个空字符串。
任务整合
一个子模块完整的资源同步任务大概需要经历下面几个步骤:
我们前面都建立的单个任务,使用grunt.registerTask可以将任意单个任务进行自由组合,看下图:
grunt.registerTask的参数包括:任务名、任务说明、子任务列表,子任务列表是一系列的任务或插件任务字符串的组合,上图中up-hall-svn任务,是由三个shell插件任务组成。
此时我们在项目根目录中,执行:
>grunt up-hall
即可享受到所有美术图片、动画、音效、配置等等资源的同步到我们的客户端hall模块。
我们经常会遇到这样一个场景:
美术同学:“xxx程序我增加了大厅商店道具张图片,你更新一下呢,我想看看效果”。
程序同学:“这几张图需要与策划配置文件配合才能生效,yyy策划你更新下商店配置”。
策划同学:“今天早上一来我就已经更好了,你直接取吧!”。
动画同学:“商店里几个动画特效我也更新了,你也取一下”。
程序同学打开终端,键入:grunt up-hall,屏幕一阵疯狂输闪,涌现出无数文字符号...
,
1、2、3、4、5 ....
五秒钟过去,点击Cocos Creator编译资源,观察控制台,一切ok!
程序同学:“你们使用我的IP:7456自己去看吧!”
美术策划同学,一脸惊讶地看着你,效率这么高…
曾经十几分钟都搞不定的事情,现在几秒就解决了,每天做50遍都不会觉得累!也不需要做50遍,将grunt任务与Cocos Creator插件结合,嵌入到Cocos Creator界面菜单,让 程序员 多休息一会儿吧!
欢迎关注「奎特尔星球」公众号, 欢迎大家投稿 ,来我们一起成长!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。