How to have a 2D world with 4194304 tiles or more?

This question is SOLVED :slight_smile:

Using Unity 4.0 (free), C#, Windows 7 pro;

Currently, I am trying to make a game system for 2D side-scroller. If you know the game called Terraria, you might have a better idea of what I’m trying to do here.
screen shot from terraria

This is a screen shot from Terraria. In this game, the player can dig, and place a block (or tile whatever you want to call it). Like Minecraft, but in very flat 2D world.
The game has a world around 4000 wide x 1000 high blocks. (a small world that is)
So my goal is to create a 2D side scroller with possibly 4 million blocks.

Anyhow, I’ve created a prototype using a free 2D framework called Orthello, which comes with a sprite class called OTSprite. It worked until the world exceeded 100x100 blocks.

The structure was very simple and inadequate. I simply created an empty gameObject and added the blocks. The framework handled the drawing calls and calls were reduced to 1 (other calls from blocks within the visible area were batched).
the prototype

Above image is my prototype. I’ve made a noise based 2D terrain data generator so the ground is in kind of a weird shoe looking shape. In this picture, there are 881 blocks and it is working fine, but far far from my goals. So if you are planning on doing a similar idea like me, don’t. It won’t work.

Now, I’d like to talk about a solution that actually works. One idea is that I will build a 2D version of what is known as a voxel engine. Since the word voxel refers to a 3D box, I might call it a “Planel Engine”, because we are adding bunch of planes instead of boxes. I know, there’s no such word.

So far, thanks forum members who answered to my previous question. I hope this cleared some confusion…

So now my project has shifted from having bunch o’ orthello sprites to procedural mesh creation.

I’ve read some websites and learned the basic of the mesh, vertices, triangles, UVs and I’ve made this image below so far. (no primitives used. code only)

Work in progress

This is 64 planes by 64 planes (or 4096 blocks), which is my hypothetical chunk size. (if not too large?? Not based on anything, so I’m open to any suggestions.)

This is my 3rd rev.

I’ve found some solution to my previous questions, and my world is getting closer to 4 mil tiles.
Here is 1 chunk of mesh drawn with textures.
3rd stage

In a loop where I determine the block type for my game’s block coordinate, I set the UVs for each plane. I’m using a text texture to display just a few patterns(in the picture, I’m using transparent block, and a red block).

First I had an error where my transparent PNG texture became all white, but I fixed that with photoshop, adding an alpha layer. It says PNG24 instead of PNG32, so it is kind of confusing.
Now, to turn ON/OFF the block, I just need to find a block in my game block coordinates, and change preset UVs from A to B.

Regarding the chunk size. 1 mesh can ONLY HOLD, up to 65000 vertices. I got this error.

Mesh.vertices is too large. A mesh may not have more than 65000 vertices.

So, from this error, my chunk is now determined as 100x100 blocks. ish. This may change.
Now, my last question is, how should I make an entire world, instead of a chunk.
And I think it is already given to me by @Bunnybomb7670 !

Thank you guys, I really appreciate all the comments and answers.

Rev4.

My code was getting quite messy, so I was re-factoring my code. And I added a different solution to my mesh creation regarding the empty planes.

My first approach was to … Set a transparent texture UVs for empty space. My second approach is not adding the vertices/UVs/triangles the whole set, but add vertices count up, so it can be re-filled later on (hopefully).

This is the picture from my second approach. No mesh in the transparent area.

Second approach

Now re-factoring is done, I can move on to chunks… will post soon.

** Rev 5 **

Took me little longer than I expected… I implemented the chunks. Now they are all good to go!
I also implemented collision for character movement test. Now each tile(or block) is ready to be walked. Thanks guys!!

Chunked!

Well, firstly, you need to start by initializing an array of however many blocks in your “chunk” and store the ID values in an array. You may also want to store the chunk X and Y size in variables if you want to change your chunk size alot.

public int[,] voxels = new int[32,32];

