作者:异名
用代码是实现想法
努力成长为一个可靠的工程师
写在前面
因为公司的业务需求,近期学习了CocosCreator
这款游戏引擎的开发,也基于此上线了一款游戏,因此写这系列文章记录一下我从入门到项目发布的学习过程。
相对于web开发,像CocosCreator
和egret
这种界面化的游戏引擎最大的区别就是可视化的UI编辑,以及像动画编辑、物理引擎、资源管理系统等一系列高度封装集成的工具集。所以第一篇文章我主要会介绍一下我从web端开发转向游戏开发这个过程中,我对cocos的工作流程的一些认识。尽管文档上有介绍但是新手上路,很多东西一开始被我忽略掉了,随着项目的进展,我断断续续地从文档、社区中学到了一些能提高效率的小方法和配置,在此记录一下,主要给其他新人做参考
因为游戏的界面编辑都是通过编辑器来完成的,所以编辑器的一些基本功能和操作说明我们需要通过文档去理解熟悉,我这里会记录几个我新建一个项目必须会用的设置
配置项目设置
在开始做项目前,别忘了要在项目-项目设置
中先设置下面几个配置,后续的新建场景都会默认使用这些配置,后面就不需要每建一个场景都要设置一下了
-
初始预览场景(指定某一个场景/当前打开场景),我一般设置后者
-
设计分辨率,引擎默认的是960*640
-
适配模式(fit-height/fit-width)
调整编辑器布局
在工作中,因为我们绑定资源、脚本和变量的过程都是通过把它们拖拽到属性面板来实现的,而引擎的默认启动界面把属性面板放到最右侧,那当我们需要绑定操作的时候就需要把物体从最左侧面板拖拽到最右侧面板,其实这段超长的操作距离是非常影响效率的,所以大部分的开发者其实都会选择经典布局,像下图这样,把属性面板通过拖拽放在左侧和层级管理器放在一起,这样绑定变量和资源的操作效率就会相对高一点
熟悉常用的界面快捷键
CocosCreator
的快捷键支持是比较弱的,但是它还是提供了几个高频操作的快捷键,通过这四个快捷键我们能快速切换这四个高频工具,很大地提高效率
-
W
移动工具 -
E
旋转工具 -
R
缩放工具 -
T
矩形变换工具
还有就是用过3D模式的都知道,在3D模式下调整相机视角和位置的操作是相对来说比较困难的,之前的项目中也实现过一个2.5D的场景,所以也这里也分享两个快捷键,一个就是调整物体的时候,点击住屏幕,在视图左上角会出现wasd
(上下左右)的提示,可以通过键盘上wasd
这四个按键来微调方向,相对来说会便捷很多
还有一个就是调整相机的角度,如果我们根据预览效果去调整相机的位置,实际操作起来有一定的麻烦,有一个隐藏的组合快捷键control+shift+f
可以把我们场景编辑器的画面同步成相机的预览,它可以让我们更直观得调整相机效果
和web端的开发不一样,cocos的UI是不用写样式的,界面上所有的元素都是用图片堆积起来的,对我来说这个转变过程挺有意思的,把样式编写去掉了可以省掉我们一些布局的时间,我在开发项目过程中也发现了一些比较好的实践方法
精准还原设计稿的窍门
因为我们在可视化的编辑器中都是通过拖拽来实现图片的定位的,我们不写样式文件。但是我在第一个项目的时候思维又还没转换过来,还是习惯web端那种开发模式,我每次布局的时候都会去翻一下设计稿,测一下物体的位置,然后再在属性面板上输入正确的position信息,这样做当然是没问题的,但是效率真是太低了,后来我看到一位前辈的开发过程后才恍然大悟,改正过来。他是这样做的,在设计稿那里截一张完整的界面图,然后把它当成一个底层的节点,这样子我们就有一个和设计稿一模一样的背景图了,我们布局的时候再把一个个物体放到它正确的位置上,完事之后只需要把那个底层的节点删除就可以了,这样子布局的的效率提升可是质的飞跃。
把UI按模块拆分管理
在项目中,我们的一个场景里面的内容可能会很多,比如我这个场景里面有N个弹窗,如果我们把所有的弹窗的内容按他们的真实位置塞到canvas
节点中,那真的是一件很糟糕的事情,因为一个个物体们会重叠在一起,后期我们将很难进行选中操作。所以我们在做UI管理的时候需要一个窍门就是把UI按模块拆分,并且移位管理,如下图
我并不会把弹窗一和弹窗二的内容叠加到主界面上,当然一开始我们可以这样做,因为我们可能需要根据背景图来确定每个物体的位置,但是当我们完成布局之后,可以像我一样把他们的父节点移出主界面之外,我这里的父节点是通过widget做了拉伸的,完全适配窗口的大小,当父节点被移出其实就是position做了偏移,后续在代码里面控制弹窗出现的时候,我们只需要多写一行代码,把父节点的position设置为(0,0)
就可以了。这样子在后续维护的过程中我们的UI界面也能一目了然。如果用了widget,也别忘了在代码调用的时候去手动更新widget的位置:
let widget= this.mapDlg.getComponent(cc.Widget);
widget.right = 0;
widget.left = 0;
widget.updateAlignment();
利用好自定义控件
自定义控件库其实就是给常用的prefab增加了一个入口,当然可以直接把prefab拖拽到层级管理器里面直接用,但是其实还是一个效率的问题。当你的prefab多了之后,先不说你要去翻资源管理器里面的文件夹,当你打开prefab所在的文件夹之后,一眼看过去是比较难找到自己需要的控件的,自定义控件不仅仅额外给了一个入口,而且它可以给prefab增加一个图标和控件名称,对高频的复用控件的使用来说,这真的太有必要了,打开面板一下就能找到你所需要的控件然后直接拖拽使用了
代码开发配置Vscode
虽然这块文档里面有写,但是因为我是从写web端转来做游戏的,以为自己很熟悉vscode,又因为项目的原因要急于上手就选择性地忽略了这一块,仅仅只使用了代码提示这个工作流,等到看到社区内的前辈们的日常操作之后,才发现配置好vscode的工作流其实也是一大效率神器,它包括四块东西
-
代码智能提示
-
设置文件搜索范围
-
手动触发编译
-
配合Chrome debug插件在编译器内debug
代码提示和文件搜索我就不提了。在正常情况下我们修改了代码,只有回到cocos界面才能触发项目实时热更新,而我们在vscode上配置好编译的task,并且设置启动task的快捷键,我设置的快捷键是cmd+r
,我们就可以在vscode上通过快捷键触发项目的热更新,它可以让我们在写代码的时候更专注在代码上;具体的配置步骤可以查看文档
// .vscode/tasks.json
// 配置vscode任务
{
"version": "2.0.0",
"tasks": [
{
"label": "compile",
"command": "curl",
"args": ["http://localhost:7456/update-db"],
"type":"shell",
"isBackground": true,
"group": "build",
"presentation": {
"reveal": "always"
},
}
]
}
// User/keybindings.json
// 配置任务启动快捷键
[
{
"key": "cmd+r",
"command": "workbench.action.tasks.runTask",
"args": "compile"
}
]
还有一个就是ChromeDebug插件,这块的流程只在1.9.0
的文档中才有,后续的文档中虽然能搜索出来,但是无法阅读,它支持你在vscode中唤醒Chrome,并在vscode中进行debug,也是一大效率神器
参数绑定
在处理复杂界面的时候,因为页面上的元素较多,就算我们尽可能地拆分脚本,但是在一个脚本里面需要绑定的变量还是不可避免的增多,然后代码里面就会有一大串@property
声明的变量,所以新手们需要充分利用的类型,比如我有10个只是单纯用于的显示和隐藏的node节点,就可以通过声明一个node数组节点来绑定,这样子我在代码里面显式声明的变量就会少很多????
还有一种常见的情况就是一个物体它有两种甚至多种状态,当我刚上路的时候我对cocos的内置对象还不熟,然我因为我们界面上的基本组成单元是sprite图,我就做了一个很蠢的操作就是有多少状态我就创建多少sprite节点,状态切换的时候就切换相应节点的显隐。但是这种情况下其实好的做法是通过一个spriteFrame的数组变量去存储不同状态的spriteFrame然后状态切换的时候去更新相应的spriteFrame:
@property([cc.SpriteFrame])
btnStatus: cc.SpriteFrame[] = [];
// 切换不同的状态
this.node.getComponent(cc.Sprite).spriteFrame = this.btnStatus[0];
this.node.getComponent(cc.Sprite).spriteFrame = this.btnStatus[1];
this.node.getComponent(cc.Sprite).spriteFrame = this.btnStatus[2];
当然这样也不是最佳实践,因为后续接手我们代码的人通过代码根本就看不出数组不同下标对应的spriteFrame是哪一个,他还得通过界面去查看绑定的资源。所以最佳的实践应该是把该一个物体不同状态图片生成图集,图集里面的每个图片可以精确命名,当需要切换状态的时候,我们就可以通过精确的名称获取到对应的spriteFrame,虽然这样子我们就需要多维护一个图集,但是它是一个相对更规范的实践方式
@property(cc.SpriteAtlas)
spacemanAtlas: cc.SpriteAtlas = null;
// 切换不同的状态
this.body.getComponent(cc.Sprite).spriteFrame = this.spacemanAtlas.getSpriteFrame("take_off_state");
this.body.getComponent(cc.Sprite).spriteFrame = this.spacemanAtlas.getSpriteFrame("ready_state");
this.body.getComponent(cc.Sprite).spriteFrame = this.spacemanAtlas.getSpriteFrame("rest_state");
this.body.getComponent(cc.Sprite).spriteFrame = this.spacemanAtlas.getSpriteFrame("fire_state");
第三方工具
我目前用到的第三方工具主要有这下面几个
利用 Texture Packer 生成图集
虽然引擎在打包阶段给我们提供了自动合图功能,但是为方便资源的维护、代码变量的管理以及后面性能优化上很重要的drawcall优化处理等等,我们还是很有必要在开发阶段就要有意识地去做图集管理。
ShoeBox
异常强大的ps插件,我目前用得最多的就是拆分图集、gif图拆解、生成位图字体、合成gif图,它也可以合成图集,但是我觉得Texture Packer
在这方面更好维护。拆分图集、gif图拆解这两个功能我主要用来获取一些游戏的公共资源,最简单的像一些金币的动画,我需要它的序列帧,如果设计师那边有现成可用的固然好,如果没有的话特意让人家去做一份也没有很大的必要,我比较常去爱给网翻一些CC0版权的资源来用。比如下面我需要用到一张合图里面的某一张图片,我就会利用ShoeBox拆分合图
游戏当中不可避免的会用到一些卡通点的字体,比如数字由0到9组成,是我们设计师独出心裁设计的,需要应用到游戏当中。应该会有部分的前端像我一样是做web开发的,以前没有接触过游戏开发,那要实现这个需求就一脸懵逼,总不能让我用一个个sprite去代替吧。其实还真的是,位图字体的做法就是将会使用到的每个文字对应的字体做成位图,生成文字内容和位图的映射,游戏中动态的调用显示位图。这样即在游戏中呈现了漂亮的字体,又节省了资源。位图字体是由一张png的图片集和一个fnt配置文件组成的。其中png图片集中包括了所有会使用到的位图,fnt配置文件里描述了这些位图应该怎么从png图片集中切分出来。下面就是利用ShoeBox制作位图字体的过程
Sketch
我司的设计工具,之前做web端开发的时候我和设计师的交付都是通过sketch的导出的网页或者通过蓝湖,但是做了游戏项目之后,我发现开发这边对稿子的调整还是挺多的,当稿子已经基本交付过来之后,我们这边有小的调整的话能够自己搞定的话,也没必要再去麻烦人家,所以像切图、去色、通过变换工具减少九宫格图片的多余面积等一些基本又高频的操作能不麻烦设计师就自己搞定吧,同时还有一些特殊交互动画需要对切图有一定的拆分处理,如果自己能搞定的话也能省去沟通以及交付的成本
字蛛
这个其实用得相对低频,还是回到游戏中的字体需求,如果我希望界面上的用的字体比较符合整个UI的风格,比如我要一个圆体,但是用户的系统字体默认是那种瘦体,UI上看起来肯定是体验不好的,设计师同事也不可能给每个字都做成位图字的,圆体中文字体库起码几M以上,我们又不可能全部当成资源引入,所以我们需要做字体库的切割,我们的游戏里面用到哪些字就用只提取那些字,切割后的字体库只有几k,那就可以满足我们的生产环境使用了,具体的用法可以去官网查看。因为每次都需要启动服务,我很早之前嫌麻烦做过一个小工具(Mac下载,window下载),下面基于它做一个演示
总结以上就是这篇文章的全部内容了,主要记录一下我从新手开始做游戏开发后,一些后知后觉才学会的能够提高工作效率的工作流和配套小工具,因为是在公司内做第一个螃蟹的人,团队内部还没有这块的沉淀,加上这些小经验要么在文档中被我忽略而过,要么就零零散散在论坛中,所以后面会尝试做一些小结沉淀下来,后面我如果发现还有别的能提高效率的工作流程我也会在这里更新。除了上面这些新手上路的絮絮叨叨外,还有一些在真实项目中遇到的坑以及解决方案,我也觉得比较有意思,后面也会尝试做成一系列的总结沉淀下来,让我们不见不散
一个游戏多份收益,助力开发者技术精进、副业变现,我是工程师经纪人张晓衡,欢迎加我微信!
我就知道你“在看”▼