Unity ----- 对象池GameObjectPool

孙广东 2014.6.28

非常早之前看到的外国文章,认为不错,分享一下。 对象池在AssetStore中也是有非常多插件的,可是有些重了。自己写一个轻量的岂不是非常好。

当你须要创建大量某种类型对象时,它能够方便地又一次使用单个对象,而不是不断地 创建/销毁(操作会导致大量的垃圾回收)。此ObjectPool脚本,生成和回收您的游戏对象的对象池。
代码:https://github.com/UnityPatterns/ObjectPool
http://unitypatterns.com/resource/objectpool/

特性:
?选择性地池对象基于prefab类型
?简单和表达式语法 对于 实例化和回收
?轻松地预先实例化对象,以防止执行时实例化
?搜索和跟踪全部生成/池子内的在场景中实例化的

怎么使用?
通常情况下,当您实例化并销毁prefabs的实例,您在执行时不断创建新的对象和摧毁它们。这可能会导致执行时垃圾回收和偶尔的帧速率下降。

ObjectPool 能够防止这样的。通过预先实例化。而不是被摧毁然后又一次生成对象!

生成池子中的对象:
比如,假设我有能发射子弹对象的炮塔。我能创建 10 枚一模一样的子弹对象并又一次使用。子弹将永远不会被销毁。仅仅是取消激活、须要生成时又一次激活他们。
ObjectPool要做到这一点,你仅仅要调用CreatePool() 生成指定Prefab的对象。


public class Turret : MonoBehaviour
{
    public Bullet bulletPrefab;
    void Start()
    {
        //Create a pool with 10 pre-instantiated bullets in it
        bulletPrefab.CreatePool(10);
        //Or you could also pre-instantiate none, and the system will instantiate them as it needs them
        bulletPrefab.CreatePool();
    }
}

如今你能够使用ObjectPool类中的Spawn() and Recycle() 来取代Instantiate() and Destroy()方法。比如。当抢发射子弹时。我生成子弹实例:

public class Turret : MonoBehaviour
{
    public Bullet bulletPrefab;
    public void ShootBullet()
    {
        //Spawn a bullet at my position with my rotation
        bulletPrefab.Spawn(transform.position, transform.rotation);
    }
}

当你想要回收这个实例,在你想要消失的组件或者对象上调用Recycle()函数。

当子弹发生碰撞时,我们将回收它。

public class Bullet : MonoBehaviour
{
    void OnCollisionEnter(Collider other)
    {
        //De-activate the object and return it to the spawn pool
        gameObject.Recycle();
        //You can also use this:
        //this.Recycle();
    }
}

函数Spawn()被创建的对象的引用, 所以你能够存储这个对象或者调用它的其它方法. 这个函数不像Unity的 Instantiate(), 你不须要强制类型转换得到 GameObject or Component.


小心用回收的对象!
如今,您的对象正在被回收和又一次使用,你必须要小心,由于假设您的实例有不论什么变量被改变。您必须手动重置它们。你能够通过使用Unity提供的的 OnEnable() 和 OnDisable() 函数,仅仅要您的实例使用spawned or recycled函数将会触发OnEnable() 和 OnDisable()。

比如。这是不对的:

public class Bullet : MonoBehaviour
{
    public float travelDuration;
    float timer = 0; //Only gets set to zero once!
    void Update()
    {
        timer += Time.deltaTime;
        if (timer >= travelDuration)
        {
            gameObject.Recycle();
        }
    }
}

为什么不对呢?

由于我们的timer变量计数。但永远不会返回到零!所以当回收并在此使用时,它已经不是最開始的状态了。

我们能够非常easy解决问题:

public class Bullet : MonoBehaviour
{
    public float travelDuration;
    float timer;
    void OnEnable()
    {
        //Correct! Now timer resets every single time:
        timer = 0;
    }
    void Update()
    {
        timer += Time.deltaTime;
        if (timer >= travelDuration)
        {
            gameObject.Recycle();
        }
    }
}

如今我们的子弹正确重置他的timer变量。

你能通过对象引用预制体,在曾经是不能的
GameObject如今有组件的扩展方法

InitialPoolSize 參数已加入到 CreatePool()函数总,并告诉它要预先实例化多少的对象,这些都是最初被隐藏和延迟生成的。
您还能够将 ObjectPool 附加到一个游戏对象,通过inspector 设置要 预先实例化 的预制体
附加函数已经加入了用于搜索/统计 实例对象

假设你想要 RecycleAll 要使用派生类型,然后更改这:
var active = instance.prefabLookup.Keys.Where(p => p.GetType() == typeof(T).ToList();
to:
var active = instance.prefabLookup.Keys.Where(p => p is T).ToList();
也适用于 GetAllOfType 的相似的变化






posted @ 2017-05-19 14:48  yjbjingcha  阅读(891)  评论(0)    收藏  举报