CocosCreator入門
推薦閱讀:
最近公司要換遊戲引擎了,想象當初從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 方法呼叫前執行,做一些初始化相關的操作。
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
}