Creating game worlds with Lists in code rather than as actual GameObjects to save resources?

I am familiar with the common method of creating a world for a 3D platformer, where game objects are painted on a grid to create a world, as illustrated here:

However, in doing so, it seems apparent that you may paint hundreds of game objects just to create a small level environment. This creates hundreds of game objects for your project to manage.

It strikes me that a more efficient way would be to instead code the environment grid as a List. So for example, I imagine you could have, for a 3D rendered 2D platformer (like a Mario game) where the character moves hypothetically in X (horizontal) and Y (vertical):

class WorldObjects{

    public position Vector3;
    public int objectType; //could be an enum representing types of blocks, enemies, ladders, etc.
    public bool currentlyRendered = false;
    //other parameters needed perhaps
}

//program the list of world objects here:
List<WorldObjects> worldObjects = new List<WorldObjects>();
worldObjects.Add(new WorldObject{position= new Vector3(0,0,0), objectType=0});

Then on an Update loop, you can poll the camera view range to see what range of positions should be visible and need to be rendered (assuming you only want to render things in vision range).

So for example, camera bounds can be used by camera.ViewportToWorldPoint for each of the four corners of your viewport rect. I understand the z of the Vector3 for this is how far the camera is from the play area plane. Bottom-left of the viewport is (0,0); the top-right is (1,1).

So you can run routinely after each given increment of camera motion:

Vector3 bottomLeft = camera.ViewportToWorldPoint(new Vector3(0, 0, cameraDistanceToGamePlay));
Vector3 topRight = camera.ViewportToWorldPoint(new Vector3(1, 1, cameraDistanceToGamePlay));

for (int i=0;i<worldObjects.Count;i++){
    bool shouldRender = false;
    if (worldObjects.position.x > bottomLeft.x && worldObjects.position.x < topRight.x){
        if (worldObjects.position.y > bottomLeft.y && worldObjects.position.y < topRight.y){
            shouldRender = true;

        }
    }
    if (shouldRender){
        if (!worldObjects[i].currentlyRendered){
            renderThisObject(worldObjects[i]); //function to take needed object from object pool and render it
        }
    }
    else{
        unRenderThisObject(worldObjects[i]); // function to cannibalize the object used to render it and return it to the `free` pool
    }
    worldObjects[i].currentlyRendered = shouldRender;
       
}

I recall seeing a video of how MineCraft was able to manifest such massive worlds and I believe it used a similar method. I am just wondering before I undertake this if this is a normal approach am I considering or foolish. Or if other people are routinely doing this how they are handling it compared to how I imagined above.

In principle, recycling objects and enemies, environment, etc. can keep the total GameObject count down which might be more efficient overall for weak environments like mobile. It might allow you to make each GameObject more detailed or more complex without overloading the system.

This called Occlusion culling and is already built into Unity. You don’t really need to implement it yourself. Pretty much every modern game under the sun uses this technique.

Here it is in the manual: Unity - Manual: Occlusion culling

Occlusion culling has to do with only rendering objects within Camera view.

That is not what I’m talking about.

I am talking about not even having the GameObjects at all for objects outside of Camera view by instead implementing object pools for them and recycling those GameObjects based on lists of which elements “exist” in the game world and which ones are needed at any time.

So for example, if you had 50 enemies of type Enemy1 in a level, rather than having 50 Game Objects to represent these 50 enemies, you could just have 2-3 (or however many might be visible at any one time).

Or if you were using tile blocks to build a platformer world, rather than having 500+ Game Objects for all these tiles, you could just have 50 or however many might be visible at one time and recycle them.

You will get good results using a sprite renderer on the few objects that do exist. It works well if you had a grid based world. This way it’s easier to determine which points are in cameras view, and restore objects to those points.

I’m not entirely sure on the question you’re asking but it looks like you know the answer! So I would suggest putting it into practice with a few prototypes.

Right, I understand what you’re saying a bit more.

The issue then becomes one of maintaining state where required. In your enemies example, you’d need to separate pretty much all the relevant data from the pool of game objects. Which is pretty much a data driven approach, and then you’re off into DOTS land.

You may be onto something, but it would require a super specific project for such egregious optimisation to be needed.