- 最后登录
- 2021-9-15
- 注册时间
- 2011-7-15
- 阅读权限
- 100
- 积分
- 50625
- 纳金币
- 53202
- 精华
- 32
|
有用的一个小功能实现方法,可以将两个物体合并为一个物体,当然,也是通过代码来实现的,具体步骤请看下文:
I made a simple (but effective) object pooling system that I want to share. If you're after code, scroll down a bit, because here is where I put in the obligatory "what I understand of the topic" spiel, in my own noobish words:
Let's say that in my game I have a turret which shoots missiles. Each of those missiles is a GameObject, and over the course of a single gameplay session that turret might shoot thousands of missiles, each of which is only 'active' for less than a second before it hits its target and is destroyed. The missiles are small, they don't take up a lot of memory, and we can have hundreds of them on-screen without impacting on performance. Woot.
The act of creating and destroying GameObjects, however, is expensive.
To prevent each of those missiles sitting around in memory forever and taking up unnecessary space, Unity will perform a process called Garbage Collection, where it scans all of the GameObjects in memory and figures out which of those missiles is no longer being used before clearing their allocated memory. Garbage Collection can cause noticeable hickups in the framerate of a game. To make matters worse, Unity decides when to perform a Garbage Collection based on how many GameObjects have been created (or memory allocated). So by rapidly firing hundreds and thousands of missiles from our turret, Unity is going to want to Garbage Collect frequently.
Object-pooling aims to rectify the problem through a couple of simple practices. Firstly, if we can pre-allocate a number of objects before gameplay begins, then we can hide the performance hickup caused by GameObject creation in a loading screen (or mission debriefing, or similar non-active screen). Also, rather than destroying a GameObject when it is no longer needed, we can just turn it off (i.e. make it invisible and move it out of the way somewhere) until we need another one.
A robust object-pooling system should include some control over how many of a particular object should be pre-allocated, and how long they should be kept in the pool for. We might only need 10 to 20 missiles for our turret, as that may be the maximum number of missiles that can be in the air at any one time, so pre-allocating a thousand missiles would make no sense. Similarly, some objects might take up a lot of memory, and those we might prefer to only allocate when needed, but keep around for a while as they may be needed again within a short time frame.
The following object-pooling system I've created is simple. I'm sure there are better, more robust options out there, and ways in which to improve the performance of this one. But it works!
Currently, it allows you to define how many of a particular GameObject should be pre-allocated, the maximum number which should be kept in memory indefinitely, and how often the pool should be checked for GameObjects exceeding that maximum number which should be destroyed. So, using the turret example: We could pre-allocate 20 missiles which are created on level load. The turret will use those 20 missiles, grabbing them from the pool when firing, and returning them to the pool when they hit the target. If suddenly our rate of fire changes and the pool becomes empty, new missiles will be created as needed. Every 'n' seconds, the pool will be checked for excess missiles which are no longer being used and destroy them. This allows me to optimise my game to find a balance between memory use and performance.
To use this system:
1. Create two new prefabs (containing empty GameObjects) called GOPool and PoolObject.
2. Assign the two scripts below to the corresponding prefabs.
3. Drag-and-drop a GOPool to the hierarchy view. This is your pool manager. You can pull an object from the pool using GOPool.Instance.Pop(string nameOfPrefab). You return an object to the pool using GOPool.Instance.Push(GameObject reference).
4. To create a pool of objects, drag-and-drop a PoolObject to the hierarchy view. Assign a Prefab directly from the project view, and define the pre-allocation count, maximum storage, and destroy frequency. Then drag-and-drop the PoolObject onto the GOPool's pool collection in the inspector. Repeat as necessary.
That should do it! There may be errors depending on how you use it. I haven't spent much time making sure it's robust or scenario independent. But hopefully someone can make some use of it. It works perfectly for me!
下面为两段代码:
/// <summary>
/// ? Boon Cotter
/// You are granted non-exclusive license to do whatever
/// you want with this code.
/// </summary>
public class GOPool : MonoBehaviour
{
#region Static
/// <summary>
/// Static GOPool instance.
/// </summary>
public static GOPool Instance;
#endregion
#region Fields
/// <summary>
/// The PoolObject collection.
/// </summary>
public List<oolObject> PoolCollection;
/// <summary>
/// The post-build collection.
/// </summary>
private Dictionary<string, PoolObject> Pool;
#endregion
#region Methods
/// <summary>
/// Local initialization.
/// </summary>
private void Awake()
{
if (Instance != null)
Destroy(this);
Instance = this;
Pool = new Dictionary<string, PoolObject>();
foreach (PoolObject po in PoolCollection)
Pool.Add(po.Prefab.name, po);
// Free memory
PoolCollection = null;
}
/// <summary>
/// Get a GameObject from the Pool. Returns null if the Pool does not contain
/// any GameObjects with the specified name.
/// </summary>
public GameObject Pop(string prefabName)
{
PoolObject po;
if (Pool.TryGetValue(prefabName, out po))
return po.Pop();
else
return null;
}
/// <summary>
/// Return a GameObject to the Pool.
/// </summary>
public void Push(GameObject gameObject)
{
PoolObject po;
if (Pool.TryGetValue(gameObject.name, out po))
po.Push(gameObject);
else
Debug.LogError("Trying to push a game object into the GOPool when no collection exists for the type " + gameObject.name + ".");
}
#endregion
}
第二段代码:
/// <summary>
/// ? Boon Cotter
/// You are granted non-exclusive license to do whatever
/// you want with this code.
/// </summary>
public class PoolObject : MonoBehaviour
{
#region Fields
/// <summary>
/// The Prefab to be pooled.
/// </summary>
public GameObject Prefab;
/// <summary>
/// The number of Prefab instances to pre allocate.
/// </summary>
public int PreAlloc = 10;
/// <summary>
/// The maximum number of Prefab instances to maintain.
/// </summary>
public int MaxStore = 10;
/// <summary>
/// The frequency at which unwanted Prefabs exceeding the maximum are destroyed.
/// </summary>
public float DestroyDelay = 5f;
/// <summary>
/// Stores a collection of recycleable GameObjects.
/// </summary>
private Stack<GameObject> Pool;
#endregion
#region Methods
/// <summary>
/// Global Initialize.
/// </summary>
private void Awake()
{
if (Prefab == null)
Debug.LogError("ERROR: PoolObject can not have a null reference for Prefab.");
Pool = new Stack<GameObject>();
for (int n = 0; n < PreAlloc; n++)
{
GameObject go = Instantiate(Prefab, Vector3.zero, Quaternion.identity) as GameObject;
go.name = Prefab.name;
Disable(go);
Pool.Push(go);
}
InvokeRepeating("oll", DestroyDelay, DestroyDelay);
}
/// <summary>
/// Get a GameObject from the stack, or create a new one if the stack is empty.
/// </summary>
public GameObject Pop()
{
GameObject go;
if (Pool.Count > 0)
{
go = Pool.Pop();
Enable(go);
}
else
{
go = Instantiate(Prefab, Vector3.zero, Quaternion.identity) as GameObject;
go.name = Prefab.name;
}
return go;
}
/// <summary>
/// Return a Unit to the stack.
/// </summary>
public void Push(GameObject gameObject)
{
Pool.Push(gameObject);
Disable(gameObject);
}
/// <summary>
/// Disable the specified GameObject.
/// </summary>
private void Disable(GameObject gameObject)
{
gameObject.GetComponentInChildren<Renderer>().enabled = false;
gameObject.SetActiveRecursively(false);
}
/// <summary>
/// Enable the specified GameObject.
/// </summary>
private void Enable(GameObject gameObject)
{
gameObject.SetActiveRecursively(***e);
gameObject.GetComponentInChildren<Renderer>().enabled = ***e;
}
/// <summary>
/// Poll the PoolObject collection for unecessary GameObjects which can be culled.
/// </summary>
private void Poll()
{
for (int n = 1; n <= Pool.Count - MaxStore; n++)
{
GameObject go = Pool.Pop();
Destroy(go);
}
}
#endregion
}
本文转自:http://blog.booncotter.com/?p=240
|
|