GameObject 池 Pool

本文介绍了一个通用的游戏对象池实现,用于Unity游戏开发中重复利用游戏对象。该对象池支持从预设实例化游戏对象,并提供了预分配功能以避免在游戏运行期间频繁调用实例化方法。此外,还支持按需创建游戏对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://vonlehecreative.wordpress.com/2010/01/06/unity-resource-gameobjectpool/

#pragma strict

// A general pool object for reusable game objects.
//
// It supports spawning and unspawning game objects that are
// instantiated from a common prefab. Can be used preallocate
// objects to avoid calls to Instantiate during gameplay. Can
// also create objects on demand (which it does if no objects
// are available in the pool).
class GameObjectPool {
	// The prefab that the game objects will be instantiated from.
	private var prefab : GameObject;
	// The list of available game objects (initially empty by default).
	private var available : Stack;
	// The list of all game objects created thus far (used for efficiently 
	// unspawning all of them at once, see UnspawnAll).
	private var all : ArrayList;
	
	// An optional function that will be called whenever a new object is instantiated.
	// The newly instantiated object is passed to it, which allows users of the pool
	// to do custom initialization.
	private var initializationFunction : Function;
	// Indicates whether the pool's game objects should be activated/deactivated
	// recursively (i.e. the game object and all its children) or non-recursively (just the
	// game object).
	private var setActiveRecursively : boolean;
	
	// Creates a pool.
	// The initialCapacity is used to initialize the .NET collections, and determines 
	// how much space they pre-allocate behind the scenes. It does not pre-populate the 
	// collection with game objects. For that, see the PrePopulate function.
	// If an initialCapacity that is <= to zero is provided, the pool uses the default
	// initial capacities of its internal .NET collections.
	function GameObjectPool(prefab : GameObject, initialCapacity : int, initializationFunction : Function, setActiveRecursively : boolean){
		this.prefab = prefab;
		if(initialCapacity > 0){
			this.available = Stack(initialCapacity);
			this.all = ArrayList(initialCapacity);
		} else {
			// Use the .NET defaults
			this.available = Stack();
			this.all = ArrayList();
		}
		this.initializationFunction = initializationFunction;
		this.setActiveRecursively = setActiveRecursively;
	}
	
	// Spawn a game object with the specified position/rotation.
	function Spawn(position : Vector3, rotation : Quaternion) : GameObject {
		var result : GameObject;
		
		if(available.Count == 0){
			// Create an object and initialize it.
			result = GameObject.Instantiate(prefab, position, rotation) as GameObject;
			if(initializationFunction != null){
				initializationFunction(result);
			}
			// Keep track of it.
			all.Add(result);
		} else {
			result = available.Pop() as GameObject;
			// Get the result's transform and reuse for efficiency.
			// Calling gameObject.transform is expensive.
			var resultTrans = result.transform;
			resultTrans.position = position;
			resultTrans.rotation = rotation;
			
			this.SetActive(result, true);
		}
		return result;
	}
	
	// Unspawn the provided game object.
	// The function is idempotent. Calling it more than once for the same game object is 
	// safe, since it first checks to see if the provided object is already unspawned.
	// Returns true if the unspawn succeeded, false if the object was already unspawned.
	function Unspawn(obj : GameObject) : boolean {
		if(!available.Contains(obj)){ // Make sure we don't insert it twice.
			available.Push(obj);
			this.SetActive(obj, false);
			return true; // Object inserted back in stack.
		}
		return false; // Object already in stack.
	}
	
	// Pre-populates the pool with the provided number of game objects.
	function PrePopulate(count : int){
		var array : GameObject[] = new GameObject[count];
		for(var i = 0; i < count; i++){
			array[i] = Spawn(Vector3.zero, Quaternion.identity);
			this.SetActive(array[i], false);
		}
		for(var j = 0; j < count; j++){
			Unspawn(array[j]);
		}
	}
	
	// Unspawns all the game objects created by the pool.
	function UnspawnAll(){
		for(var i = 0; i < all.Count; i++){
			var obj : GameObject = all[i] as GameObject;
			if(obj.active)
				Unspawn(obj);
		}
	}
	
	// Unspawns all the game objects and clears the pool.
	function Clear(){
		UnspawnAll();
		available.Clear();
		all.Clear();
	}
	
	// Returns the number of active objects.
	function GetActiveCount() : int {
		return all.Count - available.Count;
	}
	
	// Returns the number of available objects.
	function GetAvailableCount() : int {
		return available.Count;
	}
	
