- Home /
After Instantiating a Prefab I get a NullReference Error even though the Object was created
So I don´t know if I´m missing an dumb error of myself but my problem is: I´ve got an class which deserializes a json file into a tree-structure. That part is working without an error. Now while doing this i want to create Objects of another class - we just call it "CityObject" for the moment.
public class CityObject : MonoBehaviour {
private GameObject gameObject;
public ArrayList children;
public int width;
public CityObject(GameObject prefab, int newWidth)
{
gameObject = Instantiate(prefab);
children = new ArrayList();
width = newWidth;
}
}
Depending on the Information in the json file, the GameObject should get a simple cube prefab or a flat cube prefab and instantiate it. So far so good - in the unity Scene all Objects are created, so in my example there are 5 created GameObjects - 2 flat and 3 simple cubes. Code (CSharp):
public class CityBuilder : MonoBehaviour {
public GameObject plane;
public GameObject cube;
public CityObject origin;
private int maxW = 0;
private ProjectView ProcessProjectData(string jsonString)
{
ProjectView parsejson = JsonUtility.FromJson<ProjectView>(jsonString);
return parsejson;
}
private GameObject FindPrefab(string type)
{
switch (type.ToLower())
{
case "block": newPlane = false; return cube;
case "scope": newPlane = true; return plane;
default: return null;
}
}
private CityObject CreateGameObjectFromProjectData(ProjectData projectData, CityObject cityObjectParent)
{
var prefab = FindPrefab(projectData.type);
if (!prefab)
return null;
CityObject rootCityObject = new CityObject(prefab, getSubtreeWidth(projectData));
if(maxW == 0)
{
maxW = rootCityObject.width;
}
scaleObject(rootCityObject);
/* * *
* If an object has one or more children, then the children should execute this method on their own.
*/
if (projectData.children != null)
{
foreach (ProjectData data in projectData.children)
{
Debug.Log(data.id);
}
foreach (ProjectData data in projectData.children)
{
CityObject newChild = CreateGameObjectFromProjectData(data, rootCityObject);
rootCityObject.appendChild(newChild);
}
}
return rootCityObject;
}
The problem is that in line 40 i want to scale the gameObject of the cityObject like this:
private void scaleObject(CityObject node)
{
float scaleRatio = (float)(node.width/maxW);
node.gameObject.transform.localScale.Set(scaleRatio, scaleRatio, scaleRatio);
}
Then there is a NullReference Error for the node.GameObject - so there seems to be no reference between the cityObject and the GameObject. This is the part i can´t understand. Is it the Instantiate in the CityObejct Constructor? Isn´t the Instantiate creating a Reference between the "Script-Object" and the Gameobject in the unity-Scene?
Just for explanation. I don´t want to create a GameObject in the scene which should get the "CityObject" Component. What i wanna do is creating an CityObject (just in code) and this CityObject has a "gameObject" attribute which is basically just a real GameObject in the scene. After creating it, i still want to change the GameObject. I want to scale it and later on i want to reposition it but when I try to access it, there´s the NullReference Error.
What i forgot to mention: When i write a "scale method" in the CityObject class itself, i can scale the gameObject without a problem. The error just occurs if i try to access the gameObject in the "Citybuilder" class.
I don´t want to create a GameObject in the scene which should get the "CityObject" Component.
Then, why CityObject
derives from $$anonymous$$onoBehaviour? You should not have a constructor in a deriving from $$anonymous$$onoBehaviour
.
Why define a variable with name gameObject?
So confusing... gameObject is a public variable from all objects in the scene... this will only create chaos...
And then you say the gameObject containing this script = NEw instance of object prefab...
CHAOS!
gameObject = Instantiate(prefab);
Okay sorry i thought i can just quickly point out the main points of the idea and the problem and didn´t care about the names. The names will surely be changed.
Edit: This chaos i created (while trying to be as simple as i can) even was the issue. So I will definitely remember this dumb mistake next time!
Answer by Bunny83 · Jul 18, 2018 at 08:34 AM
There are countless things wrong in your code ^^.
Lets start with the most important ones:
First of all all components (which include any classes derived from MonoBehaviour) have to be created with AddComponent. They can not be created with "new". Components have a native code counter part in the C++ engine core which would be missing when you create such a class with new. Such a class would be a fake-null object (see the link at the end). As it's not attached to a gameobject it just doesn't work at all.
Since MonoBehaviours can only be created through AddComponent you can't have custom constructors for those classes. Using a constructor on MonoBehaviours is generally not recommended as the actual object creation happens on a seperate thread where you can't use the Unity API.
It looks like your "CityObject" is more like a management class that isn't attached to a gameobject so it probably shouldn't be derived from MonoBehaviour at all.
Unity's JsonUtility (or serialization system in general) has very limited capabilities. It can not serialize / deserialize tree structures. As you know JSON does not support any type information to be stored in the actual json data, so polymorphism also isn't possible.
Next, this line doesn't work as you might think:
node.gameObject.transform.localScale.Set(scaleRatio, scaleRatio, scaleRatio);
localScale is a property of a value type. What you're doing here is invoking the getter of the property and then you modify the temporary copy of the vector3 value. This will have no effect on the actual scale of the object which is stored on the native side. In order to change the scale you have to invoke the setter of the property. i.e. you have to assign a vector3 value to the localScale property. In your case you can just do:
node.gameObject.transform.localScale = Vector3.one * scaleRatio;
The "JsonUtility" is quite limited as i said. If you want to design a flexible Json format you want to use another Json parser. I've created SimpleJSON which just reads the json data into data container classes for further processing. I've also made an extension specifically for Unity to read and write some of the most common Unity types (Vector2/3/4, Quaternion, Rect, RectOffset, Matrix4x4). However there are other third party solutions as well.
Some minor things you may want to change:
The ArrayList class is an old pretty much deprecated left over class from times before generics were a thing in C# / .NET / Mono. It's generally adviced to not use it anymore. It stores untyped "object" references and always require casting when accessing elements.
For more information about the serialization issues with Unity's serialization system have a look at this documentation as well as this important blog post
Answer by Sonky108 · Jul 18, 2018 at 07:35 AM
I would rather not use constructors while inheriting from MonoBehaviour. See the link below: https://answers.unity.com/questions/862032/c-constructor-in-monobehaviour.html
Also it could be ambiguous gameObject since every MonoBehaviour already have field called gameObject. Probably you are hiding it with new field and then assigning new GameObject to the base one?
Seems like this really was the issue.. I changed the name and it worked out. Sad that in the end it really was such a dumb mistake Thank youAlso it could be ambiguous gameObject since every $$anonymous$$onoBehaviour already have field called gameObject. Probably you are hiding it with new field and then assigning new GameObject to the base one?
Glad it helped. Even though it works now I would suggest to refactor the code and don't use constructors while inheriting from $$anonymous$$onoBehaviour
No, you're drawing false conclusions here. Yes your particular null ref error was probably the hiding if the gameObject property of the Component class. However you should have a look at my answer.
btw.: I've reopened your question for two reasons: First I've already written my answer and you just closed the question right before i could post it. Second you closed it with the reason that you accepted the right answer but no answer was accepted.
Your answer
Follow this Question
Related Questions
Simultaneous Null Reference Exception and expected value 1 Answer
Instantiating class creating copies with same instance ID of 0 0 Answers
NullReferenceException and m_InstanceID == 0 on LoadLevel (C#) 1 Answer
[Closed]NullReferenceException on Object Instantiation onto game world 2 Answers
NullReferenceException 3 Answers