对象池这个名字听起来好像不明觉厉,其实就是将一系列需要反复创建和销毁的对象存储在一个看不到的地方,下次用同样的东西时往这里取,类似于一个存放备用物质的仓库。

它的好处就是避免了反复实例化个体的运算,能减少大量内存碎片,当然你需要更多的空间来存这些备用对象,相信使用这些空间是非常值得的。

最常见的应用就是子弹的创建和销毁。

一般对象池都是一个全局性的通用脚本,可以采用单例模式来设计。

https://www.cnblogs.com/koshio0219/p/11203631.html

对象池至少包含以下两个基本功能:

1.从池中取出指定类型的对象

2.回收各式各样的对象到池中

先定义对象池和池子的容量:

     private const int maxCount = ;
private Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>();

容量是一个常量,最好取二的幂值,这样的话可以刚好占用所有内存位的资源,避免浪费。

这里池子用字典标识,key为对象的名字,这样比较好记,你用InstanceID也没问题。

每个同样的对象一般在池子中可以有很多,用一个List来存。

下面先定义回收对象的方法:

     public void RecycleObj(GameObject obj)
{
var par = Camera.main;
obj.transform.SetParentSafe(par.transform);
obj.SetActive(false); if (pool.ContainsKey(obj.name))
{
if (pool[obj.name].Count < maxCount)
{
pool[obj.name].Add(obj);
}
}
else
{
pool.Add(obj.name, new List<GameObject>() { obj });
}
}

这里将回收的对象统一放在了场景主摄像机下,你也可以选择放在自己喜欢的位置。

回收对象就是先把对象隐藏,然后看池子中有没有这一类对象,有的话没有超过容量上限就直接扔进去。

如果没有这类对象,那就创建这一类型对象的Key值(名字:比如说螃蟹),顺便添加第一只螃蟹。

如果回收对象时改变了父物体,最好在设置父物体前后记录下对象的本地位置,旋转和缩放,可以写了一个扩展方法用于安全设置父物体:

     public static void SetParentSafe(this Transform transform,Transform parent)
{
var lp = transform.localPosition;
var lr = transform.localRotation;
var ls = transform.localScale;
transform.SetParent(parent);
transform.localPosition = lp;
transform.localRotation = lr;
transform.localScale = ls;
}

经常会遇到要批量回收进池子的情况:

     public void RecycleAllChildren(GameObject parent)
{
for (; parent.transform.childCount > ;)
{
var tar = parent.transform.GetChild().gameObject;
RecycleObj(tar);
}
}

对象可以回收了,那怎么取呢,自然也是能从池子里取就从池子里取,实在不行才去实例化:

     public GameObject GetObj(GameObject perfab)
{
//池子中有
GameObject result = null;
if (pool.ContainsKey(perfab.name))
{
if (pool[perfab.name].Count > )
{
result = pool[perfab.name][];
result.SetActive(true);
pool[perfab.name].Remove(result);
return result;
}
}
//池子中缺少
result = Object.Instantiate(perfab);
result.name = perfab.name;
RecycleObj(result);
GetObj(result);
return result;
}

如果池子中有对象,取出来之后记得要把这个对象从该类对象的列表中移除,不然下次可能又会取到这家伙,而这家伙已经要派去做别的了。

如果池子中缺少对象,那就只能实例化了,要注意把实例化后的对应改为大家都一样的名字,这样方便下一次取能找到它。

没有对象的情况下,我这里又重新回收了一下再取一次,你也可以直接返回该对象,相当于在取的时候不存在这类对象的话我提前做了标记。

和Instantiate方法一样,加一个可以设置父对象的重载方法:

     public GameObject GetObj(GameObject perfab, Transform parent)
{
var result = GetObj(perfab);
result.transform.SetParentSafe(parent);
return result;
}

下面是完整脚本:

 using System.Collections.Generic;
using UnityEngine; public class ObjectPool : Singleton<ObjectPool>
{
private const int maxCount = ;
private Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>(); public GameObject GetObj(GameObject perfab)
{
//池子中有
GameObject result = null;
if (pool.ContainsKey(perfab.name))
{
if (pool[perfab.name].Count > )
{
result = pool[perfab.name][];
if (result != null)
{
result.SetActive(true);
pool[perfab.name].Remove(result);
return result;
}
else
{
pool.Remove(perfab.name);
}
}
}
//池子中缺少
result = Object.Instantiate(perfab);
result.name = perfab.name;
RecycleObj(result);
GetObj(result);
return result;
} public GameObject GetObj(GameObject perfab, Transform parent)
{
var result = GetObj(perfab);
result.transform.SetParentSafe(parent);
return result;
} public void RecycleObj(GameObject obj)
{
var par = Camera.main;
obj.transform.SetParentSafe(par.transform);
obj.SetActive(false); if (pool.ContainsKey(obj.name))
{
if (pool[obj.name].Count < maxCount)
{
pool[obj.name].Add(obj);
}
}
else
{
pool.Add(obj.name, new List<GameObject>() { obj });
}
} public void RecycleAllChildren(GameObject parent)
{
for (; parent.transform.childCount > ;)
{
var tar = parent.transform.GetChild().gameObject;
RecycleObj(tar);
}
} public void Clear()
{
pool.Clear();
}
}

因为是用名字作为存储的Key值,所以不同类的物体命名不能相同,不然可能会取错对象。

