Handling large amount of gameobjects

I am developing a top down game with a randomly generated world made out of blocks.

I am using mathf.perlinnoise to get height for the terrain.
My problem is the user can remove or place blocks any time, meaning when they dig into the terrain I need blocks to be below this. But spawning in that amount of blocks, even with a terrain height of only 5 or 10 drops me to less than 3fps on my reasonably powerful PC when I have a 100x100 (x, z) world.

I would use occlusion culling however there is also NPC’s which the player controls who might be doing something off screen, meaning I need the blocks spawned in.

So my question is what is the best way of going about this so the world can be expand forever but keeping a reasonable FPS?

Gameobject count: Around 72,000

My generation Code:

public void GenTerrain() {
        for (float x = 0.0f; x < 5; x += 0.05f) {
            for (float z = 0.0f; z < 5; z += 0.05f) {
                float y = (Mathf.PerlinNoise(x, z));
                y = Mathf.Round(y * 10f) / 10f;
                //Debug.Log(x + "" + y + "" + z);
                GameObject newGo = (GameObject)Instantiate(block, new Vector3(x * 10 * 2, y * 10, z * 10 * 2), Quaternion.identity);

                for (int i = -1; i < y * 10; i++) {
                    GameObject newGo2 = (GameObject)Instantiate(block, new Vector3(x * 10 * 2, i, z * 10 * 2), Quaternion.identity);

        //Debug.Log("Game Object Count: " + gameObjNo);


I think that’s just the easiest way to create a very performance consuming version of it. Here ate just a couple of thoughts how to improve this:

  • divide the whole world volume into chunks
  • generate the surface of every chunk as a whole
  • regenerate it if you start digging
  • only generate front faces, leave the back out
  • only generate what you see prior to culling (?)

I think the general rule here is: create only what you needed when you need it, not more