推荐阅读:
- 我的CSDN
- 我的博客园
- QQ群:704621321
最近公司要换游戏引擎了,想象当初从unity到egret,再到接下来的cocos,刚把新引擎的坑踩完就不用了,哎,就当学习丰富自己了,先来说说cocos的代码格式以及一些基础API的使用吧。
基本格式:
cc.Class({
extends: cc.Component,
properties: {
},
// use this for initialization
onLoad: function () {
},
// called every frame, uncomment this function to activate update callback
update: function (dt) {
},
});
1.属性中声明某个Label,类型指定为cc.Label,而不是cc.Node。
例如:
scoreDisplay: {
default: null,
type: cc.Label
},
2.属性中音效声明,不需要指定类型,只需要指出URL,url:cc.AudioClip。
例如:
scoreAudio: {
default: null,
url: cc.AudioClip
}
3.播放音效:
cc.audioEngine.playEffect(this.jumpAudio, false);
4.暂存对对象的引用
// 暂存对脚本 GameManager 对象的引用
properties: {
gameManager: {
default: null,
serializable: false
}
}
可以通过this.gameManager.player.getPosition();获取GameManager中player属性的位置。
总结:属性中的常用参数
default: 设置属性的默认值,这个默认值仅在组件第一次添加到节点上时才会用到
type: 限定属性的数据类型,详见 CCClass 进阶参考:type 参数
visible: 设为 false 则不在 属性检查器 面板中显示该属性
serializable: 设为 false 则不序列化(保存)该属性
displayName: 在 属性检查器 面板中显示成指定名字
tooltip: 在 属性检查器 面板中添加属性的 Tooltip
注意:数组的 default 必须设置为 [],如果要在 属性检查器 中编辑,还需要设置 type 为构造函数,枚举,或者 cc.Integer,cc.Float,cc.Boolean 和 cc.String。
例如:
properties: {
names: {
default: [],
type: [cc.String] // 用 type 指定数组的每个元素都是字符串类型
},
enemies: {
default: [],
type: [cc.Node] // type 同样写成数组,提高代码可读性
},
}
5.计算两点的距离:cc.pDistance()
6.销毁某个节点:this.node.destory() ;
7.场景资源的延迟加载:如果选项开启,则这个场景直接或间接依赖的所有贴图、粒子和声音都将被延迟到场景切换后才加载,使场景切换速度极大提升。玩家进入场景后可能会看到一些资源陆续显示出来,并且激活新界面时也可能会看到界面中的元素陆续显示出来,因此这种加载方式更适合网页游戏。
8.Cocos Creator 可以使用的图片格式,目前包括 JPG 和 PNG 两种。
9.节点的激活与关闭
this.node.active = true;
this.node.active = false;
10.Cocos Creator目前提供给用户的生命周期回调函数主要有:
onLoad:在组件首次激活时触发,会在任何 start 方法调用前执行,做一些初始化相关的操作。在 onLoad 阶段,保证了你可以获取到场景中的其他节点,以及节点关联的资源数据
start:组件第一次激活前,也就是第一次执行 update 之前触发。初始化一些中间状态的数据,这些数据可能在 update 时会发生改变,并且被频繁的 enable 和 disable
update
lateUpdate
onDestroy
onEnable
onDisable
11.加载场景:
cc.director.loadScene("MyScene");
12预加载场景:后台静默加载新场景,并在加载完成后手动进行切换。那就可以预先使用 preloadScene 接口对场景进行预加载:
cc.director.preloadScene("table", function () {
cc.log("Next scene preloaded");
});
合适的时间调用 loadScene, 就可以真正切换场景。
cc.director.loadScene("table");
13.常驻节点:在场景切换时不被自动销毁,常驻内存。
cc.game.addPersistRootNode(myNode);
这样挂在上面的组件都可以在场景之间持续作用,我们可以用这样的方法来储存玩家信息,或下一个场景初始化时需要的各种数据。
取消一个节点的常驻属性:
cc.game.removePersistRootNode(myNode);
14.两种资源:Asset和Raw Asset
Asset:cc.SpriteFrame, cc.AnimationClip, cc.Prefab 等资源都属于 Asset
定义一个 Asset 属性:
cc.Class({
extends: cc.Component,
properties: {
spriteFrame: {
default: null,
type: cc.SpriteFrame
},
}
});
Raw Asset:图片(cc.Texture2D),声音(cc.AudioClip),粒子(cc.ParticleAsset)等资源都是 Raw Asset
定义一个Raw Asset 属性:
cc.Class({
extends: cc.Component,
properties: {
textureURL: {
default: "",
url: cc.Texture2D
}
}
});
15.加载释放Asset资源
动态加载资源:必须放置在 resources 文件夹或它的子文件夹下
加载那些位于 resources 目录下的 Asset:cc.loader.loadRes
// 加载 Prefab
cc.loader.loadRes("test assets/prefab", function (err, prefab) {
var newNode = cc.instantiate(prefab);
cc.director.getScene().addChild(newNode);
});
// 加载 SpriteAtlas(图集),并且获取其中的一个 SpriteFrame
// 注意 atlas 资源文件(plist)通常会和一个同名的图片文件(png)放在一个目录下, 所以需要在第二个参数指定资源类型
cc.loader.loadRes("test assets/sheep", cc.SpriteAtlas, function (err, atlas) {
var frame = atlas.getSpriteFrame('sheep_down_0');
sprite.spriteFrame = frame;
});
图片设置为 Sprite 后,将会在 资源管理器 中生成一个对应的 SpriteFrame。但如果直接加载 test assets/image,
得到的类型将会是 cc.Texture2D。必须指定第二个参数为资源的类型,才能加载到图片生成的 cc.SpriteFrame:
// 加载 SpriteFrame
var self = this;
cc.loader.loadRes("test assets/image", cc.SpriteFrame, function (err, spriteFrame) {
self.node.getComponent(cc.Sprite).spriteFrame = spriteFrame;
});
释放资源:cc.loader.releaseRes
cc.loader.releaseRes("test assets/image", cc.SpriteFrame);
cc.loader.releaseRes("test assets/anim");
16.加载Raw Asset资源
动态加载资源:可以直接使用 url 从远程服务器上加载,也可以从项目中动态加载。对远程加载而言,使用 cc.loader.load 即可。对项目里的 Raw Asset,加载方式和 Asset 一样:
// 加载 Texture,不需要后缀名
cc.loader.loadRes("test assets/image", function (err, texture) {
...
});
17.上面介绍的加载方式都是对于单个资源,如果是需要加载相同路径下的多个资源,需要使用:cc.loader.loadResDir
// 加载 test assets 目录下所有资源
cc.loader.loadResDir("test assets", function (err, assets) {
// ...
});
// 加载 sheep.plist 图集中的所有 SpriteFrame
cc.loader.loadResDir("test assets/sheep", cc.SpriteFrame, function (err, assets) {
// assets 是一个 SpriteFrame 数组,已经包含了图集中的所有 SpriteFrame。
// 而 loadRes('test assets/sheep', cc.SpriteAtlas, function (err, atlas) {...}) 获得的则是整个 SpriteAtlas 对象。
});
18.加载远程贴图资源:加载用户头像等或如果用户用其他方式下载了资源到本地设备存储中。 cc.loader.load
// 远程 url 带图片后缀名
var remoteUrl = "http://unknown.org/someres.png";
cc.loader.load(remoteUrl, function (err, texture) {
// Use texture to create sprite frame
});
// 远程 url 不带图片后缀名,此时必须指定远程图片文件的类型
remoteUrl = "http://unknown.org/emoji?id=124982374";
cc.loader.load({url: remoteUrl, type: 'png'}, function () {
// Use texture to create sprite frame
});
// 用绝对路径加载设备存储内的资源,比如相册
var absolutePath = "/dara/data/some/path/to/image.png"
cc.loader.load(absolutePath, function () {
// Use texture to create sprite frame
});
19.输入事件:键盘、设备重力传感器此类全局事件是通过函数 cc.systemEvent.on(type, callback, target) 注册的。
type 类型有:
cc.SystemEvent.EventType.KEY_DOWN (键盘按下)
cc.SystemEvent.EventType.KEY_UP (键盘释放)
cc.SystemEvent.EventType.DEVICEMOTION (设备重力传感)
键盘事件:
事件监听器类型:cc.SystemEvent.EventType.KEY_DOWN 和 cc.SystemEvent.EventType.KEY_UP
事件触发后的回调函数:callback(event);
cc.Class({
extends: cc.Component,
onLoad: function () {
// add key down and key up event
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
},
onDestroy () {
cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
},
onKeyDown: function (event) {
switch(event.keyCode) {
case cc.KEY.a:
console.log('Press a key');
break;
}
},
onKeyUp: function (event) {
switch(event.keyCode) {
case cc.KEY.a:
console.log('release a key');
break;
}
}
});
20.重力传感事件
事件监听器类型:cc.SystemEvent.EventType.DEVICEMOTION
事件触发后的回调函数:callback(event);
cc.Class({
extends: cc.Component,
onLoad () {
// open Accelerometer
cc.systemEvent.setAccelerometerEnabled(true);
cc.systemEvent.on(cc.SystemEvent.EventType.DEVICEMOTION, this.onDeviceMotionEvent, this);
},
onDestroy () {
cc.systemEvent.off(cc.SystemEvent.EventType.DEVICEMOTION, this.onDeviceMotionEvent, this);
},
onDeviceMotionEvent (event) {
cc.log(event.acc.x + " " + event.acc.y);
},
});
21.动作系统
// 创建一个移动动作
var action = cc.moveTo(2, 100, 100);
// 执行动作
node.runAction(action);
// 停止一个动作
node.stopAction(action);
// 停止所有动作
node.stopAllActions();
cc.moveTo 用来移动节点到某个位置;cc.rotateBy 用来旋转节点一定的角度;cc.scaleTo 用来缩放节点。
顺序动作 cc.sequence 顺序动作可以让一系列子动作按顺序一个个执行。
// 让节点左右来回移动
var seq = cc.sequence(cc.moveBy(0.5, 200, 0), cc.moveBy(0.5, -200, 0));
node.runAction(seq);
同步动作 cc.spawn 同步动作可以同步执行对一系列子动作
// 让节点在向上移动的同时缩放
var spawn = cc.spawn(cc.moveBy(0.5, 0, 50), cc.scaleTo(0.5, 0.8, 1.4));
node.runAction(spawn);
重复动作 cc.repeat 重复动作用来多次重复一个动作
// 让节点左右来回移动,并重复5次
var seq = cc.repeat(cc.sequence(cc.moveBy(2, 200, 0),cc.moveBy(2, -200, 0)), 5);
node.runAction(seq);
永远重复动作 cc.repeatForever 让目标动作一直重复,直到手动停止
// 让节点左右来回移动并一直重复
var seq = cc.repeatForever(cc.sequence(cc.moveBy(2, 200, 0),cc.moveBy(2, -200, 0)));
速度动作 cc.speed 速度动作可以改变目标动作的执行速率,让动作更快或者更慢完成
// 让目标动作速度加快一倍,相当于原本2秒的动作在1秒内完成
var action = cc.speed( cc.spawn( cc.moveBy(2, 0, 50),cc.scaleTo(2, 0.8, 1.4)), 0.5);
node.runAction(action);
22.计时器
//开始一个计时器
component.schedule(function() {
// 这里的 this 指向 component
this.doSomething();
}, 5);
Component 中所有关于计时器的函数:
schedule:开始一个计时器
scheduleOnce:开始一个只执行一次的计时器
unschedule:取消一个计时器
unscheduleAllCallbacks:取消这个组件的所有计时器
23.使用统一的控制脚本来初始化其他脚本
// Game.js
const Player = require('Player');
const Enemy = require('Enemy');
const Menu = require('Menu');
cc.Class({
extends: cc.Component,
properties: {
player: Player,
enemy: Enemy,
menu: Menu
},
onLoad: function () {
this.player.init();
this.enemy.init();
this.menu.init();
}
});
其中在 Player.js, Enemy.js 和 Menu.js 中需要实现 init 方法,并将初始化逻辑放进去。这样我们就可以保证 Player, Enemy 和 Menu 的初始化顺序。
24.对象池
假如玩家在一关中要杀死 100 个敌人,但同时出现的敌人不超过 5 个,那我们就只需要生成 5 个节点大小的对象池,然后循环使用就可以了。
//初始化对象池
properties: {
enemyPrefab: cc.Prefab
},
onLoad: function () {
this.enemyPool = new cc.NodePool();
let initCount = 5;
for (let i = 0; i < initCount; ++i) {
let enemy = cc.instantiate(this.enemyPrefab); // 创建节点
this.enemyPool.put(enemy); // 通过 putInPool 接口放入对象池
}
}
//...
//从对象池请求对象
// ...
createEnemy: function (parentNode) {
let enemy = null;
if (this.enemyPool.size() > 0) { // 通过 size 接口判断对象池中是否有空闲的对象
enemy = this.enemyPool.get();
} else { // 如果没有空闲对象,也就是对象池中备用对象不够时,我们就用 cc.instantiate 重新创建
enemy = cc.instantiate(this.enemyPrefab);
}
enemy.parent = parentNode; // 将生成的敌人加入节点树
enemy.getComponent('Enemy').init(); //接下来就可以调用 enemy 身上的脚本进行初始化
}
//...
//将对象返回对象池
// ...
onEnemyKilled: function (enemy) {
// enemy 应该是一个 cc.Node
this.enemyPool.put(enemy); // 和初始化时的方法一样,将节点放进对象池,这个方法会同时调用节点的 removeFromParent
}