另外由于上面的脚本有更改父物体的情况,在取出物体之后根据需要也可以对transform进行归位:

     public static void ResetLocal(this Transform transform)
{
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
}

上面是对Transform类的一个扩展方法,例如:

  var ins = ObjectPool.Instance.GetObj(bulletPrefab, parent.transform);
ins.transform.ResetLocal();

游戏设计模式——Unity对象池的更多相关文章

  1. Unity——对象池管理

    Unity对象池管理 一.Demo展示 二.逻辑 在游戏中会出现大量重复的物体需要频繁的创建和销毁:比如子弹,敌人,成就列表的格子等: 频繁的创建删除物体会造成很大的开销,像这种大量创建重复且非持续性 ...

  2. Unity 对象池 生产 保存

    Unity对象池主要是保存那些常用的物体,避免他们在不断销毁和创造中损坏性能. 主要思路为:创造物体时,判断是否存在,如果存在则调用并使其显示.如果不存在则创造一个新的. 当销毁时,调用协程延时隐藏物 ...

  3. 游戏开发设计模式之对象池模式(unity3d 示例实现)

    前篇:游戏开发设计模式之命令模式(unity3d 示例实现) 博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正. 原理:从一个固定 ...

  4. unity游戏开发_对象池

    现在假设游戏中我们需要实现一个这样功能,按下鼠标左键,发射一颗子弹,3秒之后消失.在这个功能中,我们发射了上百上千发子弹,就需要实例化生成上百上千次.这时候,我们就需要使用对象池这个概念,每次实例化生 ...

  5. Unity ----- 对象池GameObjectPool

    孙广东 2014.6.28 非常早之前看到的外国文章,认为不错,分享一下. 对象池在AssetStore中也是有非常多插件的,可是有些重了.自己写一个轻量的岂不是非常好. 当你须要创建大量某种类型对象 ...

  6. Unity 对象池的使用

    在游戏开发过程中,我们经常会遇到游戏发布后,测试时玩着玩着明显的感觉到有卡顿现象.出现这种现象的有两个原因:一是游戏优化的不够好或者游戏逻辑本身设计的就有问题,二是手机硬件不行.好吧,对于作为程序员的 ...

  7. Unity对象池的实现

    对象池是一个单例类: using System.Collections; using System.Collections.Generic; using UnityEngine; public cla ...

  8. GoLang设计模式06 - 对象池模式

    这次介绍最后一个创建型模式--对象池模式.顾名思义,对象池模式就是预先初始化创建好多个对象,并将之保存在一个池子里.当需要的时候,客户端就可以从池子里申请一个对象使用,使用完以后再将之放回到池子里.池 ...

  9. 游戏设计模式——Unity事件队列(纪念京阿尼事件)

    “对消息或事件的发送与受理进行时间上的解耦.” 在游戏开发过程中,经常会出现不同板块之间的信息交流,或是存在“当...,就...”的情况,事件队列编程模式可以有效解决消息传递中产生的脚本耦合问题,让同 ...

随机推荐

  1. 性能测试学习第五天-----Jmeter测试脚本&基础元件使用

    JMeter简介:一个100%的纯Java桌面应用,由Apache组织的开放源代码项目,它是功能和性能测试的工具.具有高可扩展性.支持Web(HTTP/HTTPS).SOAP.FTP.JAVA等多种协 ...

  2. Ubuntu18设置mysql的sql_mode

    原因: MySQL 5.7.5及以上功能依赖检测功能.如果启用了ONLY_FULL_GROUP_BY SQL模式(默认情况下),MySQL将拒绝选择列表,HAVING条件或ORDER BY列表的查询引 ...

  3. mysql函数拼接查询concat函数

    //查询表managefee_managefee的年year 和 month ,用concat函数拼成year-month.例如将2017和1 拼成2017-01.. select CONCAT(a. ...

  4. CodeForces 1058E

    题意略. 思路:本题有两个关键点: 一.满足题设的区间条件 1.区间内1的个数和为偶数 2.区间内含1个数最多的那一项,它所含1的个数不得超过区间内1的个数和的一半. 二.长度超过60的区间必然满足上 ...

  5. Leetcode之二分法专题-441. 排列硬币(Arranging Coins)

    Leetcode之二分法专题-441. 排列硬币(Arranging Coins) 你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币. 给定一个数字 n,找出可形 ...

  6. C# - AutoMapper之不同类型的转换

    一.Dto & Model约定 class TestDto { public string Name { get; set; } public int Age { get; set; } pu ...

  7. springboot2.X 使用spring-data组件对MongoDB做CURD

    springboot2.X 使用spring-data组件对MongoDB做CURD 使用背景 基于快速开发,需求不稳定的情况, 我决定使用MongoDB作为存储数据库,搭配使用spring-data ...

  8. Django之使用中间件解决前后端同源策略问题

    问题描述 前端时间在公司的时候,要使用angular开发一个网站,因为angular很适合前后端分离,所以就做了一个简单的图书管理系统来模拟前后端分离. 但是在开发过程中遇见了同源策略的跨域问题,页面 ...

  9. 同步机制之一--Synchronized,以及此机制下的锁的本质和种类

    Java中,为了实现同步的操作临界区,线程在执行临界区的代码时,需要获得某个对象的锁.本文介绍获得对象的锁的方法之一----Synchronized关键字. Synchronized关键字的用法 Cl ...

  10. python暴力算法快乐数

    编写一个算法来判断一个数是不是"快乐数". 一个"快乐数"定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 ...