前序:

  Q:为啥需要对象池?

  A: 游戏中大量出现或销毁对象时会反复的开堆和放堆,程序与内存之间交互过于频繁导致资源的大量浪费

  Q: 对象池实现原理?

  A: 当子对象池没有物体的时候,它会和普通没加对象池的程序一样要内存,实例化数据,但当某个属于对象池的物体被销毁时,它不会直接回内存,而是被保存在子对象池顺序表中,当下次程序再次想用该对象时就可以直接从子对象池中拿取而不用像内存索取,像游戏中存在 特效,子弹等物体时时常会用到。

  Q:什么是传统和AB对象池

  A: 传统对象池,使用简单资源存放于Resources中,但当需要打包进AssetBundle中时,因为是代码弱关联的原因,会导致打不进去,所以需要使用强关联的形式来迫使Unity打包子对象池中的预制体。

  Q: 使用过程中需要注意哪些地方

  A: 框架使用时,请注意向下的迭代,就是说新版本的框架代码必须兼容老版本的框架代码。框架代码前面一定要加类似于JW这样的标记,由于项目一般不是一个人完成的,而框架、设计模式、算法等英文名称大多相近,如果不加以区分容易出现类重复的情况(切记!切记!)

说明:

  传统对象池与AB对象池 其实现原理一毛一样,其唯一区别在于游戏数据放在AB包中,如果按照传统对象池那样通过Resources的代码加载资源,会导致数据没有被打包,所以需要挂载资源。

原理图示:

传统对象池


方法1:(代码部分很简单直接看就行)

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic; namespace Game
{
public class CSObjectPool<T> where T : class, new()
{
private Stack<T> m_ObjStack = new Stack<T>();
private Action<T> m_OnPop;
private Action<T> m_OnPush; public CSObjectPool(Action<T> onPop, Action<T> onPush)
{
m_OnPop = onPop;
m_OnPush = onPush;
} /// <summary>
/// 取出
/// </summary>
public T Pop()
{
T obj = null;
if(m_ObjStack.Count == )
{
obj = new T();
}
else
{
obj = m_ObjStack.Pop();
} if(obj != null && m_OnPop != null)
{
m_OnPop(obj);
}
return obj;
} /// <summary>
/// 放回
/// </summary>
public void Push(T obj)
{
if(obj == null)
return; if(m_ObjStack.Count > && ReferenceEquals(m_ObjStack.Peek(), obj))
{
Debug.LogError(string.Format("CSObjectPool error. Trying to push object that is already in the pool, obj = {0}", obj));
return;
} if(m_OnPush != null)
{
m_OnPush(obj);
}
m_ObjStack.Push(obj);
} }
}

方式2:( 由于这个方法实现过于复杂已经被我放弃了!)

使用方法:

  1. 挂载ObjectPool (场景中)
  2. 讲预制体(prefab)放入Resources资源中
  3. 然后就可以直接单例要数据!
  4. 销毁就直接调  ObjectPool.Instance.Unspawn(XXXX);

代码:

