学习笔记 4_Cocos Creator_脚本开发工作流程

简介

Cocos Creator 的脚本主要是通过扩展组件来进行开发的。目前 Cocos Creator 支持 JavaScriptCoffeeScript 两种脚本语言。通过编写脚本组件,并将它赋予到场景节点中来驱动场景中的物体。

在组件脚本的编写过程中,你可以通过声明属性,将脚本中需要调节的变量映射到 属性检查器(Properties) 中,供策划和美术调整。于此同时,你也可以通过注册特定的回调函数,来帮助你初始化,更新甚至销毁节点。

创建和使用组件脚本

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) {
    },
});

使用 cc.Class 声明类型

定义 CCClass

    var Sprite = cc.Class({
        name: "sprite"
    });

实例化

 var obj = new Sprite();

判断类型

cc.log(obj instanceof Sprite);       // true

构造函数

使用 ctor 声明构造函数:

继承

使用 extends 实现继承:

// 父类
var Shape = cc.Class();

// 子类
var Rect = cc.Class({
    extends: Shape
});

父构造函数

继承后,CCClass 会统一自动调用父构造函数,你不需要显式调用。

var Shape = cc.Class({
    ctor: function () {
        cc.log("Shape");    // 实例化时,父构造函数会自动调用,
    }
});

var Rect = cc.Class({
    extends: Shape
});

var Square = cc.Class({
    extends: Rect,
    ctor: function () {
        cc.log("Square");   // 再调用子构造函数
    }
});

var square = new Square();

父类->子类顺序,以上代码将依次输出 “Shape” 和 “Square”。

组件脚本中声明属性

cc.Class({
    extends: cc.Component,
    properties: {
        userID: 20,
        userName: "Foobar"
    }
});

简单声明

 properties: {
     // 当声明的属性为基本 JavaScript 类型时,可以直接赋予默认值
      height: 20,       // number
      type: "actor",    // string
      loaded: false,    // boolean                
      target: null,     // object
      // 当声明的属性具备类型时
      target: cc.Node,
      pos: cc.Vec2,
      // 声明属性的类型继承自 cc.ValueType 时
      pos: new cc.Vec2(10, 20),
      color: new cc.Color(255, 255, 255, 128),
      // 当声明属性是一个数组时,可以在声明处填写他们的类型或构造函数来完成声明
      any: [],      // 不定义具体类型的数组
      bools: [cc.Boolean],
      strings: [cc.String],
      floats: [cc.Float],
      ints: [cc.Integer],

      values: [cc.Vec2],
      nodes: [cc.Node],
      frames: [cc.SpriteFrame],

  }

完整声明

properties: {
    score: {
        default: 0,
        displayName: "Score (player)",
        tooltip: "The score of 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 同样写成数组,提高代码可读性
    },
}

get/set 声明

properties: {
    width: {
        get: function () {
            return this._width;
        },
        set: function (value) {
            this._width = value;
        }
    }
}

访问节点和组件

1、获得组件所在的节点 this.node

 start: function () {
        var node = this.node;
        node.x = 100;
    }

2、获得其它组件 getComponent

 start: function () {
        var label = this.getComponent(cc.Label);
        if (label) {
            label.string = "Hello";
        }
        else {
            cc.error("Something wrong?");
        }
    }

3、获得其它节点及其组件

  • 查找子节点 : var cannons = this.node.children;
  • 名称查找 : this.node.getChildByName("Cannon 01");
  • 路径查找 : cc.find("Cannon 01/Barrel/SFX", this.node);
  • 全局名字查找( 从场景根节点开始逐级查找 ) : this.backNode = cc.find("Canvas/Menu/Back");

4、通过全局变量访问

  • 定义全局对象 window.Global,这个对象里面包含了 backNodebackLabel 两个属性
// Globals.js, this file can have any name
window.Global = {
    backNode: null,
    backLabel: null,
};
  • 在合适的地方直接访问并初始化 Global
// Back.js

cc.Class({
    extends: cc.Component,

    onLoad: function () {
        Global.backNode = this.node;
        Global.backLabel = this.getComponent(cc.Label);
    }
});
  • 可以在任何地方访问到 Global 里的值
cc.Class({
    extends: cc.Component,

    // start 会在 onLoad 之后执行,所以这时 Global 已经初始化过了
    start: function () {
        var text = 'Back';
        Global.backLabel.string = text;
    }
});

