05_scripting/01_player_control
01_player_control
NodeArray(节点数组)
properties: {
nodeList: {
default: [],
type: [cc.Node] 声明数组对象:
}
},
// use this for initialization
onLoad: function () {
var self = this;
this.inervalId = setInterval(function () { //计时器
self.toggleNodesVisibility();
}, 1000);
},
onDestroy: function () {
clearInterval(this.inervalId);
},
toggleNodesVisibility: function() {
console.log('toggle visibility');
for (var i = 0; i < this.nodeList.length; ++i) {
this.nodeList[i].active = !this.nodeList[i].active;
}
}
NonSerialized(非序列化)
对象添加文本对象可以实现赋值操作。
ReferenceType(引用类型)
this.myComponent.getPower().toString();
myComponent 为当前脚本下的声明对象,getPower是myComponent下的挂载脚本,toString是getPower脚本中的方法,这样的掉用可以获取(引用)到其他脚本的数据。
ValueType(值类型)
properties: {
// number
myNumber: {
default: 0,
type: cc.Integer
},
// string
myString: {
default: 'default text',
},
myVec2: {
default: cc.Vec2.ZERO,
},
myColor: {
default: cc.Color.WHITE,
},
myOtherNumber: 0,
myOtherString: 'no type definition',
myOtherVec2: cc.Vec2.ONE,
myOtherColor: cc.Color.BLACK
},
02_prefab
InstantiatePrefab(预制体的创建)
声明:
properties: {
prefab: {
default: null,
type: cc.Prefab
},
},
创建:
var monster = cc.instantiate(this.prefab);
monster.parent = this.root;
//this.canvas.node.addChild(monster);
monster.position = this.getRandomPosition();
03_events
EventInMask(遮罩)
遮罩的顺序和Canvas下的排列顺序相关联。排序越往上,遮罩时越下层。
MousePropagation(鼠标事件)
鼠标事件的监听大致可分为以下三种情况
cc.Node.EventType.MOUSE_DOWN (点击)
cc.Node.EventType.MOUSE_MOVE(移动)
cc.Node.EventType.MOUSE_LEAVE(离开,结束)
cc.Node.EventType.MOUSE_ENTER()
SimpleEvent(基本事件)
cc.Node.EventType.MOUSE_WHEEL(滚轮监听)
TouchPropagation(触摸事件)
cc.Node.EventType.TOUCH_START (进入)
cc.Node.EventType.TOUCH_MOVE(移动)
cc.Node.EventType.TOUCH_END(退出)
04_scheduler
scheduler(计时器)
his.scheduleOnce(function(){
/*****1秒后执行1次*****/
}.bind(this),1);
this.schedule(function(){
/*****每隔2秒执行1次*****/
}.bind(this),2);
this.schedule(function(){
/*****1秒后开始执行,间隔0.1秒,共执行9次*****/
}.bind(this),0.1,8,1);
this.unscheduleAllCallbacks(this); //停止this指向的组件的所有计时器
05_cross_reference
CircleReference(循环引用)
this.node.on(cc.Node.EventType.TOUCH_END, function () {
var newnode = cc.instantiate(this.prefab);
var parent = this.node.parent;
this.node.parent = null;
newnode.parent = parent;
}, this);
CrossReference(交叉引用)
properties: () => ({
refToFoo: require('Foo'), //规定了应用对象的名称
test: cc.Node
}),
// use this for initialization
onLoad: function () {
var tip = this.node.children[0].getComponent(cc.Label);
tip.string = this.name + ' has reference to ' + this.refToFoo.name;
console.log("引用对象"+this.test.name); // Foo
console.log("引用对象"+this.refToFoo.name); // Foo<Foo>
}
06_life_cycle*
life_cycle(函数生命周期)
console.log("Pos: " + this.node.getPosition().x + ", " + this.node.getPosition().y);
this.node.runAction(cc.sequence(cc.moveBy(2, 200, 0)//执行时间,一定距离, cc.callFunc(function () {
console.log("Pos: " + this.node.x + ", " + this.node.y); //顺序执行动作,创建的动作将按顺序依次运行。
this.node.destroy();
}, this)));
07_asset_loading
AssetLoading(资产加载)
const i18n = require('i18n');
cc.Class({
extends: cc.Component,
properties: {
showWindow: cc.Node,
loadAnimTestPrefab: cc.Prefab,
loadTips: cc.Label,
loadList: {
default: [],
type: cc.Node
}
},
// use this for initialization
onLoad: function () {
// cur load Target
this._curType = "";
this._lastType = "";
this._curRes = null;
this._btnLabel = null;
this._audioSource = null;
this._isLoading = false;
// add load res url
this._urls = {
// Raw Asset
Audio: "test assets/audio",
Txt: "test assets/text",
Texture: "test assets/PurpleMonster",
Font: "test assets/font",
// Raw Asset, use raw url
Plist: cc.url.raw("resources/test assets/atom.plist"),
// Asset
SpriteFrame: "test assets/image",
Prefab: "test assets/prefab",
Animation: "test assets/sprite-anim",
Scene: "test assets/scene",
Spine: "spineboy/spineboy",
CORS: "http://tools.itharbors.com/res/logo.png",
};
// registered event
this._onRegisteredEvent();
},
_onRegisteredEvent: function () {
for (var i = 0; i < this.loadList.length; ++i) {
this.loadList[i].on(cc.Node.EventType.TOUCH_END, this._onClick.bind(this));
}
},
_onClick: function (event) {
if (this._isLoading) {
return;
}
this._onClear();
this._curType = event.target.name.split('_')[1];
console.log("事件名称"+ this._curType );
if (this._lastType !== "" && this._curType === this._lastType) {
this._onShowResClick(event);
return;
}
if (this._btnLabel) {
this._btnLabel.textKey = i18n.t("cases/05_scripting/07_asset_loading/AssetLoading.js.1") + this._lastType;
}
this._lastType = this._curType;
this._btnLabel = event.target.getChildByName("Label").getComponent("cc.Label");
this.loadTips.textKey = this._curType + " Loading....";
this._isLoading = true;
this._load();
},
_load: function () {
var url = this._urls[this._curType];
var loadCallBack = this._loadCallBack.bind(this);
switch (this._curType) {
case 'SpriteFrame':
// specify the type to load sub asset from texture's url
cc.loader.loadRes(url, cc.SpriteFrame, loadCallBack);
break;
case 'Spine':
// specify the type to avoid the duplicated name from spine atlas
cc.loader.loadRes(url, sp.SkeletonData, loadCallBack);
break;
case 'Font':
cc.loader.loadRes(url, cc.Font, loadCallBack);
break;
case 'Animation':
case 'Prefab':
case 'Scene':
case 'Texture':
case 'Txt':
case 'Audio':
cc.loader.loadRes(url, loadCallBack);
break;
case 'CORS':
cc.loader.load(url, loadCallBack);
this.loadTips.textKey = "CORS image should report texImage2D error under WebGL and works ok under Canvas"
break;
default:
cc.loader.load(url, loadCallBack);
break;
}
},
_loadCallBack: function (err, res) {
this._isLoading = false;
if (err) {
cc.log('Error url [' + err + ']');
return;
}
this._curRes = res;
if (this._curType === "Audio") {
this._btnLabel.textKey = i18n.t("cases/05_scripting/07_asset_loading/AssetLoading.js.2");
}
else {
this._btnLabel.textKey = i18n.t("cases/05_scripting/07_asset_loading/AssetLoading.js.3");
}
this._btnLabel.textKey += this._curType;
this.loadTips.textKey = this._curType + " Loaded Successfully!";
},
_onClear: function () {
this.showWindow.removeAllChildren(true);
if (this._audioSource && this._audioSource instanceof cc.AudioSource) {
this._audioSource.stop();
}
},
_onShowResClick: function (event) {
if (this._curType === "Scene") {
cc.director.runScene(this._curRes.scene);
cc.loader.releaseAsset(this._curRes);
this._curRes = null;
return;
}
this._createNode(this._curType, this._curRes);
},
_createNode: function (type, res) {
this.loadTips.textKey = "";
var node = new cc.Node("New " + type);
node.setPosition(0, 0);
var component = null;
switch (this._curType) {
case "SpriteFrame":
component = node.addComponent(cc.Sprite);
component.spriteFrame = res;
break;
case "Texture":
case "CORS":
component = node.addComponent(cc.Sprite);
component.spriteFrame = new cc.SpriteFrame(res);
break;
case "Audio":
component = node.addComponent(cc.AudioSource);
component.clip = res;
component.play();
this._audioSource = component;
this.loadTips.textKey = i18n.t("cases/05_scripting/07_asset_loading/AssetLoading.js.4");
break;
case "Txt":
component = node.addComponent(cc.Label);
component.lineHeight = 40;
component.string = res;
break;
case "Font":
component = node.addComponent(cc.Label);
component.font = res;
component.lineHeight = 40;
component.string = "This is BitmapFont!";
break;
case "Plist":
component = node.addComponent(cc.ParticleSystem);
component.file = this._urls.Plist;
component.resetSystem();
break;
case "Prefab":
var prefab = cc.instantiate(res);
prefab.parent = node;
prefab.setPosition(0, 0);
break;
case "Animation":
var loadAnim = cc.instantiate(this.loadAnimTestPrefab);
loadAnim.parent = node;
loadAnim.setPosition(0, 0);
var AanimCtrl = loadAnim.getComponent(cc.Animation);
AanimCtrl.addClip(res);
AanimCtrl.play(res.name);
break;
case "Spine":
component = node.addComponent(sp.Skeleton);
component.skeletonData = res;
component.animation = "walk";
node.y = -248;
break;
}
this.showWindow.addChild(node);
}
});
LoadResDir
cc.Class({
extends: cc.Component,
properties: {
btnClearAll: cc.Node, // 清空节点
label: cc.Prefab, // 文字预制体
scrollView: cc.ScrollView // 视图插件
},
_init: function () {
this._assets = [];
this._hasLoading = false;
this.scrollView.content.height = 0;
this.btnClearAll.active = false;
},
onLoad: function () {
this._init();
},
_createLabel: function (text) {
var node = cc.instantiate(this.label);
var label = node.getComponent(cc.Label);
label.textKey = text;
this.scrollView.content.addChild(node);
},
_clear: function () {
this.scrollView.content.removeAllChildren(true);
for (var i = 0; i < this._assets.length; ++i) {
var asset = this._assets[i];
// 目前载入 plist 后的 Object 资源没有包含 _uuid 所以无法 release(PS:1.5 版本会加 _uuid)
// 暂时过滤 Object 并且没有 _uuid 类型的资源
if (typeof asset === 'object' && !asset._uuid) {
continue;
}
cc.loader.release(this._assets[i]);
}
},
onClearAll: function () {
this.scrollView.content.height = 0;
this.btnClearAll.active = false;
this._clear();
},
onLoadAll: function () {
if (this._hasLoading) { return; }
this._hasLoading = true;
this._clear();
this._createLabel("Load All Assets");
this.scrollView.scrollToTop();
cc.loader.loadResDir("test assets", (err, assets) => {
if (!this.isValid) {
return;
}
this._assets = assets;
var text = "";
for (var i = 0; i < assets.length; ++i) {
if (typeof assets[i] === 'string') {
text = assets[i]
}
else {
text = assets[i].url || assets[i]._name || assets[i];
}
if (typeof text !== 'string' ) {
continue;
}
this._createLabel(text);
}
this._hasLoading = false;
this.btnClearAll.active = true;
});
},
onLoadSpriteFrameAll: function () {
if (this._hasLoading) { return; }
this._hasLoading = true;
this._clear();
this._createLabel("Load All Sprite Frame");
this.scrollView.scrollToTop();
cc.loader.loadResDir("test assets", cc.SpriteFrame, (err, assets) => {
if (!this.isValid) {
return;
}
this._assets = assets;
var text = "";
for (var i = 0; i < assets.length; ++i) {
if (typeof assets[i] === 'string' ) {
text = assets[i]
}
else {
text = assets[i].url || assets[i]._name || assets[i];
}
this._createLabel(text);
}
this._hasLoading = false;
this.btnClearAll.active = true;
});
}
});
LoadRes
onLoad: function () {
this._url = ["test assets/atlas", "test assets/prefab"];
},
loadPrefab: function () {
var url = this._url[1];
this._releaseResource(url, cc.Prefab);
cc.loader.loadRes(url, cc.Prefab, (err, prefab) => {
this._removeAllChildren();
cc.loader.setAutoRelease(prefab, true);
var node = cc.instantiate(prefab);
this.content.addChild(node);
node.position = cc.v2(0, 0); // 创建对象位置赋值
});
},
_removeAllChildren: function () {
this.content.removeAllChildren(true); //清除其他子物体
},
_releaseResource: function (url, type) {
this._removeAllChildren();
var res = cc.loader.getRes(url, type);
var all = cc.loader.getDependsRecursively(res);
cc.loader.release(all); //通过资源对象自身来释放资源
}
08_module
load_module(加载模块)
使用 cc.instantiate创建预制体对象,然后赋值文字信息,图片信息进行加载模式。
09_singleton
Singleton(单例模式)
10_loadingBar
loadingBar(进度条)
cc.Class({
extends: cc.Component,
properties: {
progressBar: {
default: null,
type: cc.ProgressBar
},
progressTips: {
default: null,
type: cc.Label
},
laodBg: {
default: null,
type: cc.Node
}
},
onLoad: function () {
this._urls = [
cc.url.raw("resources/audio/ding.wav"),
cc.url.raw("resources/audio/cheering.wav"),
cc.url.raw("resources/audio/music_logo.mp3"),
cc.url.raw("resources/test assets/audio.mp3"),
cc.url.raw("resources/loadingBar/font.png"),
cc.url.raw("resources/loadingBar/mikado_outline_shadow.png"),
cc.url.raw("resources/loadingBar/enligsh-chinese.png")
];
this.resource = null;
this.progressBar.progress = 0;
this._clearAll();
this.progressTips.textKey = i18n.t("cases/05_scripting/10_loadingBar/LoadingBarCtrl.js.3");
this.node.on(cc.Node.EventType.TOUCH_START, function () {
if (this.resource) { return; }
cc.loader.load(this._urls, this._progressCallback.bind(this), this._completeCallback.bind(this)); //事件触发
}, this);
},
_clearAll: function () {
for (var i = 0; i < this._urls.length; ++i) {
var url = this._urls[i];
cc.loader.release(url);
}
},
_progressCallback: function (completedCount, totalCount, res) {
this.progress = completedCount / totalCount;
this.resource = res; //开启循环
this.completedCount = completedCount;
this.totalCount = totalCount;
},
_completeCallback: function (error, res) {
},
update: function (dt) {
if (!this.resource) {
return;
}
var progress = this.progressBar.progress;
if (progress >= 1) {
this.progressTips.textKey = i18n.t("cases/05_scripting/10_loadingBar/LoadingBarCtrl.js.1");
this.laodBg.active = false;
this.progressBar.node.active = false;
this.enabled = false;
return;
}
if (progress < this.progress) {
progress += dt;
}
this.progressBar.progress = progress;
this.progressTips.textKey = i18n.t("cases/05_scripting/10_loadingBar/LoadingBarCtrl.js.2")+ this.resource.id + " (" + this.completedCount + "/" + this.totalCount + ")";
}
});
11_network
downloader(下载器)
cc.Class({
extends: cc.Component,
properties: {
label: cc.Label,
sprite: cc.Sprite,
imgUrl: "http://www.cocos.com/wp-content/themes/cocos/img/download1.png",
txtUrl: "http://api.lololyrics.com/0.5/getLyric?artist=John%20Lennon&track=Imagine",
_downloader: null,
_imgTask: null,
_txtTask: null,
_storagePath: "",
_inited: false
},
// use this for initialization
onLoad () {
if (!CC_JSB) {
this.label.string = 'Downloader is a NATIVE ONLY feature.';
return;
}
this._downloader = new jsb.Downloader();
this._downloader.setOnFileTaskSuccess(this.onSucceed.bind(this));
this._downloader.setOnTaskProgress(this.onProgress.bind(this));
this._downloader.setOnTaskError(this.onError.bind(this));
this._storagePath = jsb.fileUtils.getWritablePath() + '/example-cases/downloader/';
this._inited = jsb.fileUtils.createDirectory(this._storagePath);
if (!this._inited) {
this.label.string = 'Failed to create storage path, downloader won\'t work correctly';
}
},
onSucceed (task) {
var atlasRelated = false;
switch (task.requestURL) {
case this.imgUrl:
var self = this;
cc.loader.load(task.storagePath, function (err, tex) {
var spriteFrame = new cc.SpriteFrame(tex);
self.sprite.spriteFrame = spriteFrame;
self.sprite.node.active = true;
self.label.node.active = false;
});
break;
case this.txtUrl:
var content = jsb.fileUtils.getStringFromFile(task.storagePath);
this.sprite.node.active = false;
this.label.node.active = true;
this.label.string = content.substr(0, 350);
break;
}
},
onProgress (task, bytesReceived, totalBytesReceived, totalBytesExpected) {
},
onError (task, errorCode, errorCodeInternal, errorStr) {
this.sprite.node.active = false;
this.label.node.active = true;
this.label.string = 'Failed to download file (' + task.requestURL + '): ' + errorStr + '(' + errorCode + ')';
},
downloadImg () {
if (!this.imgUrl || !this._inited) {
return;
}
this._imgTask = this._downloader.createDownloadFileTask(this.imgUrl, this._storagePath + 'download1.png');
},
downloadTxt () {
if (!this.txtUrl || !this._inited) {
return;
}
this._txtTask = this._downloader.createDownloadFileTask(this.txtUrl, this._storagePath + 'imagine.txt');
}
});
network(网络)
const i18n = require('i18n');
cc.Class({
extends: cc.Component,
properties: {
xhr: cc.Label,
xhrAB: cc.Label,
xhrTimeout: cc.Label,
websocket: cc.Label,
socketIO: cc.Label,
xhrResp: cc.Label,
xhrABResp: cc.Label,
xhrTimeoutResp: cc.Label,
websocketResp: cc.Label,
socketIOResp: cc.Label
},
// use this for initialization
onLoad: function () {
this._wsiSendBinary = null;
this.xhrResp.textKey = "cases/05_scripting/11_network/NetworkCtrl.js.1";
this.xhrABResp.textKey = "cases/05_scripting/11_network/NetworkCtrl.js.2";
this.xhrTimeoutResp.textKey = "cases/05_scripting/11_network/NetworkCtrl.js.2";
this.websocketResp.textKey = "cases/05_scripting/11_network/NetworkCtrl.js.3";
this.socketIOResp.textKey = "cases/05_scripting/11_network/NetworkCtrl.js.4";
this.sendXHR();
this.sendXHRAB();
this.sendXHRTimeout();
this.prepareWebSocket();
this.sendSocketIO();
},
sendXHR: function () {
var xhr = cc.loader.getXMLHttpRequest();
this.streamXHREventsToLabel(xhr, this.xhr, this.xhrResp, 'GET');
xhr.open("GET", "https://httpbin.org/get?show_env=1", true);
if (cc.sys.isNative) {
xhr.setRequestHeader("Accept-Encoding","gzip,deflate");
}
// note: In Internet Explorer, the timeout property may be set only after calling the open()
// method and before calling the send() method.
xhr.timeout = 5000;// 5 seconds for timeout
xhr.send();
},
sendXHRAB: function () {
var xhr = cc.loader.getXMLHttpRequest();
this.streamXHREventsToLabel(xhr, this.xhrAB, this.xhrABResp, "POST");
xhr.open("POST", "https://httpbin.org/post");
//set Content-type "text/plain" to post ArrayBuffer or ArrayBufferView
xhr.setRequestHeader("Content-Type","text/plain");
// Uint8Array is an ArrayBufferView
xhr.send(new Uint8Array([1,2,3,4,5]));
},
sendXHRTimeout: function () {
var xhr = new XMLHttpRequest();
this.streamXHREventsToLabel(xhr, this.xhrTimeout, this.xhrTimeoutResp, 'GET');
xhr.open("GET", "https://192.168.22.222", true);
// note: In Internet Explorer, the timeout property may be set only after calling the open()
// method and before calling the send() method.
xhr.timeout = 5000;// 5 seconds for timeout
xhr.send();
},
prepareWebSocket: function () {
var self = this;
var websocketLabel = this.websocket;
var respLabel = this.websocketResp;
this._wsiSendBinary = new WebSocket("ws://echo.websocket.org");
this._wsiSendBinary.binaryType = "arraybuffer";
this._wsiSendBinary.onopen = function(evt) {
websocketLabel.textKey = "cases/05_scripting/11_network/NetworkCtrl.js.5";
};
this._wsiSendBinary.onmessage = function(evt) {
var binary = new Uint16Array(evt.data);
var binaryStr = 'response bin msg: ';
var str = '';
for (var i = 0; i < binary.length; i++) {
if (binary[i] === 0)
{
str += "\'\\0\'";
}
else
{
var hexChar = '0x' + binary[i].toString('16').toUpperCase();
str += String.fromCharCode(hexChar);
}
}
binaryStr += str;
respLabel.string = binaryStr;
websocketLabel.textKey = "cases/05_scripting/11_network/NetworkCtrl.js.6";
};
this._wsiSendBinary.onerror = function(evt) {
websocketLabel.textKey = "cases/05_scripting/11_network/NetworkCtrl.js.7";
};
this._wsiSendBinary.onclose = function(evt) {
websocketLabel.textKey = "cases/05_scripting/11_network/NetworkCtrl.js.8";
// After close, it's no longer possible to use it again,
// if you want to send another request, you need to create a new websocket instance
self._wsiSendBinary = null;
};
this.scheduleOnce(this.sendWebSocketBinary, 1);
},
sendWebSocketBinary: function(sender)
{
if (!this._wsiSendBinary) { return; }
if (this._wsiSendBinary.readyState === WebSocket.OPEN)
{
this.websocket.textKey = "cases/05_scripting/11_network/NetworkCtrl.js.9";
var buf = "Hello WebSocket中文,\0 I'm\0 a\0 binary\0 message\0.";
var arrData = new Uint16Array(buf.length);
for (var i = 0; i < buf.length; i++) {
arrData[i] = buf.charCodeAt(i);
}
this._wsiSendBinary.send(arrData.buffer);
}
else
{
var warningStr = "send binary websocket instance wasn't ready...";
this.websocket.string = i18n.t("cases/05_scripting/11_network/NetworkCtrl.js.10") + warningStr;
this.scheduleOnce(function () {
this.sendWebSocketBinary();
}, 1);
}
},
// Socket IO callbacks for testing
testevent: function(data) {
if (!this.socketIO) { return; }
var msg = this.tag + " says 'testevent' with data: " + data;
this.socketIO.string = i18n.t("cases/05_scripting/11_network/NetworkCtrl.js.11") + msg;
},
message: function(data) {
if (!this.socketIO) { return; }
var msg = this.tag + " received message: " + data;
this.socketIOResp.string = msg;
},
disconnection: function() {
if (!this.socketIO) { return; }
var msg = this.tag + " disconnected!";
this.socketIO.string = i18n.t("cases/05_scripting/11_network/NetworkCtrl.js.12") + msg;
},
sendSocketIO: function () {
var self = this;
if (typeof io === 'undefined') {
cc.error('You should import the socket.io.js as a plugin!');
return;
}
//create a client by using this static method, url does not need to contain the protocol
var sioclient = io.connect("ws://tools.itharbors.com:4000", {"force new connection" : true});
this._sioClient = sioclient;
//if you need to track multiple sockets it is best to store them with tags in your own array for now
this.tag = sioclient.tag = "Test Client";
//register event callbacks
//this is an example of a handler declared inline
sioclient.on("connect", function() {
if (!self.socketIO) { return; }
var msg = sioclient.tag + " Connected!";
self.socketIO.string = i18n.t("cases/05_scripting/11_network/NetworkCtrl.js.13") + msg;
// Send message after connection
self._sioClient.send("Hello Socket.IO!");
});
//example of a handler that is shared between multiple clients
sioclient.on("message", this.message.bind(this));
sioclient.on("echotest", function (data) {
if (!self.socketIO) { return; }
cc.log("echotest 'on' callback fired!");
var msg = self.tag + " says 'echotest' with data: " + data;
self.socketIO.string = i18n.t("cases/05_scripting/11_network/NetworkCtrl.js.14") + msg;
});
sioclient.on("testevent", this.testevent.bind(this));
sioclient.on("disconnect", this.disconnection.bind(this));
},
streamXHREventsToLabel: function ( xhr, eventLabel, label, method, responseHandler ) {
var handler = responseHandler || function (response) {
return method + " Response (30 chars): " + response.substring(0, 30) + "...";
};
var eventLabelOrigin = eventLabel.string;
// Simple events
['loadstart', 'abort', 'error', 'load', 'loadend', 'timeout'].forEach(function (eventname) {
xhr["on" + eventname] = function () {
eventLabel.string = eventLabelOrigin + "\nEvent : " + eventname;
if (eventname === 'timeout') {
label.string = '(timeout)';
}
};
});
// Special event
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)) {
label.string = handler(xhr.responseText);
}
};
}
});
12_pool
nodePool(对象池)
对象池就是一组可回收的节点对象,我们通过创建 cc.NodePool 的实例来初始化一种节点的对象池。通常当我们有多个 prefab 需要实例化时,应该为每个 prefab 创建一个 cc.NodePool 实例。 当我们需要创建节点时,向对象池申请一个节点,如果对象池里有空闲的可用节点,就会把节点返回给用户,用户通过 node.addChild 将这个新节点加入到场景节点树中。
当我们需要销毁节点时,调用对象池实例的 put(node) 方法,传入需要销毁的节点实例,对象池会自动完成把节点从场景节点树中移除的操作,然后返回给对象池。这样就实现了少数节点的循环利用。 假如玩家在一关中要杀死 n 个敌人,但同时出现的敌人不超过 m 个,那我们就只需要生成 m个节点大小的对象池,然后循环使用就可以了。
关于 cc.NodePool 的详细 API 说明,请参考 添加链接描述cc.NodePool API 文档。
对象池使用的工作流程:
1准备好Prefab
2初始化对象池:在场景加载的初始化脚本中,我们可以将需要数量的节点创建出来,并放进对象池:
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 接口放入对象池
}
3从对象池请求对象
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 身上的脚本进行初始化
}
安全使用对象池的要点就是在 get 获取对象之前,永远都要先用 size 来判断是否有可用的对象,如果没有就使用正常创建节点的方法,虽然会消耗一些运行时性能,但总比游戏崩溃要好!另一个选择是直接调用 get,如果对象池里没有可用的节点,会返回 null,在这一步进行判断也可以。
4将对象返回对象池
当我们杀死敌人时,需要将敌人节点退还给对象池,以备之后继续循环利用,我们用这样的方法:
onEnemyKilled: function (enemy) {
// enemy 应该是一个 cc.Node
this.enemyPool.put(enemy); // 和初始化时的方法一样,将节点放进对象池,这个方法会同时调用节点的 removeFromParent
5清除对象池
如果对象池中的节点不再被需要,我们可以手动清空对象池,销毁其中缓存的所有节点:
myPool.clear(); // 调用这个方法就可以清空对象池
myPool.clear(); // 调用这个方法就可以清空对象池
当对象池实例不再被任何地方引用时,引擎的垃圾回收系统会自动对对象池中的节点进行销毁和回收。但这个过程的时间点不可控,另外如果其中的节点有被其他地方所引用,也可能会导致内存泄露,所以最好在切换场景或其他不再需要对象池的时候手动调用 clear 方法来清空缓存节点。