/***************************************
作者: 蒋伟
版本: v1.0
最后修改时间: 2016-12-22
电话: 15928517727
功能&使用方法: 可重用接口
*
* void OnSpawn -------- 当生成时调用
* void OnUnspawn ------ 当回收时调用
***************************************/
using UnityEngine;
using System.Collections;
public interface IReusable
{
//当生成时调用(初始化)
void OnSpawn();
//当回收时调用(销毁)
void OnUnspawn();
}
/***************************************
作者: 蒋伟
版本: v1.3
最后修改时间: 2016/12/26
电话: 15928517727
功能&使用方法:
* 自定义顺序表, 注意存放在该表中的所有
* 物体必须可比较,如果为自定义的类需要继
* 自IComparable接口,并public实现接口汇总CompareTo(Object obj)
* 接口!
*
* 存在方法:
* <0> ----------- MyArrayList<T>() -------- 无参构造
* <1> ----------- Size() ------------------ 得到使用空间
* <2> ----------- Expansion()(私有)------ 扩容
* <3> ----------- Add(T data) ------------- 添加数据
* <4> ----------- (r)T At(int index) ------ 得到数据
* <5> ----------- Clear() ----------------- 清空数据
* <6> ----------- (r)T this[int index] ---- 得到/设置 数据【索引器】
* <7> ----------- SortSmallToBig() -------- 排序(从小到大)(冒泡排序)
* <8> ----------- ConsoleShow() ----------- 显示(控制台) Unity程序别用
* <9> ----------- DebugShow() ------------- 显示(Debug.Log) Unity调试使用
* <10> ---------- Contains(T) ------------- bool类型返回是否包含这个东西
***************************************/
using UnityEngine;
using System.Collections;
using System;
namespace MyList
{ //泛型顺序表 (IComparable) 可以比较的
//由于部分数据类型(GameObject)不支持数据比较,
//然而它还不知羞耻的加了Sealed属性
public class MyArrayList<T> /*where T : IComparable*/
{
//容量
int capacity;
//使用量
int size;
//堆 --- 指针
T[] obj;
//构造
public MyArrayList()
{
//内存容量 (初始4 自动扩容)
capacity = ;
//空间使用量归0
size = ;
//开堆
obj = new T[capacity];
}
//属性 --- 返回使用空间
public int Size
{
//只写返回
get { return size; }
}
//方法 --- 扩容
void Expansion()
{
//容量 == 使用量
if (size == capacity)
{
//容量扩大一倍
capacity *= ;
//数据开堆
T[] nt = new T[capacity];
//复制数据
for (int i = ; i < size; ++i)
{
nt[i] = obj[i];
}
//修改数据结构指针指向
obj = nt;
}
}
//方法 --- 数据添加
public void Add(T data)
{
//扩容
Expansion();
//放入数据
obj[size++] = data;
}
//方法 --- 得到数据[下标索引]
public T At(int index)
{
//参数检查
if (index < || index >= size)
return default(T);
else
return obj[index];
}
//方法 --- 清空
public void Clear()
{
//使用空间修改为0就可以了
size = ;
}
//索引器a
public T this[int index]
{
set
{
//设置数据 在 范围内
if (index >= && index < size)
{
obj[index] = value;
}
}
get
{
if (index >= && index < size)
return obj[index];
else
return default(T);
}
}
//声明委托
//case 1 -------------- Left > Right
//case 0 -------------- Left == Right
//case -1 -------------- Left < Right
public delegate int CompareTo(T _objLeft, T _objRight);
//方法 --- 排序(冒泡)
public void SortSmallToBig(CompareTo CTF)
{
//冒泡排序
for (int i = ; i < size; ++i)
{
for (int j = size - ; j > i; --j)
{
if (CTF(obj[i], obj[j]) > )
{
T temp = obj[i];
obj[i] = obj[j];
obj[j] = temp;
}
}
}
}
//遍历显示(C#控制台显示)
public void ConsoleShow()
{
//遍历
for (int i = ; i < size; ++i)
{
//这个显示方式比
Console.WriteLine(obj[i]);
}
}
//遍历显示(Unity.Debug.Log(name))
public void DebugShow()
{
//遍历
for (int i = ; i < size; ++i)
{
//这个显示方式比
Debug.Log(obj[i].ToString());
}
}
//遍历查找
public bool Contains(T t, CompareTo CTF)
{
//遍历
for (int i = ; i < size; ++i)
{
if (CTF(t, obj[i]) == )
{
return true;
}
}
//否则return false
return false;
}
} }
/***************************************
作者: 蒋伟
版本: v1.1
最后修改时间: 2018-06-05
电话: 15928517727
功能&使用方法: 传统对象池 1.挂载ObjectPool (场景中)
2.讲预制体(prefab)放入Resources资源中
3.然后就可以直接单例要数据了! ****************************************/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ObjectPool : Singleton<ObjectPool>
{
//子对象池存储
Dictionary<string, SubPool> m_pools; //初始化
public override void Awake()
{
base.Awake(); Init();
} private void Init()
{
m_pools = new Dictionary<string, SubPool>();
} //取对象 绝对路径
public GameObject Spawn(string _name)
{
//返回的子对象池
SubPool pool = null;
//如果存在该子对象池
if (m_pools.ContainsKey(_name))
{
pool = m_pools[_name];
}
else
pool = CreateNewSubPool(_name);
//返回一个新对象
return pool.Spawn();
}
//回收对象
public void Unspawn(GameObject _obj)
{
SubPool pool = null; //遍历查找所有子池子
foreach (SubPool p in m_pools.Values)
{
//找到了
if (p.Contains(_obj))
{
pool = p;
break;
}
}
//调用回收方法
if (pool!= null)
pool.Unspawn(_obj);
} //回收所有对象
public void UnspawnAll()
{
foreach (SubPool p in m_pools.Values)
p.UnspawnAll();
} //创建新的子对象池
private SubPool CreateNewSubPool(string _name)
{
//预设路径
string path = "";
path = _name;
//加载预设
GameObject prefab = Resources.Load<GameObject>(path);
SubPool np = new SubPool(prefab);
//添加到池列表
m_pools.Add(_name, np); //返回子对象池
return np;
} }
/***************************************
作者: 蒋伟
版本: v1.0
最后修改时间: 2016-12-21
电话: 15928517727
功能&使用方法: 可被回收的类(抽象类)
*
***************************************/
using UnityEngine;
using System.Collections;
public abstract class ReusableObject : MonoBehaviour, IReusable
{
//pS:纯虚类不能有本体 但如果用virtual 修饰则不会有这个问题
public abstract void OnSpawn();
public abstract void OnUnspawn();
}
/***************************************
作者: 蒋伟
版本: v1.0
最后修改时间: 2016-12-22
电话: 15928517727
功能&使用方法: 单例
* 这个单例还需要理解
***************************************/
using UnityEngine;
using System.Collections;
public class Singleton<T> : MonoBehaviour
where T : MonoBehaviour
{
//指针
protected static T m_instance = null;
//属性
public static T Instance
{
get { return m_instance; }
}
//偷懒的单例方式 在 Awake里面进行初始化
public virtual void Awake()
{
m_instance = this as T;
}
}
/***************************************
作者: 蒋伟
版本: v1.0
最后修改时间: 2016-12-22
电话: 15928517727
功能&使用方法:子对象池
*
* --------------------------------------------
* GameObject m_prefab ------------- 预设
* MyArrayList<GameObject> m_objects - 存储物体用链表
* --------------------------------------------
* Name ------------------------------- 得到子对象池名字(属性)
* SubPool(GameObject prefab)-------- 构造
* Spawn()--------------------------- 喂!对象池!给我一个对象!(单身狗之咆哮~)
* Unspawn(GameObject obj) ------------ 回收一个对象
* UnspawnAll() --------------------- 回收所有对象s
* Contains(GameObject obj) --------- 是否包含对象
***************************************/
using UnityEngine;
using System.Collections;
using MyList;
public class SubPool
{
//预设
GameObject m_prefab;
//存储物体用链表(构造来)
MyArrayList<GameObject> m_objects;
//子对象池名字
public string Name
{
get
{
return m_prefab.name;
}
}
//构造
public SubPool(GameObject prefab)
{
//赋值
this.m_prefab = prefab; //开堆
m_objects = new MyArrayList<GameObject>();
}
//取对象 池子中则拿出 无则新建
public GameObject Spawn()
{
GameObject obj = null;
for (int i = ; i < m_objects.Size; ++i)
{
//池子中存在,拿出
if (!m_objects[i].activeSelf)
{
obj = m_objects[i];
break;
}
}
//如果对象池没有了!
if (obj == null)
{
obj = GameObject.Instantiate<GameObject>(m_prefab);
m_objects.Add(obj);
}
//设置为启用
obj.SetActive(true);
//调用怪物的初始化函数 并且是预设体子树下所有组件的初始化
//(厉害了我的哥!),参2保证不投射错误
obj.SendMessage("OnSpawn", SendMessageOptions.DontRequireReceiver);
return obj;
}
//回收对象
public void Unspawn(GameObject obj)
{
//对象存在于子对象池
if (Contains(obj))
{
obj.SendMessage("OnUnspawn", SendMessageOptions.DontRequireReceiver);
obj.SetActive(false);
}
}
//回收该池子的所有对象
public void UnspawnAll()
{
for (int i = ; i < m_objects.Size; ++i)
{
if (m_objects[i].activeSelf)
{
m_objects[i].SendMessage("OnUnspawn", SendMessageOptions.DontRequireReceiver);
m_objects[i].SetActive(false);
}
}
}
//是否包含对象
public bool Contains(GameObject go)
{
bool temp = false;
//判断物体是否存在于顺序表中
for (int i = ; i < m_objects.Size; ++i)
{
if (m_objects[i] == go)
{
temp = true;
break;
}
}
return temp;
}
}

