博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Unity GameObject 对象池
阅读量:5131 次
发布时间:2019-06-13

本文共 3981 字,大约阅读时间需要 13 分钟。

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

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

  在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);
  }
}

这个方法是私有的,我们的池子很智能,不需要外部给我们加水,当池子的对象被耗尽,池子会自己往里添加更多能用的对象。

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

  不管你是前者还是后者,既然想让更多的项目能够复用,我们得能同时满足两者的需要,为了前者,我们需要另一个构建函数:

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应用中,也许你还会碰到各种池,比如常见的线程池,总体的思路都是如此,具体实现会略有不同,你还可以创建一个统一的接口,添加各有特色的池,让你的池系统更加完善

转载于:https://www.cnblogs.com/Vongolar/p/7996448.html

你可能感兴趣的文章
autopep8
查看>>
GIT在Linux上的安装和使用简介
查看>>
Android 官方新手指导教程
查看>>
幸运转盘v1.0 【附视频】我的Android原创处女作,请支持!
查看>>
[51nod] 1199 Money out of Thin Air #线段树+DFS序
查看>>
Red and Black(poj-1979)
查看>>
安装 Express
查看>>
存储(硬件方面的一些基本术语)
查看>>
观察者模式
查看>>
Weka中数据挖掘与机器学习系列之基本概念(三)
查看>>
Win磁盘MBR转换为GUID
查看>>
大家在做.NET B/S项目的时候多用什么设技术啊?
查看>>
Java SE和Java EE应用的性能调优
查看>>
leetcode-Sort List
查看>>
中文词频统计
查看>>
了解node.js
查看>>
想做移动开发,先看看别人怎么做
查看>>
Eclipse相关集锦
查看>>
虚拟化架构中小型机构通用虚拟化架构
查看>>
继承条款effecitve c++ 条款41-45
查看>>