對象池優化是遊戲開發中很是重要的優化方式,也是影響遊戲性能的重要因素之一。
在遊戲中有許多對象在不停的建立與移除,好比角色攻擊子彈、特效的建立與移除,NPC的被消滅與刷新等,在建立過程當中很是消耗性能,特別是數量多的狀況下。
對象池技術能很好解決以上問題,在對象移除消失的時候回收到對象池,須要新對象的時候直接從對象池中取出使用。
優勢是減小了實例化對象時的開銷,且能讓對象反覆使用,減小了新內存分配與垃圾回收器運行的機會。html
https://docs.cocos.com/creator/manual/zh/scripting/pooling.html
node
針對以上問題,我分享一下本身使用對象池的經驗。微信
import { IPool } from "./IPool"; export default class CCNodePool implements IPool{ private pool: cc.NodePool; private resItem: cc.Prefab; private name: string = '' /** * * @param prefab 預製體 * @param conut 初始化個數 */ constructor(name: string, resItem: cc.Prefab, conut: number) { this.name = name this.pool = new cc.NodePool(); this.resItem = resItem; for (let i = 0; i < conut; i++) { let obj: cc.Node = this.getNode(); // 建立節點 this.pool.put(obj); // 經過 putInPool 接口放入對象池 } } getName() { return this.name } get() { let go: cc.Node = this.pool.size() > 0 ? this.pool.get() : this.getNode(); return go; } getNode() { if(this.resItem){ return cc.instantiate(this.resItem); }else{ console.error(' 預製體沒有賦值 ') return null; } } size() { return this.pool.size(); } put(go: cc.Node) { this.pool.put(go); } clear() { this.pool.clear(); } }
export default class TSObjectPool<T> { private pool:any [] = [] private className:string; constructor(className:string,type: { new(): T ;},count:number = 0){ this.className = className; for (let index = 0; index < count; index++) { this.pool.push(new type()); } } getClassName(){ return this.className; } get<T>(type: { new(): T ;} ): T { let go = this.pool.length > 0 ? this.pool.shift() : null; if(!go){ go = new type(); } return go; } put(instance:T){ this.pool.push(instance); } clear(){ this.pool = []; } }
不管是節點對象池仍是非節點對象池。我都習慣經過一個管理器封裝起來使用。
這樣的好處就是集中管理,修改時也很是方便。性能
import CCNodePool from "./CCNodePool"; import SelfPool from "./SelfPool"; export default class CCPoolManager { private static ins: CCPoolManager; static instance(): CCPoolManager { if (!this.ins) { this.ins = new CCPoolManager(); } return this.ins; } //對象池表 private pools = {}; // 對象名稱 和給定 key的 映射表 這樣在回收對象的時候就不須要傳入key了。經過節點的name就能夠找到key。 private nameMap = {}; init(key: string, resItem: cc.Prefab, count: number) { if (!this.pools[key]) { this.pools[key] = new SelfPool(new CCNodePool(key, resItem, count)) } } getPool(key: string) { return this.pools[key].getPool(); } get(key: string): cc.Node { if (this.pools[key]) { let go: cc.Node = this.pools[key].get(); if (!this.nameMap[go.name] && go.name != key) { this.nameMap[go.name] = key; } return go; } return null; } put(go: cc.Node, nodePool: boolean = false) { let key = this.nameMap[go.name]; if (!key) { key = go.name; } if (!this.pools[key]) { cc.warn(" not have name ", key, ' ,go.name ', go.name); return; } this.pools[key].put(go, nodePool); } clear(name: string) { if (this.pools[name]) { this.pools[name].clear(); this.pools[name] = null; } } clealAll() { for (const key in this.pools) { this.clear(key); } this.pools = {}; } }
import TSObjectPool from "./TSObjectPool"; export default class TSPoolManager { //對象池表 private pools = {} private static ins: TSPoolManager; static instance(): TSPoolManager { if (!this.ins) { this.ins = new TSPoolManager(); } return this.ins; } init<T>(key: string, type: { new(): T; }, count: number = 1): void { if (!this.pools[key]) { this.pools[key] = new TSObjectPool(key, type, count); } } /** * 得到被銷燬的對象 * @param key */ get<T>(key: string, type: { new(): T; }, count: number = 1): T { if (!this.pools[key]) { this.pools[key] = new TSObjectPool(key, type, count); } return this.pools[key].get(type); } put(key: string, obj) { let pool = this.pools[key] if (pool) { pool.put(obj); } } }
對象由外部建立。不用考慮是否爲預製體建立的節點對象。優化
export default class ObjectPool<T>{ private buffList: T[] = [] private key: string; constructor(key: string) { this.key = key; } get(func: () => T) { let item = this.buffList.length > 0 ? this.buffList.shift() : func(); return item; } put(obj: T) { this.buffList.push(obj) } size() { return this.buffList.length } destroy() { this.buffList.length = 0; } }
import ObjectPool from "./ObjectPool"; import TSMap from "../struct/TSMap"; export default class PoolManager { private static ins: PoolManager static instance() { if (!this.ins) { this.ins = new PoolManager(); } return this.ins; } private map: TSMap<string, ObjectPool<any>> = new TSMap(); get(key: any, func: () => any) { if (!this.map.has(key)) { this.map.set(key, new ObjectPool(key)) } return this.map.get(key).get(func) } put(key: any, obj: any) { if (this.map.has(key)) { this.map.get(key).put(obj) } else { } } size(key: string) { if (this.map.has(key)) { return this.map.get(key).size() } return 0; } destroy() { this.map.clear(); } }
針對Cocos的這一性能問題,我利用裝飾模式,自定義了SelfPool類改變了獲取和回收時的操做。this
import CCNodePool from "./CCNodePool"; import { IPool } from "./IPool"; /** * 使用opacity方式隱藏對象 */ export default class SelfPool implements IPool{ private list:cc.Node[] = [] private pool:CCNodePool; constructor(pool:CCNodePool){ this.pool = pool; } get(){ let go:cc.Node = this.list.length > 0 ? this.list.shift() : this.pool.get(); go.opacity = 255; return go; } getPool(){ return this.pool } size(){ return this.pool.size() + this.list.length; } /** * * @param go * @param nodePool 是否放入NodePool中 */ put(go:cc.Node,nodePool:boolean = false){ if(nodePool){ this.pool.put(go) }else{ this.list.push(go); go.stopAllActions(); go.opacity = 0; } } clear(){ this.pool.clear(); this.list.length = 0; } }
在對象池初始化的時候作了這樣的處理
若是不想使用隱藏方式,能夠去掉這一層封裝,接口都是同樣的。code
在回收對象時的一向操做是put(key,obj)
若是obj確定擁有name或者其餘某個能夠標識類別的屬性,能夠將key與name作一個映射。經過name直接得到key,從而找到對應的對象池,那麼在put的時候也就不須要傳入key了。
htm
以上就是我在遊戲開發中使用對象池的幾種的方式,分享出來,供你們參考使用。對象
歡迎掃碼關注公衆號《微笑遊戲》,瀏覽更多內容。blog