使用代码:

/***************************************
Editor: Tason
Version: v1.0
Last Edit Date: 2018-XX-XX
Tel: 328791554@qq.com
Function Doc:
在屏幕左上角为初始点依次生成图像
***************************************/ using UnityEngine;
using System.Collections;
using UnityEngine.UI; public class Test : MonoBehaviour
{
//需要生成的物体地址
public string spritePath; //物体生成在哪个物体下面 如果为空则生成在当前挂载下面
public Transform m_parent; //当前物体的生成下标
private int m_xIndex;
private int m_yIndex; //初始屏幕长宽
private int m_screenW;
private int m_screenH; //生成对象的长宽
private float m_spriteW;
private float m_spriteH; private void Awake()
{
Init();
} //初始化
private void Init()
{
//安全校验
if (m_parent == null)
m_parent = transform; //初始化参数
GameObject obj = Resources.Load<GameObject>(spritePath);
if (obj == null)
{
Debug.Log("生成物体不存在!!");
return;
}
else
{
m_spriteW = obj.GetComponent<RectTransform>().sizeDelta.x;
m_spriteH = obj.GetComponent<RectTransform>().sizeDelta.y;
} m_screenW = Screen.width;
m_screenH = Screen.height; m_xIndex = m_yIndex = ; //初始测试
Debug.Log("Sw:" + m_spriteW + " Sh:" + m_spriteH);
Debug.Log("Screenw:" + m_screenW + " Screenh:" + m_screenH);
} private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GameObject obj = ObjectPool.Instance.Spawn("sprites") as GameObject;
obj.transform.SetParent(m_parent);
obj.GetComponent<RectTransform>().anchoredPosition = new Vector3(m_xIndex++ * m_spriteW - m_screenW / , - * m_yIndex * m_spriteH + m_screenH / , );
if ((m_xIndex + ) * m_spriteW >= m_screenW)
{
m_xIndex = ;
m_yIndex++;
}
}
} }

