Unity对象池技术(原理+实战)
写在前⾯
很早就听说过对象池技术……然⽽⼀直到这⼏天才真正去了解= =。还得感谢Jasper Flick的博客,这⾥推荐他的,⽬前我只看了前⼏篇,收获还是挺⼤的~本篇博客也是基于这个系列中的⼀篇——,加上个⼈的⼀些理解,对Unity的对象池技术进⾏简单介绍。
对象池简介
顾名思义,对象池是存放对象的缓冲区。⽤户可以从缓冲区中放⼊/取出对象。⼀类对象池存放⼀类特定的对象。那么对象池有什么⽤呢?在游戏中,经常会有产⽣/销毁⼤量同类游戏对象的需求,⽐如游戏中源源不断的敌⼈、频繁刷新的宝箱、乃⾄⼀些游戏特效(风、⾬等)。如果没有⼀种⽐较好的机制来管理这些对象的产⽣和销毁,⽽是⼀昧的Instantiate和Destroy,将使你的游戏性能⼤⼤下降,甚⾄出现卡死、崩溃……
对象池实现
简⽽⾔之,就是当需要使⽤⼀个对象的时候,直接从该类对象的对象池中取出(SetActive(true)),如果对象池中⽆可⽤对象,再进⾏Instantitate。⽽当不再需要该对象时,不直接进⾏Destroy,⽽是SetActiv
e(fal)并将其回收到对象池中。下⾯直接贴下代码:
PooledObject.cs
using UnityEngine;
/// <summary>
/// 所有需要使⽤对象池机制的对象的基类
/// </summary>
public class PooledObject : MonoBehaviour
{
// 归属的池
public ObjectPool Pool { get; t; }
// 场景中某个具体的池(不可序列化)
[System.NonSerialized]
private ObjectPool poolInstanceForPrefab;
/// <summary>
/// 回收对象到对象池中
/// </summary>
public void ReturnToPool()
{
if (Pool)
{
Pool.AddObject(this);
}
el
{
Destroy(gameObject);
}
}
/// <summary>声明的意思
/// 返回对象池中可⽤对象的实例
/// </summary>
public T GetPooledInstance<T>() where T : PooledObject
{
if (!poolInstanceForPrefab)
{
poolInstanceForPrefab = ObjectPool.GetPool(this);
}
return (T)poolInstanceForPrefab.GetObject();
}
}
ObjectPool.cs
using UnityEngine;
using System.Collections.Generic;
public class ObjectPool : MonoBehaviour
{
/
/ 池中对象prefab
private PooledObject prefab;
// 存储可⽤对象的缓冲区
private List<PooledObject> availableObjects = new List<PooledObject>();
/// <summary>
/// 从池中取出对象,返回该对象
/// </summary>
public PooledObject GetObject()
{
PooledObject obj;
int lastAvailableIndex = availableObjects.Count - 1;
if (lastAvailableIndex >= 0)
{
obj = availableObjects[lastAvailableIndex];
availableObjects.RemoveAt(lastAvailableIndex);
obj.gameObject.SetActive(true);
}
el// 池中⽆可⽤obj
{
obj = Instantiate<PooledObject>(prefab);
obj.Pool = this;
}
return obj;
}
/// <summary>
/// 向池中放⼊obj
/// </summary>
public void AddObject(PooledObject obj)
{
obj.gameObject.SetActive(fal);
availableObjects.Add(obj);
}
/
// <summary>
/// 【静态⽅法】创建并返回对象所属的对象池
/// </summary>
public static ObjectPool GetPool(PooledObject prefab)
{
GameObject obj;
ObjectPool pool;
// 编辑器模式下检查是否有同名pool存在,防⽌重复创建pool
if (Application.isEditor)
{
obj = GameObject.Find(prefab.name + " Pool");
if (obj)
{
pool = obj.GetComponent<ObjectPool>();
if (pool)
{
return pool;
}
}
}
obj = new GameObject(prefab.name + " Pool");
obj = new GameObject(prefab.name + " Pool");
lock onDontDestroyOnLoad(obj);
奥沙利文简介
pool = obj.AddComponent<ObjectPool>();
pool.prefab = prefab;
网上买衣服return pool;
}
}
实战:七彩喷泉
【注:以下译⾄前⾯提到的⼀⽂,有部分删减】
1.实现效果:
2.⽣成⼤量物体
⾸先新建脚本Stuff.cs,代码如下:
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class Stuff : MonoBehaviour {
Rigidbody body;
void Awake () {
body = GetComponent<Rigidbody>();
}
}
创建Cube和Sphere,挂上Stuff脚本。并将它们做成Prefab
接下来需要创建StuffSpawner(孵化器),并挂上StuffSpawner脚本,代码如下:using UnityEngine;
public class StuffSpawner : MonoBehaviour {
public float timeBetweenSpawns;
public Stuff[] stuffPrefabs;
float timeSinceLastSpawn;
void FixedUpdate () {
timeSinceLastSpawn += Time.deltaTime;
if (timeSinceLastSpawn >= timeBetweenSpawns) {
timeSinceLastSpawn -= timeBetweenSpawns;
SpawnStuff();
}
}
void SpawnStuff () {
Stuff prefab = stuffPrefabs[Random.Range(0, stuffPrefabs.Length)];
Stuff spawn = Instantiate<Stuff>(prefab);
}
}
现在我们有了孵化器,可以在⼀个点产⽣Cube和Sphere,但这还不够。我们可以给这些stuff⼀个初始速度及⽅向。
public float velocity;
void SpawnStuff () {简 爱
Stuff prefab = stuffPrefabs[Random.Range(0, stuffPrefabs.Length)];
Stuff spawn = Instantiate<Stuff>(prefab);
spawn.Body.velocity = transform.up * velocity;
}
运⾏⼀下可以发现⼀个个物体上升⼜下降,周⽽复始。如果你倾斜⼀下孵化器,会让它看上去更像流动的物体。事实上,如果我们把多个孵化器分布在⼀个环上,将得到类似喷泉的效果。因此,新建⼀个空物体StuffSpawnerRing,挂上如下脚本:
七夕节 英语using UnityEngine;
public class StuffSpawnerRing : MonoBehaviour {
public int numberOfSpawners;
public float radius, tiltAngle;
public StuffSpawner spawnerPrefab;
void Awake () {
for (int i = 0; i < numberOfSpawners; i++) {
CreateSpawner(i);
}
}
}
void CreateSpawner (int index) {
Transform rotater = new GameObject("Rotater").transform;
rotater.SetParent(transform, fal);
rotater.localRotation =
Quaternion.Euler(0f, index * 360f / numberOfSpawners, 0f);ecf
ed是什么意思StuffSpawner spawner = Instantiate<StuffSpawner>(spawnerPrefab);
}
现在将场景中的Spawner做成prefab并删除,调整SpawnerRing的参数
3.添加销毁区(KillZone)
我们现在得到了⽆⽌尽⽣成的下落的物体。为了防⽌程序卡顿,我们需要引⼊销毁区。所有进⼊销毁区的物体都要被销毁。
创建⼀个带有Box Collider的物体,设置为触发器,为Collider设置⼀个⾮常⼤的size(如1000),并将其放置在喷泉下⽅某个位置。最后给该物体添加⼀个Tag以便能被正确识别
重新编辑Stuff.cs,添加触发器事件处理
void OnTriggerEnter (Collider enteredCollider) {人才培养 英语
if (enteredCollider.CompareTag("Kill Zone")) {
Destroy(gameObject);
}
}
看看现在的效果:
4.加⼊可变因素
⽬前我们的喷泉缺少随机性,我们可以⽤随机值代替固定值。因为我们要处理多个数据,所以让我们创建⼀个结构体来更好地实现随机化。
using UnityEngine;
[System.Serializable]
dramatize
public struct FloatRange {
public float min, max;
public float RandomInRange {
get {
return Random.Range(min, max);
}
}
}
随机化⽣成时间
public FloatRange timeBetweenSpawns;
float currentSpawnDelay;
void FixedUpdate () {
timeSinceLastSpawn += Time.deltaTime;
if (timeSinceLastSpawn >= currentSpawnDelay) {
timeSinceLastSpawn -= currentSpawnDelay;
currentSpawnDelay = timeBetweenSpawns.RandomInRange;
SpawnStuff();
}
}
随机化物体scale和rotation