5、通过模块访问

  • 如果你不想用全局变量,你可以使用 require 来实现脚本的跨文件操作
// Global.js, now the filename matters

module.exports = {
    backNode: null,
    backLabel: null,
};
  • 每个脚本都能用 require + 文件名(不含路径) 来获取到对方 exports 的对象
    var Global = require("Global");
// Back.js

// this feels more safe since you know where the object comes from
var Global = require("Global");

cc.Class({
    extends: cc.Component,

    onLoad: function () {
        Global.backNode = this.node;
        Global.backLabel = this.getComponent(cc.Label);
    }
});
// AnyScript.js

// this feels more safe since you know where the object comes from
var Global = require("Global");

cc.Class({
    extends: cc.Component,

    // start 会在 onLoad 之后执行,所以这时 Global 已经初始化过了
    start: function () {
        var text = "Back";
        Global.backLabel.string = text;
    }
});

常用节点和组件接口

节点状态和层级操作

1.关闭/激活节点

  • this.node.active = false;  执行 组件上的 onDisable 方法
  • this.node.active = true;   执行 组件上的 onEnable 方法

2.更改节点的父节点
假设父节点为 parentNode,子节点为 this.node,两种方式等价

  • this.node.parent = parentNode;
  • this.node.removeFromParent(false);
    parentNode.addChild(this.node);
//更改节点锚点位置
this.node.anchorX = 1;
this.node.anchorY = 0;
或
this.node.setAnchorPoint(1, 0);
//颜色和
mySprite.node.color = cc.Color.RED;
//不透明度
mySprite.node.opacity = 128;
常用组件接口

cc.Component 是所有组件的基类,任何组件都包括如下的常见接口(假设我们在该组件的脚本中,以 this 指代本组件):
+ this.node:该组件所属的节点实例
+ this.enabled:是否每帧执行该组件的 update 方法,同时也用来控制渲染组件是否显示
+ update(dt):作为组件的成员方法,在组件的 enabled 属性为 true 时,其中的代码会每帧执行
+ onLoad():组件所在节点进行初始化时(节点添加到节点树时)执行
+ start():会在该组件第一次 update 之前执行,通常用于需要在所有组件的 onLoad 初始化完毕后执行的逻辑

PS : onLoad 初始化完毕后,在执行 start

生命周期回调

用户的声明周期回调函数主要有:

  • onLoad
  • onEnable :在onLoad 之后,start 之前被调用。
  • start
  • update
  • lateUpdate
  • onDisable
  • onDestroy

创建和销毁节点

创建节点 new cc.Node()

cc.Class({
  extends: cc.Component,

  properties: {
    sprite: {
      default: null,
      type: cc.SpriteFrame,
    },
  },

  start: function () {

    var node = new cc.Node('Sprite');
    var sp = node.addComponent(cc.Sprite);
    sp.spriteFrame = this.sprite;
    node.parent = this.node;
  },
});


});

克隆已有节点 & 创建预制节点

cc.Class({
  extends: cc.Component,

  properties: {
    target: {
      default: null,
      type: cc.Node,
    },
  },

  start: function () {
    var scene = cc.director.getScene();
    var node = cc.instantiate(this.target);

    node.parent = scene;
    node.setPosition(0, 0);
  },
});

销毁节点 node.destroy()

cc.Class({
  extends: cc.Component,

  properties: {
    target: cc.Node,
  },

  start: function () {
    // 5 秒后销毁目标节点
    setTimeout(function () {
      this.target.destroy();
    }.bind(this), 5000);
  },

  update: function (dt) {
    if (cc.isValid(this.target)) {
      this.target.rotation += dt * 10.0;
    }
  },
});

destroy 和 removeFromParent 的区别

调用一个节点的 removeFromParent 后,它不一定就能完全从内存中释放,因为有可能由于一些逻辑上的问题,导致程序中仍然引用到了这个对象。因此如果一个节点不再使用了,请直接调用它的 destroy 而不是 removeFromParent。destroy 不但会激活组件上的 onDestroy,还会降低内存泄露的几率,同时减轻内存泄露时的后果。
总之,如果一个节点不再使用,destroy 就对了,不需要 removeFromParent 也不需要设置 parent 为 null 哈。

加载和切换场景