AB对象池


本来以为很麻烦,结果发现只用加一个脚本就可以了,ObjectPool(ABObjectPool) 都是负责生成物体的,SubPool负责管理GameObject的List,代码如下 (亲测AssetBundle打包可行!!!撒花!)

/***************************************
作者: 蒋伟
版本: v0.0.1
最后修改时间: 2018-06-05
电话: 15928517727
功能&使用方法: 强关联对象池 1.挂载ABObjectPool (场景中)
2.给ABObjectPool 添加键值对!!
3.然后就可以直接单例要数据了! ****************************************/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ABObjectPool : Singleton<ABObjectPool>
{
//子对象池存储
Dictionary<string, SubPool> m_pools; [System.Serializable]
public class KeyValueStruct
{
public string m_name;
public GameObject m_prefab;
} public KeyValueStruct[] m_keyValues; //初始化
public override void Awake()
{
base.Awake();
Init();
} private void Init()
{
m_pools = new Dictionary<string, SubPool>();
} //取对象 绝对路径
public GameObject Spawn(string _name)
{
//返回的子对象池
SubPool pool = null;
//如果存在该子对象池
if (m_pools.ContainsKey(_name))
pool = m_pools[_name];
else
pool = CreateNewSubPool(_name); //返回一个新对象
if (pool != null)
return pool.Spawn(); return null;
}
//回收对象
public void Unspawn(GameObject _obj)
{
SubPool pool = null; //遍历查找所有子池子
foreach (SubPool p in m_pools.Values)
{
//找到了
if (p.Contains(_obj))
{
pool = p;
break;
}
}
//调用回收方法
if (pool!= null)
pool.Unspawn(_obj);
} //回收所有对象
public void UnspawnAll()
{
foreach (SubPool p in m_pools.Values)
p.UnspawnAll();
} //创建新的子对象池
private SubPool CreateNewSubPool(string _name)
{
//预设路径
string path = _name;
//加载预设
GameObject prefab = null;
for (int i = ; i < m_keyValues.Length; ++i)
{
if (_name.Equals(m_keyValues[i].m_name))
{
prefab = m_keyValues[i].m_prefab;
break;
}
}
if (prefab == null)
{
Debug.Log("对象池数据丢失!!!!!");
return null;
} SubPool np = new SubPool(prefab);
//添加到池列表
m_pools.Add(_name, np); //返回子对象池
return np;
} }

