Cocos Creator学习笔记
一直听说cocos做2d很好,但是从未尝试过.公司打算从egret转型到cocos creator,所以我花了两周时间,两个引擎都学习尝试了一下,并且完成一个小项目的开发.
下面我会简单讲讲在学习和开发过程中用到的知识点和踩过的坑,并且从头开始使用fgui和cocos开发一个小游戏《围住神经猫》
Egret的学习笔记可以参考我的这篇文章Egret《围住神经猫》笔记
0.踩过的坑
刚刚学完egret,我就马不停蹄的开始学习cocos creator(以下简称cocos).egret是一个开发页游的引擎,用起来更像写前端,加上做的这个项目《围住神经猫》是一个比较简单的游戏,所以游戏间的逻辑完全使用事件串起来,一点没用到生命周期回调,也没有基于场景和节点的开发思想在里面,所以完全不能和之前学的unity联系起来.
cocos其实和unity在思想上是很像的(unity的GameObject就对应cocos的Node),但是我最开始一直用egret的开发思维带入cocos,结果就是非常痛苦,写出来的东西不管怎么调都完全跑不通,其实是因为没有用基于节点的思维去做开发.
说到底还是没有学扎实,之前用unity一直是静态场景拖拽GameObject来绑定和获取组件,没有尝试过在代码中进行这些操作,这样做其实是不利于理解引擎底层逻辑的.现在我已经能够将两种思维融会贯通运用在cocos中了.
1.知识点
1.1cocos家族
cocos和Unity不同,它有不同的引擎分支,产品之间差异很大,现在用的最多的应该是Cocos-2dx和Cocos Creator,我学习的是Cocos Creator,以下简称cocos代指的就是它.
1.2常驻节点
开发时希望有一些全局的数据能够被任何模块访问到,但是一般的场景切换会销毁原先的场景导致数据被清除,如果不销毁场景,占用资源就太多了.有一个方法是选定某个节点,将其作为常驻节点,这样,在一个场景设置的节点,在其他场景也可以访问到,并且不会被清除,也无需重新构造.
在Unity中,有一个方法是使用PlayerPref做数据持久化,这个方法会把数据持久化到注册表中.不过这已经超出了"全局数据"的需求
以下是cocos官方文档中的内容
场景切换时,所有界面都会被销毁。如果不想被销毁,需要创建出界面后,把根节点设置为常驻,并且切换场景前,确保关闭界面。
cc.game.addPersistNode(view.node);
1.3FairyGUI
简称fgui,是一套gui设计解决方案,为了尽可能实现gui和代码分离,方便设计师使用而出现的工具.我没有用过cocos原生的ui,只说fgui,我觉得很难用,它似乎对Unity支持的特别好,但是现在Unity原生的UGUI已经做的很好用了,又是人家官方的前途一片光明,为什么还要用fgui呢.但是cocos的gui显然做的一般,所以fgui还是要用.
一句话总结fgui的工作流:绘制gui组件->打包成各种引擎可以使用的包(在这里是cocos)->在引擎中使用代码加载和获取组件. 具体怎么绘制要看fgui的编辑器教程,教你每个组件干什么有什么用,如何在引擎中使用绘制的组件哟啊看fgui的sdk教程,会教你fgui给各个引擎的API,但是很少,也有一部分具体组件的API是在编辑器教程的.但是它的教程写的很一般,对于非Unity的引擎,有很多东西都没有讲,所以用起来比较难受
加载fgui组件的核心api是fgui.UIPackage.createObject(packageName:string,objectName:string),一般要用到的资源会在fgui中做成组件,加载会直接加载组件,所以方法后面一般会加上.asCom.这个方法似乎是异步的,注意调用顺序,否则可能会获取不到当前的组件.
1.4事件系统
cocos的事件系统是基于节点的,而fgui的事件系统又是基于cocos的,所以二者在逻辑上相通,看看官方文档是怎么说的吧:
FairyGUI直接使用了Creator的事件系统,所以GObject.on/off其实是通过GObject.node.on/off实现的,也就是可以通过GObject.node进行任何事件的操作,包括自定义的事件。在事件回调中,cc.Event中的currentTarget反映的是这个事件是由哪个node派发的,如果要获得这个node对应哪个GObject,可以用这样的方法:
aObject.on(someEventName, this.onHandle, this); onHandle(evt:cc.Event) { cc.log(evt.currentTarget); //node对象 cc.log(fgui.GObject.cast(evt.currentTarget)); //gobject对象 }
除了基于节点处理事件以外,cocos和fgui的事件系统和常规的事件系统没有什么区别:发送事件是一个向上级抛出信息的过程,在父级对子物体加监听可以实现事件的传递.想要传递事件,我们在一个对象内部发送事件,然后在它的父对象中通过引用对子对象加监听,这样父对象就可以根据子对象的事件来处理逻辑,如果要多级传递,那么就在父对象的事件处理逻辑中发送消息给祖父对象,以此类推.
1.5 fgui.List
之前学习egret的时候已经学习了eui.list,当时是自己封装的呈示单元,这次想试一下在fgui编辑器界面指定现有的组件作为呈示单元,结果发现完全不能移动单元组件,根本就做不了六边形网格.尝试将组件的图片移动位置来实现偏移,结果list监听点击的判定还在单元组件上,点击图片不等于点击组件,这下更寄了.无奈,还是重新封装了呈示单元.然后使用addChild一个个添加节点.
不过惊喜的是fgui.List的监听居然是冒泡事件,可以呈示单元发送的事件可以穿透封装类直接到List上面,这样就不用单独加监听和重写事件了
总结下来两个要点:list的监听是冒泡事件,list的呈示单元受限于布局,不能调整位置
2.从0开始的cocos小游戏开发
2.0原先的基础
本次实战是将原先写的Egret小游戏项目《围住神经猫》移植到cocos creator上.cocos和Egret在开发逻辑上有很多不同,所以代码也有了架构上的更改和优化,比如:
脚本类基本上全部继承cc.component,基于节点进行开发
使用多场景,设计场景切换的逻辑
使用一个Resource_Loader类管理资源和场景,实现动态加载
因为一直在进步,编码规范上有了进步,代码质量提升,比如说使用公司要求的缩进规范,整合重复且仅仅有细微差别的代码,拆解逻辑结构,使得代码逻辑更合理
原项目文档:Egret学习笔记
2.1FGUI设计UI组件
打开fgui,新建项目,项目名称位置随意,fgui的项目和cocos项目是完全独立的.
把你的素材拖进fgui编辑器,新建若干个需要的组件,开始设计.记得将需要被代码获取的组件和子组件规范命名,方便一会绑定.

2.2 导出UI组件到cocos
在左边资源栏将需要使用的组件设置为导出,点击发布设置(就是三个小飞机最右边那个)设置导出的信息地址,一般来说我们会把包放到cocos目录的asset/resources地址下,因为resources这个文件夹是可以被cocos自动获取的.设置完成后点击导出,就能在cocos下面看到一个文件夹里放着你的素材,素材以图集的形式保存,通过fgui提供的API加载特定组件,这样可以减少drawcall

2.3 获取组件
创建出几个主要场景的管理类,在类中定义组件,并获取组件,这里以游戏胜利的对话框为例:
这个类中包含对话框组件canvas_success和它其中的按钮以及文本

初始化时,加载对应的组件.
注意,这里能直接使用createObject是因为之前已经加载包了,加载包的代码是
fgui.UIPackage.loadPackage("Package/MainPackage", function(err) {
});//花括号中写回调函数

2.4 编写UI事件
Resource_Loader类不仅负责加载用到的几个场景类,还负责管理它们,场景动态加载,至于加载哪些场景,数据类中的场景名称会提供
设置几个Prefab属性值,这些Prefab是挂载了各自场景管理脚本的场景管理器节点

初始化时,加载对应节点,给对应的节点添加监听

提供一些UI逻辑处理的方法

在各自的场景管理类中添加发送事件的方法,并且对各自的子UI组件加监听,就像上面的Scene_Success一样
2.5 使用fgui.GList实现六边形点阵
用Game_Node封装呈示单元,构造Game_Node的时候绑定呈示单元
提供一些属性和方法用于逻辑交互

用一个静态类管理这些数据

在游戏界面管理器中,使用addChild方法直接向List中添加这些节点,更改Game_Node中组件的位置:

给List添加监听,注意,这些监听是可以冒泡的,所以他们能直接监听Game_Node.loader

2.6游戏逻辑代码
主要在Scene_Game中
下面是刷新状态的代码,由点阵中的点被点击后发出事件来触发,具体包括了猫的逻辑和数据变化的逻辑,和我在egret项目中的很像

2.7猫逻辑代码
不赘述了,和egret项目很像
2.8资源加载和管理类
预置几个预制体的位置,在cocos里面挂载这些节点,通过实例化的方式获取节点和场景脚本



2.9跨场景全局数据类
记录各种用得到的静态数据,设置为全局节点使得所有场景中都可以访问而不需要重复设置.

最终效果不再展示,和egret版本的表现是一样的.