0
\$\begingroup\$

I made a prefab and drag drop it to the Tile Prefab slot as shown in the picture enter image description here

In my code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameGrid :MonoBehaviour
{
    private int width;
    private int height;
    private int[,] gridArray;
    private float cellSize;

    [SerializeField]
    GameObject tilePrefab;// = default;

    public GameGrid(int width, int height, float cellSize)
    {
        this.width = width;
        this.height = height;
        this.cellSize = cellSize;

        gridArray = new int[width, height];

        Debug.Log(width + " " + height);

        for(int x = 0; x < gridArray.GetLength(0); x++)
        {
            for(int z = 0; z < gridArray.GetLength(1); z++)
            {
                
                GameObject tile =Instantiate(tilePrefab);
                tile.transform.SetParent(transform, false);
                tile.transform.localPosition = GetWorldPosition(x,z);
            }
        }
    }

    private Vector3 GetWorldPosition(int x, int z, int y = 0)
    {
        return new Vector3(x, 0, z) * cellSize;
    }

}

1.It throw out error, ArgumentException: The Object you want to instantiate is null, it is very strange because I have the Prefab assigned in the inspector, what could ran wrong, and it is strange that the console did not tell me which line is wrong.

2.Another error is "You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed", it confused me because I did not new anything.

3.This line of code "tile.transform.SetParent(transform, false);" I copied from other resource which I do not fully understand, I looked to the Unity menu still did not get any idea of what it does, it setParent but what parent property this function tries to setup, is it trying to set its parent's transform or what?

Thanks very much I'm still new to Unity.

EDIT: Here is my testing.cs

public class Testing : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        GameGrid grid = new GameGrid(20, 10, 1);
    }
}

enter image description here

1.Remove the default assignment is not working either. 2.Regarding to the setParent, I do not have a parent, then what parent this child is attached to, if this function will instantiate a parent, then no information is provided to the creation of the parent, still confused.

\$\endgroup\$
1
  • \$\begingroup\$ Is is possible that the issues are caused by trying to make a MonoBehaviour (GameGrid) as if it is a regular class with a constructor? GameGrid grid = new GameGrid(20, 10, 1); as far as I remember you shouldn't make objects like that, rather have it be a new GameObject and do gameObject.AddComponent(GameGrid) or something like that. Instead of your constructor you'd put the code in Awake() or Start(). (correct me if I'm wrong please.) \$\endgroup\$
    – D.Kallan
    Jan 3 at 7:30
1
\$\begingroup\$

Another error is "You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed", it confused me because I did not new anything.

You are doing this here: GameGrid grid = new GameGrid(20, 10, 1); Creating a MonoBehaviour through a constructor does not work, because they can only be created within the context of a GameObject. So they should not have constructors. MonoBehaviours should be created through the editor as part of the scene, or if you really want to add them via code (which you shouldn't do if it can be avoided), you have to do it using the method GameObject.AddComponent.

If you try to create a MonoBehavior through a constructor anyway, then Unity will be unable to populate any of the public variables, which is why you get a NullReferenceException later. What you set in the inspector doesn't matter, because you are not using that GameGrid from your inspector. You are creating a new separate GameGrid here which has no connection to the one from the inspector.

When your MonoBehaviour requires some initialization code, then that code should be in the void Start() method. You might notice that this method accepts no parameters. When your MonoBehaviour requires some information for its initialization, you need to set that information beforehand. When you have the MonoBehaviour attached to a GameObject (which is the recommended way of doing things), make those variables public and set them in the inspector.

When you want to create an object with a MonoBehaviour at runtime, then:

  1. Create a new GameObject with GameObject newGo = new GameObject(). Or alternatively, get a reference to an existing GameObject to add the MonoBehaviour to.
  2. Create a new MonoBehaviour and attach it to that new gameObject with GameGrid grid = newGo.AddComponent<GameGrid>() as GameGrid. This creates a new component with all public variables set to the default values, but does not yet run the Start method.
  3. Set each of those public variables like this: grid.width = 20;. Or alternatively create your own public initialization method and call it.

The Start method of that new object will get executed before the next frame.

But I am a bit puzzled why you have that Testing.cs script in the first place, because your first image shows that you already created a GameObject called "Grid" with a GameGrid component attached in the editor. So there is no reason to create another one via code. While I am not sure what exactly you are trying to do, you likely don't need that script.


Regarding the line: tile.transform.SetParent(transform, false);: This attaches the newly instantiated tile to the gameObject which instantiated it. transform here refers to the Transform component of the object this script is attached to. The Transform component is also the component which manages parent/child relationships. Which is why SetParent expects the transform of the new parent.

\$\endgroup\$
9
  • \$\begingroup\$ Thanks, I understand the reason that I can not initialize class that inherits from monobehavior; However I still have problem understanding the setparent function, we provide a transform to the first argument, but imagine that what if I create another object with the same position scale and oritation, means the same transform, then which parent this prefab should be parented? There should be other connections between this transform and the the gameobject it attached to, so when I say, provide the transform, is actually saying, provide this object' transform? \$\endgroup\$
    – Dew
    Jan 3 at 10:20
  • \$\begingroup\$ The unity will do a backtrace to the gameobject the transform component belongs to? So why don't I replace the transform with gameobject, this makes more sense, thanks \$\endgroup\$
    – Dew
    Jan 3 at 10:23
  • \$\begingroup\$ @Dew I think you have some misunderstandings about what the Transform component is. Every object has its own transform. Even when two objects happen to have the exact same position, rotation and scale, they still both have their own Transform component. You also don't seem to really understand what the parent/child relationship in Unity does. An object does not have to be parented to anything, unless you have a reason to make it a child of something. \$\endgroup\$
    – Philipp
    Jan 3 at 11:05
  • \$\begingroup\$ I agree with you about the parent/child and the transform component, what I meant to say is why not provide a gameobject to the first argument of setparent function, so I can choose which object this prefab can parents, now it only allows you to choose the gameobject the script attached to as the parent, where I think is unreasonable \$\endgroup\$
    – Dew
    Jan 3 at 11:35
  • \$\begingroup\$ @Dew When you want to parent an object to another object, then pass the transform of that other object, instead of the transform of the gameobject the script is running on. \$\endgroup\$
    – Philipp
    Jan 3 at 11:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.