传统对象池&AB对象池的更多相关文章

  1. paip.提升性能----数据库连接池以及线程池以及对象池

    paip.提升性能----数据库连接池以及线程池以及对象池 目录:数据库连接池c3po,线程池ExecutorService:Jakartacommons-pool对象池 作者Attilax  艾龙, ...

  2. [翻译] 编写高性能 .NET 代码--第二章 GC -- 将长生命周期对象和大对象池化

    将长生命周期对象和大对象池化 请记住最开始说的原则:对象要么立即回收要么一直存在.它们要么在0代被回收,要么在2代里一直存在.有些对象本质是静态的,生命周期从它们被创建开始,到程序停止才会结束.其它对 ...

  3. 使用DBMS_SHARED_POOL包将对象固定到共享池

    使用DBMS_SHARED_POOL包将对象固定到共享池2011年06月24日 09:45:00 Leshami 阅读数:5808 版权声明:本文为博主原创文章,欢迎扩散,扩散请务必注明出处. htt ...

  4. java常量池与对象存储

    一 数据存储位置                                 我们先来谈谈数据的存储位置,有五个地方可以存储数据 (1)寄存器:这是最快的存储区,因为它位于不同于其他存储区的地方- ...

  5. Http请求封装(对HttpClient类的进一步封装,使之调用更方便。另外,此类管理唯一的HttpClient对象,支持线程池调用,效率更高)

    package com.ad.ssp.engine.common; import java.io.IOException; import java.util.ArrayList; import jav ...

  6. 【JVM】Java 8 中的常量池、字符串池、包装类对象池

    1 - 引言 2 - 常量池 2.1 你真的懂 Java的“字面量”和“常量”吗? 2.2 常量和静态/运行时常量池有什么关系?什么是常量池? 2.3 字节码下的常量池以及常量池的加载机制 2.4 是 ...

  7. 使用concurrent.futures模块并发,实现进程池、线程池

    Python标准库为我们提供了threading和multiprocessing模块编写相应的异步多线程/多进程代码 从Python3.2开始,标准库为我们提供了concurrent.futures模 ...

  8. [数据库连接池] Java数据库连接池--DBCP浅析.

    前言对于数据库连接池, 想必大家都已经不再陌生, 这里仅仅设计Java中的两个常用数据库连接池: DBCP和C3P0(后续会更新). 一. 为何要使用数据库连接池假设网站一天有很大的访问量,数据库服务 ...

  9. [原创]java WEB学习笔记47:Servlet 监听器简介, ServletContext(Application 对象), HttpSession (Session 对象), HttpServletRequest (request 对象) 监听器,利用listener理解 三个对象的生命周期

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

随机推荐

  1. rsync配置安装

    rsync安装 1.将rsync包解压,包链接: https://pan.baidu.com/s/1jHPosXC 密码: maay 2.进入rsync安装包运行命令: ./configure --p ...

  2. Cocos2dx开发之运行与渲染流程分析

    学习Cocos2dx,我们都知道程序是由 AppDelegate 的方法 applicationDidFinishLaunching 开始,在其中做些必要的初始化,并创建运行第一个 CCScene 即 ...

  3. 手工脱壳之FSG压缩壳-IAT表修复

    目录 一.工具及壳介绍 二.脱壳 2.1.单步跟踪脱壳 2.2.IAT修复 三.程序脱壳后运行截图 四.个人总结 五.附件 一.工具及壳介绍 使用工具:Ollydbg.PEID.ImportREC.L ...

  4. mac环境下mongodb的安装和使用

    mac环境下mongodb的安装和使用 简介 MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介于关系数据 ...

  5. 意想不到的的异常-由于eclipse和tomcat的交互出错-eclipse断点导致debug启动缓慢

    足足启动了200多秒,正赶上hibernate 的使用上全部换使用方式,修改了很多代码.赶在这个节骨点上,出现debug 启动时卡在hibernate 启动的地方不动了,也没掉到debug断点的地方. ...

  6. 原生js简单轮播图 代码

    在团队带人,突然被人问到轮播图如何实现,进入前端领域有一年多了,但很久没自己写过,一直是用大牛写的插件,今天就写个简单的适合入门者学习的小教程.当然,轮播图的实现原理与设计模式有很多种,我这里讲的是用 ...

  7. Python内置的服务器的使用

    cd 到某一文件 Python内置的服务器: E:\myObject\office\netObject\new-gcms> python -m SimpleHTTPServer 8888 如果是 ...

  8. [DP][NOIP2013]花匠

    花匠 问题描述: 花匠栋栋种了一排花,每株花都有自己的高度.花儿越长越大,也越来越挤.栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致. ...

  9. idea debug快捷键 快速查找类

    快速查找类或者文件比如xml .txt Ctrl + Shift + N 快速查找类 双击Shift 选中代码右移 Tab 选中代码左移 Shift + Tab 选中代码上下移 Shift + Alt ...

  10. AX_CreateAndPostSales

    static void CreateAndPostSales(Args _args) { List il = new List(Types::Record); SalesTable localSale ...