1
\$\begingroup\$

In my game, I procedurally generate tile map mesh and modify it when travelling around the "world". Each tile is a quad. So they each, have their own UV points. (Obviously)

I am using Unity for this, and in Unity, i can't have more then 65k vertices in a single mesh.

So i made this "tile mesh", chunk based. It's still a single mesh. But while I am creating it, i first create the "chunks", (which are 16x16 quad meshes), then i "merge" the chunk meshes to create the tile map mesh.

I keep these "chunk meshes" in a list. So basicly i merge all the meshes in the list when i want to "update" the tilemap mesh.

While i am travelling in the world, i can remove farthest chunk from list, and add new chunkes when it's necessary. List capacity is fixed to 9, so i can never exceed the "65k vertices limit".

Now the problem is, when i zoom out in game, there becomes almost 40k vertices in the scene since whole mesh becomes rendered. (Actually since it's a single mesh, it always renedered as whole, but the animation doesn't occur out side of the camera view)

But when i zoom out, there are like atleast (6x6)x(16x16) = 9.216 tiles. Which makes 36.864 vertices (and UV points).

Now updating few tiles (so few uv points) is easy. But updating the whole 36k point takes like 1-2 seconds. Which is really bad for updating animation in that way.

The code snippet for single quad uv update is here

public void SetTile(Coord chunkCoord, Coord tileCoord, Sprite sprite)
    {
        Vector2[] uv = meshFilter.mesh.uv;
        Rect spriteRect = sprite.textureRect;

        int chunkIndex = SearchForChunk(chunkCoord);

        if (chunkIndex == -1)
        {
            Debug.LogError("Chunk not found");
            return;
        }

        int ui = chunkIndex * (16 * 16) + (tileCoord.Y * 16 + tileCoord.X);
        ui *= 4;

        uv[ui] = ToUV(new Vector2(spriteRect.xMin, spriteRect.yMax), sprite.texture);
        uv[ui + 1] = ToUV(new Vector2(spriteRect.xMin, spriteRect.yMin), sprite.texture);
        uv[ui + 2] = ToUV(new Vector2(spriteRect.xMax, spriteRect.yMin), sprite.texture);
        uv[ui + 3] = ToUV(new Vector2(spriteRect.xMax, spriteRect.yMax), sprite.texture);

        meshFilter.mesh.uv = uv;
    }

It takes chunk coordinate, and finds that chunk in the chunk list, so i can get the index of that chunk mesh for UV array.

The sprite that i get as a parameter is in a "sprite atlas", so i call a helper function to convert it's rectangle to uv points.

That's how i update a single tile's UV points.

But It's too slow, how can i optimize it, make it faster?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Without knowing more about your code, its difficult to say why it would be running that slow. I can't imagine it taking 1-2 seconds to update 32000 UVs, that's not that large of a number, so my guess is that its doing something else horrible because of the fact that you are updating that many UVs.

Either way, your solution will be similar. Since all the UVs need to get updated by the same values each frame anyway, I would take one of the standard shaders and modify it so that it can offset the UVs by some fixed amount. Then I would render your mesh with the new shader, and you can just update the offset U and offset V values, and it won't require modifying the actual UVs of each polygon.

\$\endgroup\$
0
\$\begingroup\$

One thing to think about here is that you are updating the entire mesh UV state for each tile you modify. I'd be sure to check and make sure it's not refreshing the mesh after each tile. you might return the UV updated array one level up and marge them all at once. Basically restructure so you calculate all, then apply the changes to the chunk/mesh.

I also notice you're searching for the chunk index once for every tile you update. Do this outside of the tile loop, and pass the result to the SetTile() so you only do this search once per chunk instead of once per tile.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .