Unity3D:如何在没有生成器单例的情况下进行对象池化
Unity3D: How to do object pooling without a spawner singleton
通常,如果你使用对象池,你会像 this 视频中那样创建一个单例。
看了this视频后,我才发现单例有多乱。有没有其他方法可以在不使用单例的情况下进行对象池化?我想改为使用事件。
您需要将池保存在非单例的 class 中,并根据您的事件处理游戏对象池。
关于用事件调用它们,“我想用事件”不是一个很具体的问题。您需要将事件设置为侦听(方法订阅)并在代码中应发生的任何地方调用它们,这就是调用方法。我建议,如果您不清楚这一点,请尝试使用统一事件(OnTriggerEnter、Update
中的 if(Input.GetMouseButtonDown(0))
等),直到您深入了解主题以理解它们并制作自己的事件在需要时使用 c# 事件或 UnityEvents
。
找到两个模板脚本、一个池和事件处理程序来处理场景中的对象。您可以在一个空场景中检查它们,分别附加两个游戏对象,以及您想要在池中的对象,按 'space' 和 'A' 从池中创建,然后 return 到池中分别
池管理员:
using System.Collections.Generic;
using UnityEngine;
public class PoolManager : MonoBehaviour
{
private Queue<GameObject> objPool;
private Queue<GameObject> activeObj;
private int poolSize = 10;
public GameObject objPrefab;
void Start()
{
//queues init
objPool = new Queue<GameObject>();
activeObj = new Queue<GameObject>();
//pool init
for (int i = 0; i < poolSize; i++)
{
GameObject newObj = Instantiate(objPrefab);
objPool.Enqueue(newObj);
newObj.SetActive(false);
}
}
public GameObject GetRandomActiveGO() {
GameObject lastActive = default;
if (activeObj.Count > 0)
lastActive = activeObj.Dequeue();
else {
Debug.LogError("Active object queue is empty");
}
return lastActive;
}
//get from pool
public GameObject GetObjFromPool(Vector3 newPosition, Quaternion newRotation)
{
GameObject newObject = objPool.Dequeue();
newObject.SetActive(true);
newObject.transform.SetPositionAndRotation(newPosition, newRotation);
//keep actives to be retrieved
activeObj.Enqueue(newObject);
return newObject;
}
//return to pool
public void ReturnObjToPool(GameObject go)
{
go.SetActive(false);
objPool.Enqueue(go);
}
}
事件处理器:
using UnityEngine;
public class EventHandler : MonoBehaviour
{
public delegate GameObject OnSpacePressed(Vector3 newPosition, Quaternion newRotation);
public OnSpacePressed onSpacePressed;
public delegate void OnAKeyPressed(GameObject go);
public OnAKeyPressed onAKeyPressed;
public PoolManager poolManager;
void Start()
{
onSpacePressed = poolManager.GetObjFromPool;
onAKeyPressed = poolManager.ReturnObjToPool;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
onSpacePressed?.Invoke(new Vector3(0, 0, 0), Quaternion.identity);
}
//here I get a random active, however this would be called in the specific objects remove circumstances,
//so you should have a reference to that specific gameobje when rerunrning it to the pool.
if (Input.GetKeyDown(KeyCode.A))
{
GameObject go = poolManager.GetRandomActiveGO();
onAKeyPressed?.Invoke(go);
}
}
}
编辑:单例模式
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
protected static T _instance;
public static T instance
{
get
{
if (_instance == null)
{
_instance = GameObject.FindObjectOfType<T>();
if (_instance == null)
{
_instance = new GameObject(typeof(T).Name).AddComponent<T>();
}
}
return _instance;
}
}
}
通常,如果你使用对象池,你会像 this 视频中那样创建一个单例。 看了this视频后,我才发现单例有多乱。有没有其他方法可以在不使用单例的情况下进行对象池化?我想改为使用事件。
您需要将池保存在非单例的 class 中,并根据您的事件处理游戏对象池。
关于用事件调用它们,“我想用事件”不是一个很具体的问题。您需要将事件设置为侦听(方法订阅)并在代码中应发生的任何地方调用它们,这就是调用方法。我建议,如果您不清楚这一点,请尝试使用统一事件(OnTriggerEnter、Update
中的 if(Input.GetMouseButtonDown(0))
等),直到您深入了解主题以理解它们并制作自己的事件在需要时使用 c# 事件或 UnityEvents
。
找到两个模板脚本、一个池和事件处理程序来处理场景中的对象。您可以在一个空场景中检查它们,分别附加两个游戏对象,以及您想要在池中的对象,按 'space' 和 'A' 从池中创建,然后 return 到池中分别
池管理员:
using System.Collections.Generic;
using UnityEngine;
public class PoolManager : MonoBehaviour
{
private Queue<GameObject> objPool;
private Queue<GameObject> activeObj;
private int poolSize = 10;
public GameObject objPrefab;
void Start()
{
//queues init
objPool = new Queue<GameObject>();
activeObj = new Queue<GameObject>();
//pool init
for (int i = 0; i < poolSize; i++)
{
GameObject newObj = Instantiate(objPrefab);
objPool.Enqueue(newObj);
newObj.SetActive(false);
}
}
public GameObject GetRandomActiveGO() {
GameObject lastActive = default;
if (activeObj.Count > 0)
lastActive = activeObj.Dequeue();
else {
Debug.LogError("Active object queue is empty");
}
return lastActive;
}
//get from pool
public GameObject GetObjFromPool(Vector3 newPosition, Quaternion newRotation)
{
GameObject newObject = objPool.Dequeue();
newObject.SetActive(true);
newObject.transform.SetPositionAndRotation(newPosition, newRotation);
//keep actives to be retrieved
activeObj.Enqueue(newObject);
return newObject;
}
//return to pool
public void ReturnObjToPool(GameObject go)
{
go.SetActive(false);
objPool.Enqueue(go);
}
}
事件处理器:
using UnityEngine;
public class EventHandler : MonoBehaviour
{
public delegate GameObject OnSpacePressed(Vector3 newPosition, Quaternion newRotation);
public OnSpacePressed onSpacePressed;
public delegate void OnAKeyPressed(GameObject go);
public OnAKeyPressed onAKeyPressed;
public PoolManager poolManager;
void Start()
{
onSpacePressed = poolManager.GetObjFromPool;
onAKeyPressed = poolManager.ReturnObjToPool;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
onSpacePressed?.Invoke(new Vector3(0, 0, 0), Quaternion.identity);
}
//here I get a random active, however this would be called in the specific objects remove circumstances,
//so you should have a reference to that specific gameobje when rerunrning it to the pool.
if (Input.GetKeyDown(KeyCode.A))
{
GameObject go = poolManager.GetRandomActiveGO();
onAKeyPressed?.Invoke(go);
}
}
}
编辑:单例模式
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
protected static T _instance;
public static T instance
{
get
{
if (_instance == null)
{
_instance = GameObject.FindObjectOfType<T>();
if (_instance == null)
{
_instance = new GameObject(typeof(T).Name).AddComponent<T>();
}
}
return _instance;
}
}
}