Is there a more efficient way to assign parent object for many instantiated prefabs?

In the spirit of 7DRL, I’m creating a little roguelike game. I’m instantiating 3D “tile” representation of my randomly generated map and so far there is only 2 types of tiles: floor (0), and wall (everything else). In code below, I’m creating a container object for all these level “tiles” and would like to assign it as a parent for my instantiated level geometry objects.

I have narrowed down the problem to be the line that is commented out in the code. Everything is fine, until I assign a parent to those objects. With that line commented, a normal 100x100 tile map loads in under 1 second, but when I uncomment the line to make it assign the parent object it takes about 15 seconds longer. The reason I’d like to assign parent for these objects is not to have my object hierarchy tab be cluttered by thousands of objects while I run and debug my game within Unity. Is parenting objects just this resource intensive or am I doing something inefficiently? How can I improve the performance while still having non-cluttered hierarchy view in Unity?

private void LoadLevel(DungeonLevel level) {
    // TileObjects is an ArrayList of tile data
    GameObject LevelContainer = new GameObject("LevelContainer");
    Object obj;
    List<GameObject> TileInstances = new List<GameObject>();

    foreach (KeyValuePair<LevelTileLocation, LevelTile> entry in level.GetTiles()) {
    	if (entry.Value.TileType == 0) {
            // Floor
    		obj = Instantiate(TileObjects[entry.Value.TileType] as GameObject, new Vector3(entry.Key.y + 0.5f, -0.5f, -entry.Key.x - 0.5f), new Quaternion(0, 0, 0, 0));
    	} else {
            // Wall
    		obj = Instantiate(TileObjects[entry.Value.TileType] as GameObject, new Vector3(entry.Key.y + 0.5f, 0.5f, -entry.Key.x - 0.5f), new Quaternion(0, 0, 0, 0));
    	}
    	TileInstances.Add(obj as GameObject);
    }

    foreach (GameObject item in TileInstances) {
    	//item.transform.parent = LevelContainer.transform;
    }
}

Added information after more experiments:

  • This only happens inside Unity. If I “Build & Run” the game there is no big delay loading the level.
  • Reducing map size from 100x100 to 30x30 doesn’t cause the big delay anymore while assigning parent to objects.
  • I attached some code to trying to find out exact times. Here’s some data and the script modified to time the delay:

Average results over 10 tests:

  • 100x100 map (10000 objects) without assigning parent to objects: ~340ms
  • 100x100 map (10000 objects) with assigning parent to objects: ~16700ms
  • 30x30 map (900 objects) without assigning parent to objects: ~38ms
  • 30x30 map (900 objects) with assigning parent to objects: ~204ms

Modified script to measure the delay:

private void LoadLevel(DungeonLevel level) {
	int count = 0;
	Debug.Log("--- Starting to load level ---");
	System.DateTime start = System.DateTime.Now;
	
	Transform LevelContainer = (new GameObject("LevelContainer")).transform;
	Object obj;

	foreach (KeyValuePair<LevelTileLocation, LevelTile> entry in level.GetTiles()) {
		if (entry.Value.TileType == 0) {
			obj = Instantiate(TileObjects[entry.Value.TileType] as GameObject, new Vector3(entry.Key.y + 0.5f, -0.5f, -entry.Key.x - 0.5f), new Quaternion(0, 0, 0, 0));
		} else {
			obj = Instantiate(TileObjects[entry.Value.TileType] as GameObject, new Vector3(entry.Key.y + 0.5f, 0.5f, -entry.Key.x - 0.5f), new Quaternion(0, 0, 0, 0));
		}
		//(obj as GameObject).transform.parent = LevelContainer;
		count++;
	}
	System.DateTime stop = System.DateTime.Now;
	Debug.Log("Done loading " + count + " level geometry objects in " + (stop - start).TotalMilliseconds + "ms.");
}

Problem is not so noticeable with 30x30 map size but timed test data would seem to indicate that the problem is still there. I think it’s safe to say that major part of the delay is caused by assigning a parent to the objects.

Sounds like the operating system is having trouble with memory management (after it makes those 10,000 items, it stores them on disk or something.) Maybe assign in the loop :obi.transform.parent=... while the object is sill “on deck.”

Then the standard advice is also to grab a reference: Transform levT = LevelContainer.transform; (but that wasn’t your slowdown.)

[edit, new from here:]
Second guess: The usual way to make something is by Instantiating a prefab. new GameObject seems to work, but the Transform may not be properly “set in” until next frame, say.

Maybe try spawning LevelContainer from an empty prefab (and you may want to later add a layer to it, start with a small script on it… , which is easier with a prefab anyway.)

[even newer edit]:
Third idea: it may be an issue with inserting into a size 10,000 list – the Inspector may not be optimized for that. Maybe, first making a child for each row would help. Then child each item to the row: obj.go.parent = levelBase.go.Find("row"+myRow).go;.

The only optimisation i would suggest is to use Transform references instead of GameObject references. That way Unity doesn’t need to find the transform of each object. My version of your function / script would look like this:

public Transform[] TileObjects;

private void LoadLevel(DungeonLevel level)
{
    Transform LevelContainer = (new GameObject("LevelContainer")).transform;
    foreach (KeyValuePair<LevelTileLocation, LevelTile> entry in level.GetTiles())
    {
        Transform obj;
        if (entry.Value.TileType == 0)
        {
            obj = (Transform)Instantiate(TileObjects[entry.Value.TileType], new Vector3(entry.Key.y + 0.5f, -0.5f, -entry.Key.x - 0.5f), new Quaternion(0, 0, 0, 0));
        }
        else
        {
            obj = (Transform)Instantiate(TileObjects[entry.Value.TileType], new Vector3(entry.Key.y + 0.5f, 0.5f, -entry.Key.x - 0.5f), new Quaternion(0, 0, 0, 0));
        }
        obj.parent = LevelContainer;
    }
}

Keep in mind when you change the type from GameObject to Transform you have to reassign all prefabs to the TileObjects array / List