1、切换场景
cc.director.loadScene("MyScene");
2、跨场景不销毁
cc.game.addPersistRootNode(myNode);
取消 不销毁 cc.game.removePersistRootNode(myNode);
3、场景加载回调
cc.director.loadScene("MyScene", onSceneLaunched);
PS : 由于回调函数只能写在本脚本中,所以场景加载回调通常用来配合常驻节点,在常驻节点上挂载的脚本中使用。
4、预加载场景

cc.director.preloadScene("table", function () {
    cc.log("Next scene preloaded");
});

//之后在合适的时间调用 loadScene, 就可以真正切换场景。
cc.director.loadScene("table");

获取和加载资源

资源的分类


  • Asset

type 关键字
sset” 的资源类型,cc.SpriteFrame, cc.AnimationClip, cc.Prefab 等资源都属于 Asset
  spriteFrame: {
     default: null,
       type: cc.SpriteFrame
   },


  • Raw Asset

url 关键字
图片(cc.Texture2D),声音(cc.AudioClip),粒子(cc.ParticleAsset)等资源都是 Raw Asset
   textureURL: {
      default: "",
        url: cc.Texture2D
    }
动态加载

注意事项
1. 所有需要通过脚本动态加载的资源,都必须放置在 assets/resources 文件夹或它的子文件夹下
2. 资源动态加载的时都是异步的,需要在回调函数中获得载入的资源

动态加载 Asset
  1. Asset/resourcescc.loader.loadRes加载
  2. loadRes 一次只能加载单个 Asset
  3. 传入路径,不能包含文件扩展名
    eg :
// 加载 Prefab
cc.loader.loadRes("test assets/prefab", function (err, prefab) {
    var newNode = cc.instantiate(prefab);
    cc.director.getScene().addChild(newNode);
});

// 加载 AnimationClip
var self = this;
cc.loader.loadRes("test assets/anim", function (err, clip) {
    self.node.getComponent(cc.Animation).addClip(clip, "anim");
});

// 加载 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;
});

// 加载单个 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("test assets/image", cc.SpriteFrame);
cc.loader.releaseRes("test assets/anim");
// 传实例
cc.loader.releaseAsset(spriteFrame);
动态加载 Raw Asset

Raw Asset 可以直接使用 url 从远程服务器上加载,也可以从项目中动态加载。

cc.url.raw

Raw Asset 加载成功后,如果需要传给一些 url 形式的 API,还是需要给出完整路径才行。你需要用 cc.url.raw 进行一次 url 的转换:

// 原 url 会报错!文件找不到
var texture = cc.textureCache.addImage("resources/test assets/image.png");

// 改用 cc.url.raw,此时需要声明 resources 目录和文件后缀名
var realUrl = cc.url.raw("resources/test assets/image.png");
var texture = cc.textureCache.addImage(realUrl);
资源批量加载
// 加载 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 对象。
});
加载远程资源和设备资源
// 远程 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
});
资源的依赖和释放
  1. 加载完资源之后,所有的资源都会临时被缓存到 cc.loader
  2. 注意资源之间的依赖性
  3. JavaScript 的垃圾回收是延迟的,释放资源后,垃圾回收还没开始,又重启请求资源情况
    -> 判断资源是否有多份
    -> 检查游戏逻辑
// 直接释放某个贴图
cc.loader.release(texture);
// 释放一个 prefab 以及所有它依赖的资源
var deps = cc.loader.getDependsRecursively('prefabs/sample');
cc.loader.release(deps);
// 如果在这个 prefab 中有一些和场景其他部分共享的资源,你不希望它们被释放,有两种方法:
// 1. 显式声明禁止某个资源的自动释放
cc.loader.setAutoRelease(texture2d, false);
// 2. 将这个资源从依赖列表中删除
var deps = cc.loader.getDependsRecursively('prefabs/sample');
var index = deps.indexOf(texture2d._uuid);
if (index !== -1)
    deps.splice(index, 1);
cc.loader.release(deps);

监听和发射事件

监听事件

1、on 监听,我们还可以使用 once 方法。once 监听在监听函数响应后就会关闭监听事件。

    // 监听事件on 方法一  
  this.node.on('mousedown', function ( event ) {
      console.log('Hello!');
    });
  // 监听事件on 方法二 使用第三个参数 this绑定调用者
this.node.on('mousedown', function (event) {
  this.enabled = false;
}, this); 

