Introducción y precauciones del uso de NodePool del grupo de objetos Cocos3.x


prefacio

Las publicaciones técnicas existentes del grupo de objetos Cocos son básicamente 2.x, y hay muy poca información sobre 3.x. Si desea comenzar directamente desde 3.x, consulte aquí.


1. Escenarios aplicables

La optimización del rendimiento debe tenerse en cuenta en cualquier desarrollo de juego complejo, que básicamente implica el uso de grupos de objetos. El uso de grupos de objetos está dirigido a situaciones que requieren la creación y destrucción frecuentes de la misma unidad, como balas y enemigos en acción. juegos, porque las operaciones (cc. instanciate) y destrucción (node.destroy) requieren mucho rendimiento;

2. Principios básicos

Para la creación y destrucción frecuentes de la misma unidad, para mejorar el rendimiento, podemos reutilizarla después de la creación y luego destruirla cuando se cambia la escena;
en el grupo de caché (NodePool), sacarlo del grupo cuando sea necesario. , y devuélvalo cuando se agote. En este ciclo, solo participarán 5 operaciones de creación y destrucción en todo el proceso del juego.

Por supuesto, este es el caso más simple y será más flexible en la práctica, como la expansión dinámica de la piscina, etc.

3. Creación y método

import {
    
     NodePool } from "cc";

// 创建,创建后pool是一个节点数组
const pool = new NodePool()

// 获取当前缓冲池的可用对象数量
pool.size() 

// 获取对象池中的对象,如果对象池没有可用对象,则返回空。这个函数会调用 poolHandlerComp 的 reuse 函数,如果组件和函数都存在的话。
pool.get() 

// 向缓冲池中存入一个不再需要的节点对象。这个函数会自动将目标节点从父节点上移除,但是不会进行 cleanup 操作。这个函数会调用 poolHandlerComp 的 unuse 函数,如果组件和函数都存在的话。
pool.put() 

// 销毁对象池中缓存的所有节点
pool.clear() 

4. Plan de uso

Aquí usamos "ciclo de disparo de 1 bala" como ejemplo.

4.1 Solución 1: crear con anticipación

diagrama de flujo

Created with Raphaël 2.3.0 开始 创建对象池 实例化子弹并放进对象池 开始使用:从对象池取出子弹 结束使用:把子弹放进对象池 游戏结束 销毁对象池里子弹 结束 yes no

el código

import {
    
     _decorator, Component, Prefab, NodePool, instantiate } from 'cc'
const {
    
     ccclass, property } = _decorator

@ccclass('Start')
export class Start extends Component {
    
    
  public pool!: NodePool // 定义节点池
  @property({
    
     type: Prefab, tooltip: '子弹' }) readonly bulletPfb!: Prefab // 子弹预制体

  start() {
    
    
    // 1. 创建节点池
    this.pool = new NodePool()

    // 2. 通过预制体创建节点,并放到节点池
    const node = instantiate(this.bulletPfb)
    this.pool.put(node)

    // 3. 每5秒从节点池中取出节点,使用后放回节点池
    this.schedule(() => {
    
    
      // 从节点池中取出节点
      const node = this.pool.get()

      // 这里开始使用节点
      // ..............

      // 使用结束后放回节点池
      this.pool.put(node)
    }, 5)
  }

  // 控件销毁时,清空节点池
  onDestroy() {
    
    
    this.pool.clear()
  }
}

4.2 Solución 2: creación dinámica

El núcleo de la creación dinámica es crear un grupo de objetos cuando se usa. Aquí usamos "disparar 1 bala en un bucle" como ejemplo.

diagrama de flujo

Created with Raphaël 2.3.0 开始 开始使用 存在对象池 对象池有可用子弹 从对象池取出子弹 使用结束:把子弹放进对象池 游戏结束 销毁对象池里子弹 结束 实例化子弹 创建对象池 yes no yes no yes no

el código

1. Cree un grupo de objetos global en aplicaciones prácticas y cree un grupo de objetos para cada prefabricado, por ejemplo aquí:
data.pools = {}

2. Encapsule el método getPoolNode, que se utiliza para crear nodos dinámicamente según si el grupo de objetos tiene nodos.

3. El nodo Bullet estará vinculado al componente personalizado Bullet, que encapsula el método destroyPool para manejar la destrucción del nodo y activa onDisable cuando pool.put () recicla el nodo para manejar la lógica de reciclaje.

  • Uso del escenario
import {
    
     _decorator, Component,  Prefab, } from 'cc'
import data from '../global/Data'
import {
    
     getPoolNode } from '../utils/GameCommon'
const {
    
     ccclass, property } = _decorator

@ccclass('Start')
export class Start extends Component {
    
    

  @property({
    
     type: Prefab, tooltip: '子弹' }) readonly bulletPfb!: Prefab // 子弹预制体

  start() {
    
    
    // 1. 每5秒从节点池中取出节点,使用后放回节点池
    this.schedule(() => {
    
    
      // 从节点池中取出节点
      const node = getPoolNode(this.bulletPfb)

      // 这里开始使用节点
      // ..............

      // 使用结束后放回节点池(destroyPool为自定义方法)
      node.getComponent(Bullet).destroyPool()
    }, 5)
  }

  // 控件销毁时,清空节点池
  onDestroy() {
    
    
    Object.keys(data.pools).forEach((key) => {
    
    
      data.pools[key].clear()
    })
  }
}

  • método getPoolNode
/**
 * 通过节点池创建节点
 * @param prefab 预制体
 */
export function getPoolNode(prefab: Prefab) {
    
    
  let name = prefab.data.name
  let node: Node = null
  if (data.pools.hasOwnProperty(name)) {
    
    
    //已有对应的对象池
    let pool = data.pools[name]
    if (pool.size() > 0) {
    
    
      node = pool.get()
    } else {
    
    
      node = instantiate(prefab)
    }
  } else {
    
    
    // 没有对应对象池,创建他!
    let pool = new NodePool()
    data.pools[name] = pool

    node = instantiate(prefab)
  }
  return node
}
  • Componente de bala
import {
    
     _decorator, Component } from 'cc'
import data from '../../global/Data'
const {
    
     ccclass } = _decorator

@ccclass('Bullet')
export class CommonUnit extends Component {
    
    
  public $hp = 1 // 生命

  // 禁用时还原状态(节点池pool.put()时触发)
  onDisable() {
    
    
    this.$hp = 1
    // 如果有用到tween,则这里要停止,否则节点还会执行
    // Tween.stopAllByTarget(this.node)
  }

  // 从对象池中销毁
  destroyPool() {
    
    
    const pool = data.pools['Bullet']
    if (pool) {
    
    
      pool.put(this.node)
    } else {
    
    
      this.destroy()
    }
  }
}

5. Asuntos que requieren atención

5.1 Ciclo de vida del grupo de nodos

Cuando el nodo está en NodePool

  • El inicio de onLoad solo se activará cuando el nodo se saque del NodePool por primera vez.
  • onEnable se activa cuando el nodo se elimina del NodePool
  • onDisable 当节点被放回 NodePool 时触发
  • onDestroy 当 NodePool 被 clear 时触发

5.2 还原节点状态

回收节点时,节点状态并不会还原节点初始状态,需要用户手动还原
这就是方案二中,onDisable需要做的事

六、总结

这就是Cocos3.x 对象池NodePool使用介绍和注意事项,我也是从坑中走出来的,希望对您有帮助。
点个赞再走!

Guess you like

Origin blog.csdn.net/iamlujingtao/article/details/126794528