	// Returns the prefab being used by this pool.
	function GetPrefab() : GameObject {
		return prefab;
	}
	
	// Applies the provided function to some or all of the pool's game objects.
	function ForEach(func : Function, activeOnly : boolean){
		for(var i = 0; i < all.Count; i++){
			var obj : GameObject = all[i] as GameObject;
			if(!activeOnly || obj.active){
				func(obj);
			}
		}
	}
	
	// Activates or deactivates the provided game object using the method
	// specified by the setActiveRecursively flag.
	private function SetActive(obj : GameObject, val : boolean){
		if(setActiveRecursively)
			obj.SetActiveRecursively(val);
		else
			obj.active = val;
	}
}

在Unity中,创建和销毁`GameObject`是一个较为耗费资源的操作,尤其是在频繁实例化或删除大量对象的情况下。为了提升性能,我们可以利用**对象Object Pooling)模式**来复用已经存在的未激活的游戏对象而不是不断地生成新对象。 以下是使用C#脚本实现一个简单但实用的对象步骤: ### 步骤一:设计对象类结构 首先我们定义一个通用的对象管理器类 `ObjectPoolManager` 和具体的容器 `ObjectPool<T>` ,其中T表示泛型参数代表想要缓存的目标组件类型比如预制件(Prefab). ```csharp using System.Collections.Generic; using UnityEngine; public class ObjectPoolManager : MonoBehaviour { private Dictionary<string, ObjectPool<GameObject>> pools = new Dictionary<string, ObjectPool<GameObject>>(); public static ObjectPoolManager Instance { get; private set; } void Awake() { if (Instance == null) Instance = this; else Destroy(gameObject); DontDestroyOnLoad(this.gameObject); // 确保该单例不随场景切换而被销毁 } } ``` 然后是实际负责维护一组特定类型对象的内部类: ```csharp [System.Serializable] public class ObjectPool<T> where T : Component { [SerializeField] private GameObject prefab; // 预制件引用 [SerializeField] private int poolSize = 5; // 初始大小 private Queue<T> _poolQueue = new Queue<T>(); private Transform parentTransform; public void Initialize(Transform transformParent){ for(int i=0;i<poolSize;++i){ CreatePooledItem(); } parentTransform = transformParent; } /// <summary> /// 获取可用项从队列顶部移出 /// </summary> /// <returns></returns> public T GetPooledItem(){ if(_poolQueue.Count > 0){ var item=_poolQueue.Dequeue(); item.gameObject.SetActive(true); return item ; }else{ Debug.LogWarning("超出预设容量"); return CreateNewPooledItem(); // 或者你可以在这里增加更多的对象到子里... } } private T CreatePooledItem(){ T obj=(Instantiate(prefab,parentTransform)).GetComponent<T>(); obj.gameObject.SetActive(false); _poolQueue.Enqueue(obj); return obj; } private T CreateNewPooledItem(){ T newObj =(Instantiate(prefab,parentTransform)).GetComponent<T>(); newObj.transform.SetParent(parentTransform,false); return newObj; } /// <summary> /// 返回一项回到队列底部以便后续重用 /// </summary> /// <param name="item"></param> public void ReturnToPool(T item){ item.gameObject.SetActive(false); _poolQueue.Enqueue(item); } } ``` 注意上述代码片段需要您根据自身需求调整字段属性以及初始化配置. ### 步骤二:注册和访问对象 为了让各个部分能够方便地获得所需种类的对象服务,我们需要完善我们的全局管理者 `ObjectPoolManager`. 我们添加了两个关键函数——用于向系统登记新建好的子(`Register`)还有从中取出指定名称对应的实例 (`GetPool`). 同时也别忘了让每个能在适当时候调用自己的构造方法开始运作。 ```csharp // ...继续上面的ObjectPoolManager类... /// 注册一个新的对象 public void Register(string key,ObjectPool<GameObject> pool){ if (!pools.ContainsKey(key)){ pools.Add(key,pool); pool.Initialize(transform); // 初始化传入父级转换信息 } } /// 根据键获取相应对象 public ObjectPool<GameObject> GetPool(string key){ return pools.TryGetValue(key,out var result)?result:null; } ``` 现在有了完整的框架之后就可以很方便得把它应用起来了! 例如当我们想建立一批子弹并且希望它们能循环往复地出现在战场上而不必每次都全新制造的话就完全可以依赖这个功能强大的工具啦~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值