// 关闭监听 off, off 方法的 参数必须和 on 方法的参数一一对应,才能完成关闭   
  _sayHello: function () {
    console.log('Hello World');
  },

  onEnable: function () {
    this.node.on('foobar', this._sayHello, this);
  },

  onDisable: function () {
    this.node.off('foobar', this._sayHello, this);
  },
发送事件

1、emit

cc.Class({
  extends: cc.Component,

  onLoad: function () {
  // 注册接收事件
    this.node.on('say-hello', function (event) {
      console.log(event.detail.msg);
    });
  },

  start: function () {
  // 发送事件
    this.node.emit('say-hello', {
      msg: 'Hello, this is Cocos Creator',
    });
  },
});
派送事件
  1. 采用冒泡派送的方式。冒泡派送会将事件从事件发起节点,不断地向上传递给他的父级节点
  2. 直到到达根节点或者在某个节点的响应函数中做了中断处理 event.stopPropagation()
  3. 一个抽象类 cc.Event 对象不能直接创建,请创建 cc.Event.EventCustom 对象来进行派发
    这里写图片描述
// 节点 c 的组件脚本中
this.node.dispatchEvent( new cc.Event.EventCustom('foobar', true) );

// 节点 b 的组件脚本中
this.node.on('foobar', function (event) {
  event.stopPropagation();
});
触摸事件类型和事件对象

触摸事件在移动平台和桌面平台都会触发

枚举对象定义 对应的事件名 事件触发的时机
cc.Node.EventType.TOUCH_START ‘touchstart’ 当手指触点落在目标节点区域内时
cc.Node.EventType.TOUCH_MOVE ‘touchmove’ 当手指在屏幕上目标节点区域内移动时
cc.Node.EventType.TOUCH_END ‘touchend’ 当手指在目标节点区域内离开屏幕时
cc.Node.EventType.TOUCH_CANCEL ‘touchcancel’ 当手指在目标节点区域外离开屏幕时

玩家输入事件
键盘事件

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;
        }
    }
});
设备重力传感事件
cc.Class({
    extends: cc.Component,
    onLoad () {
        // open Accelerometer
        cc.inputManager.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);
    },
});
动作系统简介
基础动作
// 创建一个移动动作
var action = cc.moveTo(2, 100, 100);
// 执行动作
node.runAction(action);
// 停止一个动作
node.stopAction(action);
// 停止所有动作
node.stopAllActions();

// 开发者还可以给动作设置 tag,并通过 tag 来控制动作。

// 给 action 设置 tag
var ACTION_TAG = 1;
action.setTag(ACTION_TAG);
// 通过 tag 获取 action
node.getActionByTag(ACTION_TAG);
// 通过 tag 停止一个动作
node.stopActionByTag(ACTION_TAG);
容器动作
// 一个复杂的跳跃动画
this.jumpAction = cc.sequence(
    cc.spawn(
        cc.scaleTo(0.1, 0.8, 1.2),
        cc.moveTo(0.1, 0, 10)
    ),
    cc.spawn(
        cc.scaleTo(0.2, 1, 1),
        cc.moveTo(0.2, 0, 0)
    ),
    cc.delayTime(0.5),
    cc.spawn(
        cc.scaleTo(0.1, 1.2, 0.8),
        cc.moveTo(0.1, 0, -10)
    ),
    cc.spawn(
        cc.scaleTo(0.2, 1, 1),
        cc.moveTo(0.2, 0, 0)
    )
// 以1/2的速度慢放动画,并重复5次
).speed(2).repeat(5);
动作回调

方法名,对象,参数
var finished = cc.callFunc(this.myMethod, this, opt);

// cc.callFunc 第一个参数是处理回调的方法
// 第二个参数指定了处理回调方法的 context(也就是绑定 this)
// 第三个参数是向处理回调方法的传参
var finished = cc.callFunc(function(target, score) {
    this.score += score;
}, this, 100);//动作完成后会给玩家加100分
缓动动作
var aciton = cc.scaleTo(0.5, 2, 2);
action.easing(cc.easeIn(3.0));

使用计时器

1.开始一个计时器

 component.schedule(function() {
     // 这里的 this 指向 component
     this.doSomething();
 }, 5);

