1
\$\begingroup\$

I'm new to Unity. I've created an algorithm to procedurally generate a circle-shaped tunnel made of primitive cubes GameObjects. The problem is that the game is very laggy and I can understand why: there are thousands of objects to render.

What I'd like to know is if there's a better approach to this. For example, would be better if instead of rendering the single cubes I create a ring custom component and use that instead?

Are there any other solutions?

I'm using Unity 5.3.4f1.

EDIT

Here's what I've done so far. This works the way I want but the game is really laggy because (I assume) there are lots of objects to render.

public class TerrainGenerator : MonoBehaviour {

private static int MAX_RING_NUMBER = 1000;

private static int TUNNEL_SLOPE = 5;

// Use this for initialization
void Start () {
    int y = 0;
    for (int z = 0; z < MAX_RING_NUMBER; z++) {

        createRing(0, y, z, 10);
        // Every TUNNEL_SLOPE cubes the tunnel goes down a block.
        if (z % TUNNEL_SLOPE == 0)
            y--;
    }
}

// Update is called once per frame
void Update () {

}

private void createCube(int x, int y, int z){
    GameObject cube = GameObject.CreatePrimitive (PrimitiveType.Cube);
    cube.transform.position = new Vector3 (x, y, z);
    cube.GetComponent<Renderer>().material.color = new Color(Random.Range(0,255),Random.Range(0,255),Random.Range(0,255));
}

// Midpoint circle alghoritm.
void createRing( int x0, int y0, int z, int radius){
    int x = radius;
    int y = 0;
    int decisionOver2 = 1 - x;   // Decision criterion divided by 2 evaluated at x=r, y=0

    while( y <= x )
    {
        createCube(x + x0,  y + y0, z); // Octant 1
        createCube(y + x0,  x + y0, z); // Octant 2
        createCube(-x + x0,  y + y0, z); // Octant 4
        createCube(-y + x0,  x + y0, z); // Octant 3
        createCube(-x + x0, -y + y0, z); // Octant 5
        createCube(-y + x0, -x + y0, z); // Octant 6
        createCube(x + x0, -y + y0, z); // Octant 7
        createCube(y + x0, -x + y0, z); // Octant 8
        y++;
        if (decisionOver2<=0)
        {
            decisionOver2 += 2 * y + 1;   // Change in decision criterion for y -> y+1
        }
        else
        {
            x--;
            decisionOver2 += 2 * (y - x) + 1;   // Change for y -> y+1, x -> x-1
        }
    }
}

}
\$\endgroup\$
1
\$\begingroup\$

Yes, performance will be much better with fewer objects. I'd recommend making one gameobject for a single ring (using the info in Philipp's excellent answer), and saving it as a prefab. Then instantiate copies of the prefab as needed.

If you want a quick and dirty way to create the prefab from a runtime object, you can use the method in Joe-2's answer here: http://answers.unity3d.com/questions/21666/creatingsaving-prefabs-during-runtime.html

Once that's done, the next significant optimization (assuming you're continually destroying and creating rings - ignore if you're not) will be to pool and reuse them instead of creating new ones all the time.

\$\endgroup\$
  • \$\begingroup\$ This is exactly what I was looking for. I tried this and the performances significantly increased. Also the use of the rings instead of a single mesh allows me to continuously generate the level at runtime. I'm really interested in the pooling mechanism you are descriving... Do you mean to instantiate only an amount (let's say a 1000) and then changing the position? Am I right? Could you tell me a little bit more on why this will improve the performance instead of creating and destroying the rings continuosly? Thank you, accepted and +1. \$\endgroup\$ – Aurasphere Mar 30 '16 at 22:25
  • 1
    \$\begingroup\$ Yes, that's the essence of it. The simplest implementation would be to initially spawn however many objects you want (based on performance constraints, how many objects a player might be able to see at a time, etc), and then when you need more, move the oldest one to a its new position. In a slightly more advanced version, you'd disable objects that leave scope instead of destroying them, and add them to an "available" list. Then when you need another, you remove the first "available" object from the list, move it to its new location, and re-enable it. \$\endgroup\$ – Cameron Oltmann Apr 1 '16 at 7:27
  • 1
    \$\begingroup\$ The reason this improves performance is because instantiating objects is actually quite costly. Destroying objects also have to be garbage collected, which has its own additional cost. Pooling systems remove both these problems. The overhead from tracking the pool, and disabling/enabling/moving objects as needed, is far less than the performance hit for instantiating and garbage collecting. \$\endgroup\$ – Cameron Oltmann Apr 1 '16 at 7:29
1
\$\begingroup\$

Unity allows you to generate 3d models procedurally using C# scripts.

The key for this is the component Mesh.

  1. Set mesh.vertices to to an array of 3d coordinates in form of Vector3's. These are all the corner-points of your 3d model.
  2. If you want the 3d model to have textures, set mesh.uv to provide a list of the same number of 2d coordinates in form of Vector2's to set the UV mapping (aka texture coordinates) for each vertex.
  3. Set mesh.triangles to define which of the coordinates you provided via vertices are the corners of each polygon. A block of 3 integers defines one polygon.
  4. After you set up everything, call mesh.RecalculateNormals() and mesh.RecalculateBounds().
  5. When you want to use a shader with a normal map, you also need to set mesh.tangents to a list of Vector4's. Unfortunately Unity can not do that for you, and you have to calculate the tangents yourself. In my current project I am using s C# port of this function even though I got to admit that I don't fully understand it.

To get that 3d mesh into the game, create a new game object with a MeshFilter and a MeshRenderer component (and optionally a MeshCollider if you want collision checking) and assign your home-made mesh to the .sharedMesh property of the mesh filter.

All of this does of course assume that you actually need procedural generation. When the mesh always looks the same, it might be far easier to just create it with your favorite 3d modeling program and import it. Unity supports a very wide variety of 3d model formats.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your answer. My concern though was about performance. Is this solution going to reduce the load time and also will it be easier to render a big object with lots of vertices or lots of smaller objects? To be more clear, I'll post what I've done so far in the question. \$\endgroup\$ – Aurasphere Mar 27 '16 at 17:41
  • \$\begingroup\$ @Aurasphere I can't promise you anything, but when you used to build a tunnel out of thousands of tiny cube game objects, then replacing them all with a single game object with a single mesh will likely improve performance a lot. First, less objects to update, maintain and check for collisions. Second, likely far less polygons than all the cubes (although that of course depends on your generation algorithm). \$\endgroup\$ – Philipp Mar 27 '16 at 17:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.