cocos creator集electron, node.js, vue.js, 浏览器对象, canvas, webGL于一身, 哦看起来还是很强大的.
结构
/assets 根目录下面主要编辑的文件都在这儿
/library, /temp 可以认为都是临时文件, 貌似为了加速编辑器某些处理产生的, 和assets里的资源是匹配关系, 如果匹配出了问题, 这些都删掉, creator会自动再次生成.
/local 编辑器布局, 一般情况下并没有什么用, 貌似删了也没有关系
/settings 构建发布等相关配置, 蛮重要的,要保留.
package.json 也规定了一些重要的信息, 要保留.
运行后还会生成很多.meta文件, 是一个资源文件对应一个.meta, 里面主要是资源文件的uuid和其他一些信息.
assets资源
所以, 主要的编辑工作都是在assets里面. 再说说assets, 文件可以包括:
.fire 场景文件, 规定了场景中各个内容, 也就是creator中层级管理器里面显示的那些东东. 其实是一个json文件.
.js 脚本文件, 当然了, 是JavaScript, 这个后面专门说一下
资源文件:
图片 包括.png, jpg等, 这个不消说, 是在场景中添加到精灵里面的;
粒子 以.plist结尾. plist和json文件作用相同, 只不过是xml形式的. 粒子文件的粒子图片可以是独立图片, 也可以是内嵌在plist中的base64数据.独立图片的话, 需要和plist放在一个目录中. cocos支持的粒子文件在windows上可以用Particle Editor编辑.
字体 可以使用ttf字体或者是位图字体.fnt, windows下使用BMFont制作, MAC下使用glyph designer制作. windows下的制作方法查看这儿
动画 动画目前就我所知, 有三种形式:
- 通过creator编辑的动画, 以.anim结尾, 实际上也是json文件. 这个应该和项目是紧密耦合的, 因为在文件里面规定了frame帧引用的资源是uuid形式的, 而uuid是在项目中生成的.
- spine骨骼动画 是使用spine软件制作的骨骼动画, 收费才能使用, 恩, 因此我也不知道这个文件是什么样子的.
- DragonBones骨骼动画 是使用DragonBones软件制作的骨骼动画, 常被称为龙骨动画, 免费开源, 但似乎以前cocos不支持, 现在貌似也支持的吧太好, 我下载了一个动画加入, 发现骨头缺了一块...不厚道地揣测, 也许是因为龙骨软件的开发商是Egret, 也就是另一个游戏引擎白鹭的开发方, 白鹭引擎貌似现在发展的也不错, 在知乎上还能看到两个引擎的拥护者对喷, 是由于这种竞争关系导致的支持不好吗? 龙骨动画包括三个文件, xxx_ske.json, xxx_tex.json, _tex.png. 在creator中, 将xxx_ske.json拖进层级管理器, 再把_tex.png拖到Dragon Atlas Asset中即可.
- 音乐等其他文件, 目前暂未涉及.
js脚本与nodejs和浏览器js异同
js脚本很大程度上结合了node.js和浏览器环境, 然而又有所不同.
目前发现与nodejs一致的有:
- 可以使用node.js的require加载自定义在assets中的任何js模块.
- js中定义的所有变量var ,function 都只能在本js文件中引用.
- 要让require的方式暴露本模块中的变量, 同nodejs一样,需要通过module.exports的方式.
- 可以使用ES2015语法, 如let, const, 箭头函数等
不一致的有:
- 不能使用node.js自带的模块, 如fs, path等.
与浏览器环境一致的有:
- 包含一个全局变量window, 向window.xxx赋值,可以在任一js中访问. window其实本来是BOM(浏览器对象模型)的一部分, 然而可以在这儿使用.
据说还可以使用npm install安装第三方纯js库使用, 尚未试过.
另外根据帮助文档, cocos creator的项目中也有一个类似于electron的index.html文件, 目前暂未开放出来提前修改.
js脚本运行方式
- 事实上, 在assets目录中的js脚本, 会在游戏一开始加载的时候全部运行一遍, 运行的顺序似乎并没有什么规律. 如果要控制运行顺序参考这儿
- 所以, 在js正文中所书写的内容都会被运行.
- 通过require('js文件名')可以直接引用任意目录下的js 文件, 所以js文件就算在不同的目录下也不能同名.
js脚本与其他资源文件的关联
- 所有的资源是以节点树的方式组织, 有点类似于DOM树, 节点树的在'层级管理器'中显示.
- 在js文件中定义一个cc.Class, 就可以与其他资源节点关联, 每个js文件最多只能有一个cc.Class
- 这时把js拖到资源节点的属性面板中, 在cc.Class的properties中定义的内容可以显示在面板上,
- 如果将内容定义为内置的类型, 则可以将节点拖放到属性面板的该类型上, 在程序中可以对此节点进行引用. 如
cc.Class({
extends: cc.Component,
properties: {
label: cc.Label,
}
},
accessLabel: function(){
// 访问 this.label
}
}
- 通过几个父子方法, 从本节点可以到达任意其他节点. 如果本节点不是一个node,要获取node后才能使用.
this.label.node.parent //父节点
this.label.node.children // 子节点的array
this.label.node.parent.children // 父节点的子节点array. 由于父节点是node,所以可以直接使用node下的元素children
this.label.node.getParent() === this.label.node.parent // true. 目前暂不清楚get方法与直接访问元素是否可能有什么区别.
this.label.node.getChildern() === this.label.node.children //true
this.label.node.getChildByName('childname') // 按名字查找
- 通过修改某个节点的父节点来改变这个节点的位置.
this.label.node.parent = another.node
js脚本对任一资源引用
通过cc.find()方法, 可以从根节点到达任意子节点
cc.find('Canvas/Label') //返回Canvas下的Label节点.
js对某个节点的及其组件component的操作
- 获取到某个节点后, 你就可以对节点下面的属性直接操作.
this.label.node.x = 123 // 其他在属性检查器中看得到的都可以这样操作
- 要操作节点上的某个组件, 使用getComponent和addComponent
this.label.getComponent(cc.Widget).top = 123 // getComponent可以传入一个cc类型
this.label.getComponent('js_file_name') //可以传入字符串, 获取节点上附加的脚本组件
this.label.addComponent(cc.Layout) //也可以通过addComponent添加组件
- 使用new cc.Node()创建新的节点, 还可以通过cc.instantiate克隆一个节点.
var newNode = new cc.Node('newNodeName')
var clonedNode = cc.instantiate(anExistedNode)
- 使用destory()删除一个节点
somenode.destory()
节点生命周期
附加在节点上的js组件,可以获得节点的生命周期变更通知, 并通过回调函数进行操作.生命周期的顺序是:
onLoad
onEnable
start
update, lastUpdate 依次循环
onDisable
onDestroy
对节点的active赋值true/false, 将触发onEnable/onDisable事件
somenode.active = false // onDisable
somenode.active = true // onEnable
- 对节点调用onDestroy, 触发onDisable, onDestory事件
somenode.destroy() // onDisable, onDestory
- 节点destory以后, 可以用isValid判断是否被destory了. 恩, 我认为这个名称按照上面的active命名方式, 应该叫valid..., 要么需要把active改为isActive以表明其boolean身份.
somenode.isValid //true
somenode.destroy()
somenode.isValid //false
- 需要注意的是, 节点生命随着scene的load开始, scene关闭结束. 节点(node)的生命周期和js本身的只执行一次不同, 请注意区分.
js与被附属节点的关联
- 通过this可以获取到当前的js脚本组件, 通过this.node可以获取js脚本所附属的节点.
- 由于properties和自定义function都是通过this来引用的, 所以, 在cc.Class中定义properties和function的时候要注意了, 这两者都不要起一些creator保留的名字, 列了一下, 这样的保留字还是很多的:
__cid__
__classname__
__eventTargets
__instanceId
__onNodeActivated
__preload
__scriptAsset
__scriptUuid
_deserialize
_destroyImmediate
_destruct
_enabled
_getLocalBounds
_id
_instantiate
_isOnLoadCalled
_name
_objFlags
_onPreDestroy
addComponent
constructor
destroy
enabled
enabledInHierarchy
getComponent
getComponentInChildren
getComponents
getComponentsInChildren
isRunning
lateUpdate
name
node
onEnable
onFocusInEditor
onLoad
onLostFocusInEditor
onRestore
playJump
playRun
resetInEditor
schedule
scheduleOnce
start
unschedule
unscheduleAllCallbacks
update
uuid
场景切换与固定部分节点
加载另一个场景
cc.director.loadScene('anotherScene', callSomeFunctionWhenLoaded)
预加载另一个场景
cc.director.preLoadScene('anotherScene', callSomeFunctionWhenPreloaded)
保留一个场景中的节点到下一个场景.
cc.game.addPersistRootNode('nodeName')
取消保留节点
cc.game.removePersistRootNode('nodeName')
排列节点树中的节点
- 前面说过了, 把一个节点下挂到其他节点下,只需要继续改这个节点的parent属性就行了
somenode.parent = someNewParentNode
- 然而, 与兄弟节点间的排序, 该怎么做呢? 可以用父节点的addChild()和removeChild(), 每次addChild()以后, 被增加的节点都会自动加到父节点树的最后面, 也就是场景的最顶端.
var pa = somenode.parent
pa.removeChild(somenode)
pa.addChild(somenode) // 将somenode移动到了父节点树的最后面.
值得注意的是, 虽然可以用pa.children直接访问到children的array, 然而此array做pop/push/shift/unshift等操作, 似乎并不会对节点树有任何影响. 因此还是乖乖用addChild和removeChild吧!
* 还可以在本级使用setSiblingIndex(index), 可以调整自己在本级中的位置为index
somenode.getSiblingIndex() // 例如, 0, 是在最低端
somenode.setSiblingIndex(2) // 例如, 2, 上移两层.
发射和监听事件
- 只能从节点上发送和监听事件
- 通过在本节点上使用emit发射的事件, 只能被本节点接收到.
- 通过在本节点上dispatchEvent发射的事件, 通过冒泡的方式逐层被上级节点接收到.