- Home /
Filled array becomes an empty one when I access it in another class
(Edit: I posted more info and all the code in question in post #3 then updated this post to reflect it)
Problem: Once I create and populate the array in class A, I try to use it in Class B but returns "NullReferenceException: Object reference not set to an instance of an object" when I try to see the length of the array. It seems to be empty by that point and I have no clue why.
Class A:
public class spawnMembrane : MonoBehaviour
{
public membraneVars membrane;
public int numObjects = 36;
public int size = 5; // constant coeff
public float colliderRadius = 7;
private CircleCollider2D cellCL;
[HideInInspector]
public List<membraneVars> membraneList;
[System.NonSerialized]
public membraneVars[] membraneArray;
private Rigidbody2D cellRB;
public Transform membraneCount;
// Use this for initialization
void Start()
{
cellCL = GetComponent<CircleCollider2D>();
cellCL.radius = colliderRadius;
cellRB = GetComponent<Rigidbody2D>();
membraneArray = new membraneVars[numObjects];
for (int i = 0; i < numObjects; i++)
{
membraneArray[i] = (membraneVars)GameObject.Instantiate(membrane);
membraneArray[i].transform.SetParent(membraneCount);
membraneArray[i].transform.localScale *= size * cellCL.radius / numObjects;
membraneArray[i].transform.position = PositionCircle(cellCL.offset, cellCL.radius, i*360/numObjects);
membraneArray[i].centerJoint.connectedBody = cellRB;
Debug.Log(membraneArray[i].GetInstanceID()); // up until this point its fine, the array is full
Class B:
public class membraneVars : MonoBehaviour {
public GameObject cell;
public membraneVars[] membraneObj;
void Start()
{
membraneObj = cell.GetComponent<spawnMembrane>().membraneArray; // the array exists when I access it here
Debug.Log(membraneObj.Length); // but this line doesn't work, nullreferenceexception, Object reference not set to an instance of an object
void OnJointBreak2D(Joint2D sideJointA)
{
for (int i = 0; i < membraneObj.Length; i++) // it explodes here as well if I remove the trace above
{
membraneVars temp = membraneObj[i];
Vector3 membranePos = temp.transform.position;
float distance = Vector3.Distance(transform.position, membranePos);
distanceBetweenList.Add(distance);
}
A few things that are strange about your code:
You have abstracted your classnames into "Class A" and "Class B". However based on those "names" your two script fragments don't seem to be related at all. I guess your Class A is actually called "spawn$$anonymous$$embrane", right? It would be great when this information would be more clear.
Is there a reason why "distanceBetweenList" is a class variable? $$anonymous$$aybe just to avoid recreation each time?
Since "distanceBetweenList" is a member variable you probably want to "Clear()" it before you fill it with the distance values. Otherwise each time OnJointBreak2D is called you add more and more distances to the list.
The Linq method "$$anonymous$$in" will throw an "InvalidOperationException" in case the collection is empty since it can't return any value.
If you just want to get the $$anonymous$$-distance you wouldn't need that list at all. Just do the $$anonymous$$Distance filtering in that for loop.
Something like this:
float $$anonymous$$imumDistance = float.PositiveInfinity;
var array = membraneObj.membraneArray;
for (int i = 0; i < array.Length; i++)
{
float distance = Vector3.Distance(transform.position, array[i].transform.position);
if (distance < $$anonymous$$imumDistance)
$$anonymous$$imumDistance = distance;
}
Answer by FlaSh-G · Jul 21, 2017 at 11:07 PM
Edit: Mind @Bunny83's (better) answer. I'll leave this here because it's not wrong per se, even though not the right answer to your problem.
In the case of collections, this exception means that the collection is being modified while it is being iterated over. For example, it will happen here:
foreach(var element in collection)
{
if(IsEvil(element))
{
collection.Remove(element);
}
}
The iterator that manages the loop will be in a broken state because it maybe points at the wrong element after the collection changed.
Now, in your situation, you have a Linq statement that also uses an iterator internally. So the exception is probably stating that either the Linq statement itself or some other code is changing the list.
My problem is that I don't see where that cn happen. The Linq statement itself shouldn't be a problem since it's read only on the list. And OnJointBreak2D is called from a single thread, so there shouldn't be any other code involved here. I just hope this answer gets you in some starting direction for further research.
In terms of the array apparently being empty all the time... are you sure you are refernencing the right object in membraneObj
? I could imagine you having two objects, one with a filled array and the other being referenced in the other object.
An "InvalidOperationException" is not only thrown when the collection has changed during iteration. "$$anonymous$$in" will throw an InvalidOperationException when the collection is empty. It's not possible to return any value if the collection is empty.
He could use "DefaultIfEmpty", however using a list just to find the $$anonymous$$imum distance seems to be overkill.
float $$anonymous$$imumDistance = distanceBetweenList.DefaultIfEmpty(0f).$$anonymous$$in();
@Bunny83 Learned something new today! Since your comment is a way better answer than $$anonymous$$e, do you $$anonymous$$d putting it in the form of an answer as well?
Answer by Bunny83 · Jul 22, 2017 at 09:53 AM
Yesterday i didn't had enough time to actually write an answer, especially in the beginning the cause wasn't clear -.-
Like i said in my comment above: The Linq method "Min" will throw an "InvalidOperationException" in case the collection is empty since it can't return any value.
One easy fix is to use "DefaultIfEmpty" to ensure at least one value in the collection, even when it's empty:
float minimumDistance = distanceBetweenList.DefaultIfEmpty(0f).Min();
Though it's not clear if you use the "distanceBetweenList" somewhere else. If not you shouldn't use a list at all. If you just want to find the shortest distance you can use this:
float minimumDistance = float.PositiveInfinity;
var array = membraneObj.membraneArray;
for (int i = 0; i < array.Length; i++)
{
float distance = Vector3.Distance(transform.position, array[i].transform.position);
if (distance < minimumDistance)
minimumDistance = distance;
}
If your "membraneArray" is empty you should make sure that your "membraneObj" variable is actually referencing the correct object. Maybe there are multiple "spawnMembrane " objects in the scene and you're referencing the wrong one?
I would also recommend to move the creation of your array from Awake to Start. It's just a potential error source. Since the "membraneArray" should not be initialized from the inspector and therefore it doesn't need to be serialized it would also make sense to mark the array as NonSerialized
[System.NonSerialized]
public membraneVars[] membraneArray;
void Start()
{
membraneArray = new membraneVars[numObjects];
for (int i = 0; i < numObjects; i++)
{
// ...
Answer by Cornhelius · Jul 22, 2017 at 07:48 PM
Thank you both for the replies. The Cell object is the only object of it's kind in the scene. Here's the full code, sorry for not explaining myself properly. Up until the commented Debug.Log line in class A the array I need in class B is full of stuff. When I try to access its elements in Class B in the second line of the OnJointBreak function unity returns the error I posted earlier. I can't figure out why the array is empty in Class B.
Class A:
public class spawnMembrane : MonoBehaviour
{
public membraneVars membrane;
public int numObjects = 36;
public int size = 5; // constant coeff
public float colliderRadius = 7;
private CircleCollider2D cellCL;
[HideInInspector]
public List<membraneVars> membraneList;
[System.NonSerialized]
public membraneVars[] membraneArray;
private Rigidbody2D cellRB;
public Transform membraneCount;
// Use this for initialization
void Start()
{
cellCL = GetComponent<CircleCollider2D>();
cellCL.radius = colliderRadius;
cellRB = GetComponent<Rigidbody2D>();
membraneArray = new membraneVars[numObjects];
for (int i = 0; i < numObjects; i++) //problems: double the joints
{
membraneArray[i] = (membraneVars)GameObject.Instantiate(membrane);
membraneArray[i].transform.SetParent(membraneCount);
membraneArray[i].transform.localScale *= size * cellCL.radius / numObjects;
membraneArray[i].transform.position = PositionCircle(cellCL.offset, cellCL.radius, i*360/numObjects);
membraneArray[i].centerJoint.connectedBody = cellRB;
if (i != 0)
{
membraneArray[i].sideJointA.connectedBody = membraneArray[i - 1].GetComponent<Rigidbody2D>();
if (i - 1 != 0)
{
membraneArray[i - 1].sideJointB.connectedBody = membraneArray[i].GetComponent<Rigidbody2D>();
}
}
if (i == numObjects)
{
membraneArray[i].sideJointB.connectedBody = membraneArray[i-1].GetComponent<Rigidbody2D>();
membraneArray[0].sideJointA.connectedBody = membraneArray[numObjects].GetComponent<Rigidbody2D>();
}
if (i - 1 == 0)
{
membraneArray[0].sideJointB.connectedBody = membraneArray[i].GetComponent<Rigidbody2D>();
}
Debug.Log(membraneArray[i].GetInstanceID()); // at this point I do get all the IDs of the objects I'm creating and putting into the array.
}
}
// Update is called once per frame
void Update()
{
}
Vector2 PositionCircle(Vector2 center, float radius, int angle)
{
float ang = angle;
Vector2 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.y = center.y + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
return pos;
}
}
Class B:
public class membraneVars : MonoBehaviour {
public SpringJoint2D sideJointA;
public SpringJoint2D sideJointB;
public SpringJoint2D centerJoint;
public GameObject cell; // Prefab dragged into prefab's component slot
public float membraneRadius = 4.5f;
private List<float> distanceBetweenList;
public spawnMembrane membraneObj;
// Use this for initialization
void Awake()
{
sideJointA = gameObject.AddComponent<SpringJoint2D>();
sideJointA.breakForce = 10;
sideJointB = gameObject.AddComponent<SpringJoint2D>();
sideJointB.breakForce = 10;
centerJoint = gameObject.AddComponent<SpringJoint2D>();
}
void Start()
{
membraneObj = cell.GetComponent<spawnMembrane>(); // this doesn't give error
distanceBetweenList = new List<float>();
}
// Update is called once per frame
void Update ()
{
}
void OnJointBreak2D(Joint2D sideJointA)
{
for (int i = 0; i < membraneObj.membraneArray.Length; i++) // Error when accesing any array index.
{
membraneVars temp = membraneObj.membraneArray[i];
Vector3 membranePos = temp.transform.position;
float distance = Vector3.Distance(transform.position, membranePos);
distanceBetweenList.Add(distance);
}
float minimumDistance = distanceBetweenList.Min();
distanceBetweenList.Remove(minimumDistance);
float newMinimumDistance = distanceBetweenList.Min();
for (int i = 0; i < membraneObj.membraneArray.Length; i++)
{
if (Vector3.Distance(transform.position, membraneObj.membraneArray[i].transform.position) == newMinimumDistance)
{
sideJointA = gameObject.AddComponent(typeof(SpringJoint2D)) as SpringJoint2D;
sideJointA.breakForce = 11;
sideJointA.connectedBody = membraneObj.membraneArray[i].GetComponent<Rigidbody2D>();
}
}
}
}
Your answer
Follow this Question
Related Questions
Problem with raycasts in a for loop 2 Answers
Method to loop through an unknown number of variable combinations in C# 1 Answer
Order or Sort list of files using DirectoryInfo 1 Answer
Loop through array until certain value is found. 2 Answers
Add object on trigger to array, then cast a ray at all objects in the array 0 Answers