当同时从对象池中释放多个对象时,我在 Unity 项目中遇到 IndexOutOfRangeException。
就上下文而言,我正在开发一个 Unity 项目,其中坦克使用对象池系统来管理性能来发射炮弹。当坦克靠近时,它们会快速发射炮弹,导致池中的物体被快速连续地激活和释放。这种快速的交互似乎引发了异常。
这是我的对象池实现:
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEditor.EditorTools;
using UnityEngine;
public class GameManager : MonoBehaviour
{
private int poolSize;
//drag the object to pool in the game menu to this slot
public GameObject ObjectToPool;
public static GameManager manager;
public GameObject[] pools;
public short poolIndex;
public GameObject activePlayer;
public Camera activeCamera;
void Awake()
{
//if the static instance of the class does not exist, create one
if(manager==null){
manager=this;
}
//tag is hardcoded but i will change that later
poolSize=3*GetPooledObjectUserCount("Tank");
poolIndex=0;
InitializePool();
}
private int GetPooledObjectUserCount(string tag){
return GameObject.FindGameObjectsWithTag(tag).Length;
}
private void InitializePool(){
pools=new GameObject[poolSize];
GameObject tmp;
for(short i=0; i<poolSize; i++){
tmp=Instantiate(ObjectToPool);
tmp.SetActive(false);
tmp.AddComponent<PooledObject>().PoolIndex = i;
pools[i]=tmp;
}
}
public GameObject GetObjectFromPool(){
GameObject gameObject=pools[poolIndex];
poolIndex++;
return gameObject;
}
public void ReleaseObject(GameObject gameObject){
gameObject.SetActive(false);
GameObject latestActive=pools[poolIndex-1];
//getting release and lastest active object index in pool by getting their component
PooledObject releaseComponent=gameObject.GetComponent<PooledObject>();
PooledObject activeComponent=latestActive.GetComponent<PooledObject>();
int releaseIndex=releaseComponent.PoolIndex;
int activeIndex=activeComponent.PoolIndex;
//swap their index component value before swapping them in the array
releaseComponent.PoolIndex=activeIndex;
activeComponent.PoolIndex=releaseIndex;
//switch their place in the pool
pools[activeIndex]=gameObject;
pools[releaseIndex]=latestActive;
poolIndex--;
}
}
这是 PooledObject 类的实现,它作为组件附加到每个池化 shell,因为我不想在 shell.cs 脚本中声明变量来跟踪其池索引:
using UnityEngine;
public class PooledObject : MonoBehaviour
{
public int PoolIndex;
}
最后,附加到 shell 的 shell.cs 脚本的实现:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Shell : MonoBehaviour
{
public float speed=40;
public Rigidbody rb;
public GameObject explosion;
public Transform shellTransform;
// Start is called before the first frame update
void Awake()
{
rb=gameObject.GetComponent<Rigidbody>();
shellTransform=gameObject.GetComponent<Transform>();
}
// Update is called once per frame
void OnCollisionEnter(Collision collision)
{
GameManager.manager.ReleaseObject(gameObject);
GameObject shockwave=Instantiate(explosion, shellTransform.position, shellTransform.rotation);
Destroy(shockwave, 0.5f);
}
void Update()
{
shellTransform.forward=rb.velocity.normalized;
}
}
错误信息:
Exception caught: Index was outside the bounds of the array.
StackTrace: at GameManager.ReleaseObject (UnityEngine.GameObject gameObject) [0x0000a] in /Users/tripham/Desktop/Unity/AI Projects/AI Tank prototype/Assets/Scripts/GameManager.cs:56
UnityEngine.Debug:LogError (object)
GameManager:ReleaseObject (UnityEngine.GameObject) (at Assets/Scripts/GameManager.cs:73)
Shell:OnCollisionEnter (UnityEngine.Collision) (at Assets/Scripts/Shell.cs:23)
UnityEngine.Physics:OnSceneContact (UnityEngine.PhysicsScene,intptr,int) (at /Users/bokken/build/output/unity/unity/Modules/Physics/ScriptBindings/PhysicsContact.bindings.cs:49)
我注意到,即使有大量调用者(场景中的坦克),这种池化实现也没有问题。出现错误时的poolIndex也始终为0,因此我已经排除了poolIndex超过poolSize的可能性。仅当坦克彼此非常接近地分组时才会出现错误,因此由于炮弹相互撞击且坦克接近,炮弹被设置为激活并非常快速地释放。
我已经考虑了逻辑,但我确实找不到任何边缘情况。这让我怀疑这可能是因为线程的竞争条件导致 ReleaseObject() 的多个调用者同时访问 poolIndex。`
在函数“GetObjectFromPool”中,您可以增加 poolIndex 而不检查是否超过初始化数组的长度。您需要检查索引是否已过去并将其重置为 0
public GameObject GetObjectFromPool(){
GameObject gameObject=pools[poolIndex];
if (poolIndex + 1 >= pools.length)
poolIndex=0;
else
poolIndex++;
return gameObject;
}