記事ディレクトリ
序文
既存の Cocos オブジェクト プールの技術記事は基本的に 2.x であり、3.x に関する情報はほとんどありません。3.x から直接開始したい場合は、ここを参照してください。
1. 適用可能なシナリオ
複雑なゲーム開発ではパフォーマンスの最適化を考慮する必要がありますが、これには基本的にオブジェクト プールの使用が含まれます。オブジェクト プールの使用は、弾丸や戦闘中の敵など、同じユニットの頻繁な作成と破壊が必要な状況を目的としています。ゲーム; (cc. instantiate) 操作と破棄 (node.destroy) 操作は非常にパフォーマンスを重視するためです。
2. 基本原則
同じユニットの頻繁な作成と破棄については、パフォーマンスを向上させるために、作成後に再利用し、シーンが切り替わったときに破棄することができます; キャッシュ プール (NodePool) で、必要に応じてプールから取り出し
ますこのサイクルでは、ゲーム プロセス全体に含まれる作成操作と破棄操作は 5 つだけです。
もちろん、これは最も単純なケースであり、プールの動的な拡張など、実際にはより柔軟になります。
3. 作成と方法
import {
NodePool } from "cc";
// 创建,创建后pool是一个节点数组
const pool = new NodePool()
// 获取当前缓冲池的可用对象数量
pool.size()
// 获取对象池中的对象,如果对象池没有可用对象,则返回空。这个函数会调用 poolHandlerComp 的 reuse 函数,如果组件和函数都存在的话。
pool.get()
// 向缓冲池中存入一个不再需要的节点对象。这个函数会自动将目标节点从父节点上移除,但是不会进行 cleanup 操作。这个函数会调用 poolHandlerComp 的 unuse 函数,如果组件和函数都存在的话。
pool.put()
// 销毁对象池中缓存的所有节点
pool.clear()
4. 利用プラン
ここでは例として「サイクルファイア 1 弾」を使用します。
4.1 解決策 1: 事前に作成する
フローチャート
コード
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 解決策 2: 動的作成
動的作成の核心は、使用時にオブジェクト プールを作成することです。ここでは例として「1 つの弾丸をループで発射する」を使用します。
フローチャート
コード
1. 実際のアプリケーションでグローバル オブジェクト プールを作成し、プレハブごとにオブジェクト プールを作成します。たとえば次のようになります:
data.pools = {}
2. getPoolNode メソッドをカプセル化します。このメソッドは、オブジェクト プールにノードがあるかどうかに応じてノードを動的に作成するために使用されます。
3. Bullet ノードは Bullet カスタム コンポーネントにバインドされます。このコンポーネントはノードの破棄を処理するために destroyPool メソッドをカプセル化し、pool.put() がノードをリサイクルするときに onDisable をトリガーしてリサイクル ロジックを処理します。
- シナリオ使用
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()
})
}
}
- 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
}
- 箇条書きコンポーネント
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. 注意すべき事項
5.1 ノードプール NodePool のライフサイクル
ノードがノードプールにある場合
- onLoad start は、ノードが初めて NodePool から取り出されたときにのみトリガーされます。
- onEnable は、ノードが NodePool から削除されるとトリガーされます。
- onDisable 当节点被放回 NodePool 时触发
- onDestroy 当 NodePool 被 clear 时触发
5.2 还原节点状态
回收节点时,节点状态并不会还原节点初始状态,需要用户手动还原
这就是方案二中,onDisable需要做的事
六、总结
这就是Cocos3.x 对象池NodePool使用介绍和注意事项,我也是从坑中走出来的,希望对您有帮助。
点个赞再走!