You need to then assign each one a value dependant on your generation, so for example, if you wanted to use your generator, you can do :

voxels[posx,posy]=1;

which sets that value to 1. Now you will need to cycle through your entire mesh with a for loop and some mesh code:

    for( int i = 0;i<32;i++){
    
    for( int j = 0;j<32;j++){
    
    if(voxels[i,j]!=0){ // we have a solid block
    
    // RENDER A FACE
    
    }
    
    }
    }

Looking back at that code there, you can see that we are looping through each value of the array and checking their ID value, if its 0, we will not render a face and therefore emptying that space.

Now, what if you want to add colours? well, its not that hard, you can set the vertex colours of the mesh and use a vertex colour shader to render the faces the colours you want. To do this, you can have an extra array of block ID references to get the colour of the block, for example,

public Color[] voxeldata = new Color[256];

this array of colours will store 256 different colours, you can have a function to assign lots of these upon start, calling a function like this at start :

public void setDataValues() {

voxeldata[0]= new Color(0.1,0.2,0.3);
voxeldata[1]= new Color(0.5,0.4,0.5);
voxeldata[2]= new Color(0.2,0.6,0.2);
//.... and so on
}

Wondering why im using decimal values? This is because a Color stores the values differently, to convert a 0-255 RGB colour into a Color you need to do R/255,G/255,B/255 or use a Color32(255,255,255,255) which goes Red Green Blue Alpha ( transparency ).

now, you can assign a meshes vertex colours easy during the mesh generation by also storing the colors in an array and calling

Mesh.colors = arrayname

Now, you can ( hopefully ) create your own 2d terraria like system in this way!

for placing and destroying a block, you will need to use this system and catch the block that was hit, compare its coordinate and set the coordinate to 0 or if placing a block, set it to the ID value and update the mesh. If your thinking that updating the whole mesh is inefficient, It is by far more efficient than trying to find the vertices that you have edited because a mesh is a lot harder to search through than you think, so your best off updating the whole mesh after editing a block.

public void addBlock (Vector2 Position,int ID) {

// Position = where the block was placed

voxels[Position.x,Position.y] = ID;

UpdateWholeMeshFunctionHere();

}

would be a possible way for this. After this, you may also want to do colliders? I do colliders by doing this function :

Mesh mesh = GetComponent<MeshFilter>().mesh; 

// after the mesh is set, then it calls :

updateCollider(mesh);

// this function does this :

	public void updateCollider (Mesh mesh) {
		
		GetComponent<MeshCollider>().sharedMesh = null;
		GetComponent<MeshCollider>().sharedMesh = mesh;
		
	}

The reason why I set it to null first is because , if this is not the first meshcollider update, it will see that the sharedmesh is already the mesh and wont update it, so instead, I assign it to null and re assign it. Im not sure how your collision will be working, but this method wont really work with a flat terrain, so I guess you’ll have to figure that out yourself.

I hope this code has helped you.

I’m guessing the player can never see all 4m blocks at the same time? You need to divide this up into chunks and dynamically load them as you move around, keeping less on screen at a time.

Static batching takes time when the level loads and uses a lot of memory as it has to create a single mesh from all of your scene meshes. Dynamic batching does that every frame. Just skip that whole bit. Wasting memory and processing in having a transform etc for each tile when the tiles are uniformly laid out is expensive and pointless.

You should skip using the Orthello framework for this part and generate your own meshes/uvs either in the editor or at runtime for blocks of say 100x100 tiles (depends totally on the size of the tiles) and then dynamically load those blocks when they are in view.

10,000 x 10,000 = 100,000,000 tiles. That’s going to take a huge amount of memory if you have them all loaded at once! Just a transform for each of those would be GBs of memory.

I have an asset in the Asset Store called MeshBaker that would be useful for combining tiles into larger chunks. You can combine the tiles in advance or dynamically at runtime. It may help with your problem.