Sunday, May 24, 2015

Unity procedural TileMap generation without creating gameobject per tile


I've been searching internet to find an efficient way to create a procedural tilemap without creating a GameObject per tile. All the TileMap tutorials I've found are creating tiles by creating hundreds of GameObjects. Thus the hierarchy in Unity is expanding unexpectedly.


I'm pretty sure this is not the "right" way to do it. Especially after seeing the new Unity 2D tools that supports tilemaps. Unity made it so, you can create tiles in "one" gameobject, not for each tile.


So, how do I do it in the right and effiecent way?



Answer



Here's what I'm aware of:


Option 1. GameObject per tile. It's not completely horrible in certain cases. Depending on your needs, it could work.. well enough.


Option 2. A single quad or plane referencing a texture you create at run-time. You would essentially use your tile atlas texture to "paint" your map as one new texture. Depending on the size of your map of course, you might want to have multiple quads/planes each representing portions of your map.



Option 3. Create your own mesh. This would more than likely be the method you'll like most once implemented. It'll give you tons of flexibility and probably the highest performance. You would essentially create a quad per tile and set each vertex UV to map to the tiles in your tile atlas.


For Option 2, I'd suggest watching this video series by quill18creates: 3D TileMap tutorial series by quill18creates


For Option 3, this is my code, with tweaks, so it may not be perfect:


//For this, your GameObject this script is attached to would have a
//Transform Component, a Mesh Filter Component, and a Mesh Renderer
//component. You will also need to assign your texture atlas / material
//to it.

void Start() {
meshFilter = GetComponent();


BuildMesh();
}

public void BuildMesh() {
int numTiles = mapSizeX * mapSizeY;
int numTriangles = numTiles * 6;
int numVerts = numTiles * 4;

Vector3[] vertices = new Vector3[numVerts];

UVArray = new Vector2[numVerts];

int x, y, iVertCount = 0;
for (x = 0; x < mapSizeX; x++) {
for (y = 0; y < mapSizeY; y++) {
vertices[iVertCount + 0] = new Vector3(x, y, 0);
vertices[iVertCount + 1] = new Vector3(x + 1, y, 0);
vertices[iVertCount + 2] = new Vector3(x + 1, y + 1, 0);
vertices[iVertCount + 3] = new Vector3(x, y + 1, 0);
iVertCount += 4;

}
}

int[] triangles = new int[numTriangles];

int iIndexCount = 0; iVertCount = 0;
for (int i = 0; i < numTiles; i++) {
triangles[iIndexCount + 0] += (iVertCount + 0);
triangles[iIndexCount + 1] += (iVertCount + 1);
triangles[iIndexCount + 2] += (iVertCount + 2);

triangles[iIndexCount + 3] += (iVertCount + 0);
triangles[iIndexCount + 4] += (iVertCount + 2);
triangles[iIndexCount + 5] += (iVertCount + 3);

iVertCount += 4; iIndexCount += 6;
}

mesh = new Mesh();
//mesh.MarkDynamic(); if you intend to change the vertices a lot, this will help.
mesh.vertices = vertices;

mesh.triangles = triangles;
meshFilter.mesh = mesh;

UpdateMesh(); //I put this in a separate method for my own purposes.
}


//Note, the example UV entries I have are assuming a tile atlas
//with 16 total tiles in a 4x4 grid.


public void UpdateMesh() {
int iVertCount = 0;

for (int x = 0; x < mapSizeX; x++) {
for (int y = 0; y < mapSizeY; y++) {
UVArray[iVertCount + 0] = new Vector2(0, 0); //Top left of tile in atlas
UVArray[iVertCount + 1] = new Vector2(.25f, 0); //Top right of tile in atlas
UVArray[iVertCount + 2] = new Vector2(.25f, .25f); //Bottom right of tile in atlas
UVArray[iVertCount + 3] = new Vector2(0, .25f); //Bottom left of tile in atlas
iVertCount += 4;

}
}

meshFilter.mesh.uv = UVArray;
}

No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...