2.更灵活的计时器

 // 以秒为单位的时间间隔
 var interval = 5;
 // 重复次数
 var repeat = 3;
 // 开始延时
 var delay = 10;
 component.schedule(function() {
     // 这里的 this 指向 component
     this.doSomething();
 }, interval, repeat, delay);

3.只执行一次的计时器

 component.scheduleOnce(function() {
     // 这里的 this 指向 component
     this.doSomething();
 }, 2);

4.取消计时器

//开发者可以使用回调函数本身来取消计时器:

 this.count = 0;
 this.callback = function () {
     if (this.count === 5) {
         // 在第六次执行回调时取消这个计时器
         this.unschedule(this.callback);
     }
     this.doSomething();
     this.count++;
 }
 component.schedule(this.callback, 1);

下面是 Component 中所有关于计时器的函数:

  • schedule:开始一个计时器
  • scheduleOnce:开始一个只执行一次的计时器
  • unschedule:取消一个计时器
  • unscheduleAllCallbacks:取消这个组件的所有计时器

脚本执行顺序

统一的控制脚本来初始化其他脚本

Player.js, Enemy.jsMenu.js 中需要实现 init 方法和 update,并将初始化逻辑放进去。

// 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();
    }

  update: function (dt) {
        this.player.updatePlayer(dt);
        this.enemy.updateEnemy(dt);
        this.menu.updateMenu(dt);
    }
});
控制同一个节点上的组件执行顺序

属性检查器 里的排列顺序来控制,Move UpMove Down,排列在上的组件会先于排列在下的组件执行。

代码设置组件执行优先级
  • executionOrder 越小,该组件相对其它组件就会越先执行。
  • executionOrder 默认为 0,因此设置为负数的话,就会在其它默认的组件之前执行。
  • executionOrder 只对 onLoad, onEnable, start, update 和 lateUpdate 有效,对 onDisable 和 onDestroy 无效。
// Player.js

cc.Class({
    extends: cc.Component,
    editor: {
        executionOrder: -1
    },

    onLoad: function () {
        cc.log('Player onLoad!');
    }
});

// Menu.js
cc.Class({
    extends: cc.Component,
    editor: {
        executionOrder: 1
    },

    onLoad: function () {
        cc.log('Menu onLoad!');
    }
});

标准网络接口

我们支持 Web 平台上最广泛使用的标准网络接口:

  • XMLHttpRequest:用于短连接
  • WebSocket:用于长连接

1.XMLHttpRequest 简单示例

 var xhr = new XMLHttpRequest();
 xhr.onreadystatechange = function () {
     if (xhr.readyState == 4 && (xhr.status >= 200 && xhr.status < 400)) {
         var response = xhr.responseText;
         console.log(response);
     }
 };
 xhr.open("GET", url, true);
 xhr.send();

2.WebSocket

ws = new WebSocket("ws://echo.websocket.org");
 ws.onopen = function (event) {
     console.log("Send Text WS was opened.");
 };
 ws.onmessage = function (event) {
     console.log("response text msg: " + event.data);
 };
 ws.onerror = function (event) {
     console.log("Send Text fired an error");
 };
 ws.onclose = function (event) {
     console.log("WebSocket instance closed.");
 };

 setTimeout(function () {
     if (ws.readyState === WebSocket.OPEN) {
         ws.send("Hello WebSocket, I'm a text message.");
     }
     else {
         console.log("WebSocket instance wasn't ready...");
     }
 }, 3);

使用对象池

在运行时进行节点的创建(cc.instantiate)和销毁(node.destroy)操作是非常耗费性能的,因此我们在比较复杂的场景中,通常只有在场景初始化逻辑(onLoad)中才会进行节点的创建,在切换场景时才会进行节点的销毁。如果制作有大量敌人或子弹需要反复生成和被消灭的动作类游戏,我们要如何在游戏进行过程中随时创建和销毁节点呢?这里就需要对象池的帮助了。

初始化对象池

this.enemyPool = new cc.NodePool();

从对象池请求对象
// ...

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
     // 和初始化时的方法一样,将节点放进对象池,这个方法会同时调用节点的 removeFromParent
    this.enemyPool.put(enemy); 

}
使用组件来处理回收和复用的事件

let myBulletPool = new cc.NodePool('Enemy'); // 传入组件名

清除对象池

象池中的节点不再被需要,我们可以手动清空对象池,销毁其中缓存的所有节点 myPool.clear();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值