How to deal with a missing reference when an object referred each frame from an array is destroyed along the way?
Context:
In the Start method, I instantiate a flock of flying bugs and store them in a list called "agents".
In the Update method, I iterate on each agent in the list to: 1) store their transforms, 2) get a list of its neighbors' transforms and 3) calculate the movement of the flock based on the agent and its neighbors.
To check for neighbors, I used a method called GetNearbyObject to which I passed each agent. This method looks for colliders in a certain radius and store them in an array of colliders. Then, it iterate on each collider in the array to get their transforms. It returns a list of transforms that is needed to calculate the movement of the flock for each frame.
Problem:
My problem is that my flying bugs can be destroyed anytime when the player shoots at them. When a bug is killed, I get a "Missing Reference" error saying that one of my bug has been destroyed while the code still tries to access it.
It seems that since my bugs' colliders are stored in the array of colliders each frame, I can't manage to tell my code to ignore the newly destroyed bug's collider. I tried to add some if statements to check if a bug agent returns null and then remove it from both lists, but it doesn't remove the Missing Reference. I've also tried to disable the bugs' colliders when they return null after being destroyed. Doesn't work either.
If someone could help me get rid of this error, that would help me a lot in my learning curve of Unity and C#. Here is the concerned block of code:
void Update()
{
foreach (FlockAgent agent in agents)
{
if (agent == null)
{
agents.Remove(agent);
}
List<Transform> context = GetNearbyObjects(agent);
if (agent == null)
{
context.Remove(agent.transform);
}
Vector3 move = behavior.CalculateMove(agent, context, this);
move *= driveFactor;
if (move.sqrMagnitude > squareMaxSpeed)
{
move = move.normalized * maxSpeed;
}
agent.Move(move);
}
}
List<Transform> GetNearbyObjects(FlockAgent agent)
{
List<Transform> context = new List<Transform>();
Collider[] contextColliders = Physics.OverlapSphere(agent.transform.position, neighborRadius);
foreach (Collider c in contextColliders)
{
if (c == null)
{
context.Remove(c.transform);
}
else if (c != agent.AgentCollider)
{
context.Add(c.transform);
}
}
return context;
}
It is very simple with lists like you are using....you see in the script where you destroy the bug.....just before destroying....reference the script with the list in in.....and do this..
referencedScript.bugList.Remove(bugToDestroy.GetComponent<Collider>());
obviously this is all kind of a pseudo code, you need to adjust the na$$anonymous$$g according to your script
Answer by Will_Dev · Aug 14, 2021 at 06:15 AM
Need help with the same problem, gonna watch this thread
Answer by junk_rat_angel · Aug 14, 2021 at 07:30 AM
I think youve basically got this problem https://www.techiedelight.com/remove-elements-from-list-while-iterating-csharp/
Indeed. But my problem is about removing an item from an array of colliders during iteration, while the article you kindly share is about removing item from a list. I've tried to adapt the solution to my problem with no luck.
I've tried to use a list of colliders instead by checking nearby colliders with OnTriggerEnter(). For some reason, it messes with the movement and physics of my flock (because of the isTrigger parameter I suppose). I've also tried to implement a for loop to check if an element returns null and then destroy/clear the array slot. No more luck.
I'm still looking for an answer. Thanks for your help though.
you cannot simply delete the elements from an array....the solution is to implement something like this...let's assume originalArray is an array of strings...obviously you can implement this to any kind of array....you just need to replace the List.Remove part with this..
string elementToDestroy = "y";
int elementInstances = 0;
foreach(string el in originalArray)
{
if(el == elementToDestroy)
{
elementInstances ++;
}
}
string[] copyArray = new string[originalArray.Length - elementInstances];
int currentElement = 0;
for(int i=0; i < originalArray.Length; i++)
{
if(originalArray[i] != elementToDestroy)
{
copyArray[currentElement] = originalArray[i];
currentElement++;
}
}
originalArray = copyArray;
sure this is a bit bulky, but i guess it's something to be expected when working with arrays
Hi rage_co. Thank for your insight. I've try to implement your suggestion and the thing is: I dont know in advance what will be the elementToDestroy (which are colliders) has it depends on which enemy or enemies the player will kill first. Therefore, I cannot declare the giving variable in advance nor run your solution. I've try to workaround this using a list of colliders to store every elementToDestroy. Here is my logic code:
List<Collider> elementToDestroy = new List<Collider>;
int elementInstances = 0;
foreach(Collider item in originalArray)
{
if(el == null)
{
elementToDestroy.Add(item)
elementInstances ++;
}
}
Collider[] copyArray = new Collider[originalArray.Length - elementInstances];
int currentElement = 0;
for(int i=0; i < originalArray.Length; i++)
{
if(originalArray[i] != elementToDestroy[i])
{
copyArray[currentElement] = originalArray[i];
currentElement++;
}
}
originalArray = copyArray;
Unfotunatly, this code returns en error: "ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection". The error is link to the line 46 in the code block from the orignal post, where return context is called. I can't manage to get my head around this problem. Any suggestion would be much appreciated?