C# 如何调整我的对象池以重置我加载的每个新场景?
根据答案中给出的规格更新游戏后,我现在收到以下错误消息: Assets\ObjectPooler.cs(79,41):错误CS0103:名称“pool”在当前上下文中不存在。我理解为什么它不起作用,因为它是在另一个方法中声明的,但是我如何更改代码来访问这个变量呢 提前谢谢 我还没有将发行版和.sceneloaded脚本添加到游戏中 这是我的ObjectPooler脚本:C# 如何调整我的对象池以重置我加载的每个新场景?,c#,unity3d,object-pooling,scene-manager,C#,Unity3d,Object Pooling,Scene Manager,根据答案中给出的规格更新游戏后,我现在收到以下错误消息: Assets\ObjectPooler.cs(79,41):错误CS0103:名称“pool”在当前上下文中不存在。我理解为什么它不起作用,因为它是在另一个方法中声明的,但是我如何更改代码来访问这个变量呢 提前谢谢 我还没有将发行版和.sceneloaded脚本添加到游戏中 这是我的ObjectPooler脚本: using System.Collections; using System.Collections.Generic; usi
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPooler : MonoBehaviour
{
[System.Serializable]
public class Pool
{
public string tag;
public GameObject prefab;
public int size;
}
#region Singleton
public static ObjectPooler Instance;
private void Awake()
{
// Already another instance?
if (Instance)
{
Destroy(this.gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(this.gameObject);
}
#endregion
public List<Pool> pools;
public Dictionary<string, Queue<GameObject>> poolDictionary;
// Start is called before the first frame update
//change this method to make it work everytime level is loaded
private Dictionary<string, Pool> prefabPools;
private void Start()
{
foreach (var pool in pools)
{
Queue<GameObject> objectPool = new Queue<GameObject>();
for (int i = 0; i < pool.size; i++)
{
GameObject obj = Instantiate(pool.prefab);
obj.transform.SetParent(transform);
obj.SetActive(false);
objectPool.Enqueue(obj);
}
prefabPools.Add(pool.tag, pool);
poolDictionary.Add(pool.tag, objectPool);
}
}
public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation)
{
if (!poolDictionary.ContainsKey(tag))
{
Debug.LogWarning("Pool with tag" + tag + " doesn't exist.");
return null;
}
GameObject objectToSpawn;
// check if there are objects left otherwise insteantiate a new one
if (poolDictionary[tag].Count > 0)
{
objectToSpawn = poolDictionary[tag].Dequeue();
}
else
{
objectToSpawn = Instantiate(pool.prefab);
objectToSpawn.transform.SetParent(transform);
}
objectToSpawn.transform.position = position;
objectToSpawn.transform.rotation = rotation;
objectToSpawn.SetActive(true);
IPooledObject pooledObj = objectToSpawn.GetComponent<IPooledObject>();
if (pooledObj != null)
{
pooledObj.OnObjectSpawn();
}
return objectToSpawn;
}
}
以下是从脚本调用池中游戏对象的方式:
objectPooler.SpawnFromPool("Ball", spawnPoints[randomSpawnPoint].position, Quaternion.identity);
它的工作方式是,当我在游戏的不同场景之间切换对象池时,对象池的一个新实例会被创建或重置,它们会出现在屏幕上,并根据脚本进行操作,而不是像被破坏一样出现和操作。需要注意的是,如果其中一个objectpooler和对象在第一个场景中表现正常,并且仅在游戏在场景之间转换时开始抛出错误消息,那么当我编辑脚本以实例化对象而不使用objectpooler时,如中所示:
Instantiate(interact[Interact], spawnPoints[randomSpawnPoint].position,
Quaternion.identity);
在我将游戏对象预置存储在一个名为interaction的公共数组中并通过索引调用它们的地方,脚本似乎也能工作。然而,为了防止代码变得昂贵,我需要能够使用ObjectPooler 首先,我不会将刚刚生成的对象重新排队,而是将其从队列中移除。然后,当队列为空时,生成新的附加对象以供使用,而不是重用可能已经存在的对象
private Dictionary<string, Pool> prefabPools;
private void Start()
{
foreach (var pool in pools)
{
Queue<GameObject> objectPool = new Queue<GameObject>();
for (int i = 0; i < pool.size; i++)
{
GameObject obj = Instantiate(pool.prefab);
obj.SetActive(false);
objectPool.Enqueue(obj);
}
prefabPools.Add(pool.tag, pool);
poolDictionary.Add(pool.tag, objectPool);
}
}
public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation)
{
if (!poolDictionary.ContainsKey(tag))
{
Debug.LogWarning("Pool with tag" + tag + " doesn't exist.");
return null;
}
GameObject objectToSpawn;
// check if there are objects left otherwise insteantiate a new one
if(poolDictionary[tag].Count > 0)
{
objectToSpawn = poolDictionary[tag].Dequeue();
}
else
{
objectToSpawn = Instantiate(pool.prefab);
}
objectToSpawn.transform.position = position;
objectToSpawn.transform.rotation = rotation;
objectToSpawn.SetActive(true);
IPooledObject pooledObj = objectToSpawn.GetComponent<IPooledObject>();
if (pooledObj != null)
{
pooledObj.OnObjectSpawn();
}
return objectToSpawn;
}
然后确保您的池对象也不会因为使其成为此对象的子对象而被破坏
GameObject obj = Instantiate(pool.prefab);
obj.SetParent(transform);
而且在
objectToSpawn = Instantiate(pool.prefab);
objectToSpawn.SetParent(transform);
你应该有某种Release
方法来禁用和重新排队对象,而不是像这样破坏它们
public void Release(GameObject obj)
{
obj.SetActive(false);
// Assuming pool.tag euqals obj.tag
// In general I would rather go by type instead
poolDictionary[obj.tag].Enqueue(obj);
}
最后,当场景发生变化时,对每个对象运行此操作
private void Awake()
{
...
SceneManager.sceneLoaded -= OnSceneLoaded;
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDestroy()
{
...
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
foreach(var child in transform)
{
if(!child.activeInHierachy) continue;
Release(child);
}
}
或者,如果您不想让它们始终是池对象的子对象,您也可以在列表中跟踪它们,如
private List<GameObject> currentlySpawnedObjects = new List<GameObject>();
//...
public void Release(GameObject obj)
{
currentlySpawnedObjects.Remove(obj);
obj.SetActive(false);
// here you should still make it a child so it doesn't get destroyed
// when the scene is changed
obj.SetParent(transform);
// Assuming pool.tag euqals obj.tag
// In general I would rather go by type instead
poolDictionary[obj.tag].Enqueue(obj);
}
//...
// call this BEFORE switching scenes
public void ReleaseAll()
{
foreach(var child in currentlySpawnedObjects)
{
Release(child);
}
}
谢谢你的评论。只是个问题。我应该把释放方法放在哪里?我在对象池器本身中设置了一个名称,如果是,我应该将其放置在对象池器中的什么位置?如果将其放入脚本中,我应该替换什么内容呢?所有显示的方法都是
对象池器的一部分
所以是;)你可能不需要更换任何东西。。我只是说,如果场景中不再需要该对象,您将调用Release
,而不是Destroy
。在场景被改变之前释放所有对象是必需的,但一般来说,我不想破坏任何游戏对象,因为我不想让它们出现。我将它们设置为非活动状态。由于场景在统一中的工作方式,当场景被移除时,它们通常在场景转换之间被破坏。这是否会改变您的解决方案?是的。。这正是我为..添加的Release
方法。。我也不销毁对象,而是将其设置为非活动+1。使它们再次可用于产卵和2。使它们成为ObjectPooler
的子对象,以便它们继承其DontDestroyOnLoad
属性,直到它们再次生成(因此可能会移动到另一个父对象)。好的,我将尝试实现解决方案并返回给您。谢谢你,伙计
private void Awake()
{
...
SceneManager.sceneLoaded -= OnSceneLoaded;
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDestroy()
{
...
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
foreach(var child in transform)
{
if(!child.activeInHierachy) continue;
Release(child);
}
}
private List<GameObject> currentlySpawnedObjects = new List<GameObject>();
//...
public void Release(GameObject obj)
{
currentlySpawnedObjects.Remove(obj);
obj.SetActive(false);
// here you should still make it a child so it doesn't get destroyed
// when the scene is changed
obj.SetParent(transform);
// Assuming pool.tag euqals obj.tag
// In general I would rather go by type instead
poolDictionary[obj.tag].Enqueue(obj);
}
//...
// call this BEFORE switching scenes
public void ReleaseAll()
{
foreach(var child in currentlySpawnedObjects)
{
Release(child);
}
}
private Dictionary<string, Pool> prefabPools;
private void Start()
{
foreach (var pool in pools)
{
Queue<GameObject> objectPool = new Queue<GameObject>();
for (int i = 0; i < pool.size; i++)
{
GameObject obj = Instantiate(pool.prefab);
obj.SetActive(false);
objectPool.Enqueue(obj);
}
prefabPools.Add(pool.tag, pool);
poolDictionary.Add(pool.tag, objectPool);
}
}
public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation, Transform parent = null)
{
if (!poolDictionary.ContainsKey(tag))
{
Debug.LogWarning("Pool with tag" + tag + " doesn't exist.");
return null;
}
GameObject objectToSpawn;
// check if there are objects left otherwise insteantiate a new one
if(poolDictionary[tag].Count > 0)
{
objectToSpawn = poolDictionary[tag].Dequeue();
}
else
{
objectToSpawn = Instantiate(prefabPools[tag].prefab);
}
if(parent)
{
objectToSpawn.SetParent(parent, false);
}
// you could also decide to use localPosition in case parent is set
objectToSpawn.transform.position = position;
objectToSpawn.transform.rotation = rotation;
objectToSpawn.SetActive(true);
IPooledObject pooledObj = objectToSpawn.GetComponent<IPooledObject>();
if (pooledObj != null)
{
pooledObj.OnObjectSpawn();
}
currentlySpawnedObjects.Add(objectToSpawn);
return objectToSpawn;
}