Unity GameObject 对象池

  对象池是游戏开发中经常使用的优化方法。编程

  解决问题:在某些类型的游戏,相同的对象会屡次建立和销毁,这些对象的建立十分耗时,于是,咱们会以一部份内存为代价,将这部分对象缓存起来,并不去销毁它,在须要建立时,从缓存中将先前建立好的对象取出来使用。缓存

  在Unity游戏开发中,建立GameObject是一个费时的过程,本文将针对GameObject类建立一个对象池。由于是一个很是经常使用的优化手段,于是,咱们须要把其可以方便的移植到其余项目中,不妨放到一个单一的经常使用工具的文件夹中,而后能够很方便的导出为package,并在其余项目中导入引用。函数

public class PoolOfGameObjects
{
  private List<GameObject> pool;//存放提早建立好的对象缓存区
  private GameObject sample;//这个对象池缓存的对象样本
  private int index;//未使用对象的位置坐标(好比10个对象,使用了3个,index就会变成3,即pool[3]是下一个可用得对象)
}工具

  首先,咱们须要去建立这个池,即将它的属性初始化:性能

public PoolOfGameObjects(GameObject sample)
{
  this.sample = sample;//无论怎么说,你都先要给对象池一个对象作样本,否则池子里该放什么呢?
  pool = new List<GameObject> ();//把池子初始化,否则你提早建立好的对象要放哪里呢?
  index = -1;//如今池子里是空的,因此你读取pool[-1]只会获得错误。
}测试

  接下来咱们复制一些样本的副本到池子里:优化

private void Add(int count=1)
{
  for (int i = 0; i < count; i++)
  {
    GameObject copy = GameObject.Instantiate (this.sample);
    copy.SetActive (false);//还没使用的物体就让它安静躺着池子里,否则可能会被渲染出来。
    pool.Add (copy);
  }
}this

这个方法是私有的,咱们的池子很智能,不须要外部给咱们加水,当池子的对象被耗尽,池子会本身往里添加更多能用的对象。线程

  接下来,咱们应该在何时往池里加对象呢?也许你的游戏有一个漂亮的加载界面,你也许想在这时候给池子里加上差很少够用的对象备用,也许你没有那么多内存去存放多余的对象,你甚至不清楚你会用到几个,或者你并无足够的加载时间,只想吝啬的须要一个时添加一个(在编程时,但愿你能作一个吝啬鬼)。对象

  无论你是前者仍是后者,既然想让更多的项目可以复用,咱们得能同时知足二者的须要,为了前者,咱们须要另外一个构建函数:

public PoolOfGameObjects(GameObject sample,int capacity)
{
  this.sample = sample;
  pool = new List<GameObject> ();
  Add(capacity);
  index = 0;
}

在初始化池子的时候,咱们就往里面建立了用户预计会使用到的对象副本个数。

 

  如今,你有了一个池子,也许里面还有一些对象供你使用,如今来规定一下你能用这个池子来作什么,首先,这个池子里的对象是供咱们拿出来使用的,而且若是你有良好的管理习惯,你应该认为,用完再放回原处是理所固然的,咱们必定要吝啬,若是你随意丢弃,那么当你把池子里的对象用尽时,不得再也不请求cpu再为你建立一个,每建立一个副本,池子的体积就会愈来愈大,你可能认为,我已经把GameObject清理掉了,但池子并不会知道你以及把它的管理对象清理了,它仍是会为对象保留一个位置。因此咱们须要两个基本方法,借用和归还。

  首先是借用:

public GameObject Borrow()
{
  if (index >= 0 && index < pool.Count)//index属性保存着可用对象的下标,我但愿能连续的取对象,而不是每次都去循环一遍List,假如100个中用掉99个,得须要循环多少次哦
  {
    pool[index].SetActive(true);
    return pool[index++];//借出一个后,浮标移动到仅靠着得下一位
  }
  else
  {
    Add();//不够得时候,得为池子增长一个对象
    if (index < 0)
    {
      index = 0;//这里用index++也能够
    }
    return Borrow(); //刚刚没有借到,如今增长了一个对象,总能够借给我了
  }
}

  有时候你须要一次性借多个,你总不但愿要来好多趟吧:

public GameObject[] Borrow(int count)
{
  GameObject[] order = new GameObject[count];
  for (int i = 0; i < count; i++)//不要介意这里使用的循环,假如你把这个循环放到里层,建立GameObject的时间复杂度是同样的,但会多调用几回上面的方法,这里我不想为这一点性能,多写一部分代码。
  {
    order[i] = Borrow();
  }
  return order;
}

  使用完以后,咱们还要好好的把副本还回去:

public bool Lend(GameObject gameobject)
{
  for (int i = 0; i < index; i++)//只须要在已经借出的列表前部分进行比对
  {
    if (pool[i].Equals(gameobject))//的确是这个池子里的借出去的对象
    {
      pool[i].SetActive(false);
      pool.Insert(pool.Count, pool[i]);//将对象插入到最后面待以后继续使用
      pool.Remove(pool[i]);//将原来的空出来的位置去掉
      index--;//浮标向前一位
      return true;//归还成功
    }
  }
  return false;//归还不成功
}

  一样的,归还你也不想重复好几回吧:

public GameObject[] Lend(GameObject[] gameobects)
{
  List<GameObject> notMatch = new List<GameObject>();
  for (int i = 0; i < gameobects.Length; i++)
  {
    if (!Lend(gameobects[i]))
    {
      notMatch.Add(gameobects[i]);
    }
  }
  return notMatch.ToArray();
}

归还多个的变数多一些,咱们将不匹配的对象拒退给用户。

  最后,咱们还须要有清理对象池的方法,有时候咱们没有把握好初始化池子的大小,或者一开始咱们用了不少副本,可是以后咱们须要的不多,将未使用的副本清理出去:

public void Clear()
{
  if (index >= 0)
  {
    for (int i = pool.Count-1; i >=index; i--)
    {
      if (!pool[i].activeSelf)//以防咱们删掉正在使用的对象,这本是没有必要的。通过测试,有没有这个判断不会形成误删,可是多一层保险,有时候未必是坏事
      {
        GameObject.Destroy(pool[i]);
      }
    }
    pool.RemoveRange(index, pool.Count - index);//把池子的容量恢复到恰好的尺寸
  }
}

  当你不想要这个池子的时候,须要销毁它来释放更多的内存:

public void Destory(bool force)//false时,仅仅销毁池子和未使用的对象,已经使用的对象不会被销毁,但也没法再归还回来;true时,已经使用的对象也会被强制销毁掉。
{
  int start;
  if (force)
  {
    start = 0;
  }
  else
  {
    start = index;
  }
  for (int i = pool.Count - 1; i >= start; i--)
  {
    if ((force) || (!pool[i].activeSelf))
    {
      GameObject.Destroy(pool[i]);
    }
  }
  pool.Clear();
}

  以上,就是一个GameObject对象池的基本实现,放心大胆的部署在你的各个Unity应用中,也许你还会碰到各类池,好比常见的线程池,整体的思路都是如此,具体实现会略有不一样,你还能够建立一个统一的接口,添加各有特点的池,让你的池系统更加完善

相关文章
相关标签/搜索