4
\$\begingroup\$

I am trying to generate a simple map like this. Everything works as I want, but I am new to programing. Is it a good way to do something like this? And if not, could someone explain to me where and why it is bad to do something like this? And how bad is it on performance?

public GameObject cube;
public GameObject cube1;
public GameObject cube2;
Vector3 here;

void Start () {
    for (int y = 0; y < 50; y++) {
        for (int x = 0; x < 10; x++) {
            here = new Vector3(x,y,0);
            int change = (int)Random.Range(0,19);
            switch (change){
            case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10:
                Instantiate(cube,here,Quaternion.identity);
                break;
            case 11: case 12: case 13: case 14: case 15: case 16:
                Instantiate(cube1,here,Quaternion.identity);
                break;
            case 17: case 18:
                Instantiate(cube2,here,Quaternion.identity);
                break;
            }
        }
    }

Running this code generates something random like this:

Cropped screenshot of a rectangle containing red, green, and blue blocks

Instantiate takes 3 things (GameObject you want to make, coordinates where to make it, and rotation), and it makes a copy of GameObject - prefab in those coordinates.

  • Cube - Red
  • Cube1 - Green
  • Cube2 - Blue

The reason for all of this is that I want to learn to make something random likea map area of the game.

\$\endgroup\$
  • \$\begingroup\$ Although you asked this in the wrong site, this is really bad for performance, and very badly written indeed. \$\endgroup\$ – Fatih BAKIR Mar 18 '15 at 23:03
  • \$\begingroup\$ What kind of map is it that you want to instantiate? What does the Instantiate method do? What is the end result of this? Some more context about what kind of game we're dealing with here would make it a lot easier for us to come up with better answers. \$\endgroup\$ – Simon Forsberg Mar 18 '15 at 23:07
  • \$\begingroup\$ edited my post. \$\endgroup\$ – user2351722 Mar 18 '15 at 23:14
  • 1
    \$\begingroup\$ Could you post the GameObject code and the code for the Instantiate method as well? (don't worry about them being too long) \$\endgroup\$ – Simon Forsberg Mar 18 '15 at 23:22
  • 1
    \$\begingroup\$ GameObject is a PREFAB there is no code. Just make any prefab in unity and add to the file with script. Instantiate is unity method. \$\endgroup\$ – user2351722 Mar 18 '15 at 23:25
3
\$\begingroup\$

As a first step, I think this is cleaner than the switch

if (change <= 10)
{
    Instantiate(cube, here, Quaternion.identity);
}
else if (change <= 16)
{
    Instantiate(cube1, here, Quaternion.identity);
}
else
{
    Instantiate(cube2, here, Quaternion.identity);
}

But there's still repetition with the calls to Instantiate. I'd suggest creating a new method like this

private GameObject GetRandomCube()
{
    var change = (int)Random.Range(0, 19);
    if (change <= 10)
    {
        return cube;
    }

    if (change <= 16)
    {
        return cube1;
    }

    return cube2;
}

And then use it like this in your loop

Instantiate(GetRandomCube(), here, Quaternion.identity);

If here isn't being used elsewhere in the class, move its declaration inside the inner loop, or just use it in the function call. Now your method looks like this

void Start()
{
    for (var y = 0; y < 50; y++)
    {
        for (var x = 0; x < 10; x++)
        {
            Instantiate(GetRandomCube(), new Vector3(x, y, 0), Quaternion.identity);
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Isn't this just as in-efficient a the original code? Calling instantiate in a loop like this for a map is most definitely a bad idea ... Why do people keep up voting this? The performance of this must be horrific if you try to scale it. \$\endgroup\$ – War Apr 18 '15 at 20:48
3
\$\begingroup\$

Is it really necessary to create an object of each tile?

In many 2d map-based games, the tiles are not objects, but each tile is only an int in a 2d array.

I don't know Unity, but I feel that it should be possible to render a 2d map in a better way than creating a GameObject for each tile.

If possible, I would make your code something more like this:

int[][] map = new int[50][10];
for (int y = 0; y < map.Length(1); y++) {
    for (int x = 0; x < map.Length(2); x++) {
        int change = (int)Random.Range(0,19);
        int type = 0;
        if (change <= 10)
        {
            type = 1;
        }
        else if (change <= 16)
        {
            type = 2;
        }
        else
        {
            type = 3;
        }
        map[y][x] = type;
    }
}
\$\endgroup\$
  • \$\begingroup\$ thanks trying this method as well. My question would be, how better for performance is using If instead of switch? \$\endgroup\$ – user2351722 Mar 19 '15 at 13:48
  • 1
    \$\begingroup\$ Logically when the compiler gets hold of this it'll basically turn out the same when compiled .. this offers literally no performance improvement whatsoever. all it does is compact the switch conditions in to 3 if / else conditions, ok I suppose if you only ever want 3 tile types in your map ... I guessed that this would likely not be the case in my answer. \$\endgroup\$ – War Apr 18 '15 at 20:53
1
\$\begingroup\$

Warning: this code sample may not work, its all from my head and is only meant to give a general idea of how to generate a basic 2d tile map.

You will want to take this further in to the 3d side for cubes but the process is the same just with the extra dimension.

God no please don't keep this up ...

It looks like you're creating a tile based map using cubes but lets start with just flat tiles so you can get your head round this. So lets see how we can do this much faster ...

Firstly start by generating a map as data, this should be as simple as generating a int[x,y] where the values are between 0 and some max.

You can do it the way you are doing above or use something like perlin noise (appears to be the most commonly used method these days) ...

int tileTypes = 20;
float tileStep = 1f / (float)tileTypes;
var map = new int[10,50];

for (int y = 0; y < 50; y++) {
    for (int x = 0; x < 10; x++) {
         map[x,y] = Random.Range(0, tileTypes - 1);
    }
}

Once you have that data you can generate a mesh with 1 quad per tile ...

// generate the vertex info for your map mesh
var verts = new List<Vector3>();

// also generate uv's
var uvs = new List<Vector2>();

for (int y = 0; y < 50; y++) {
        for (int x = 0; x < 10; x++) {
             verts.AddRange(new [] {
                 new Vector3 { x,y,0 },
                 new Vector3 { x+1,y,0 },
                 new Vector3 { x,y+1,0 },
                 new Vector3 { x+1,y+1,0 }
             });

            // depends on atlas layout but lets assume a single line of textures
            // but essentially this is where your map comes in ...
            var type = map[x,y];
            uvs.AddRange(new [] {
                new Vector2 { tileStep * type, 0 },
                new Vector2 { tileStep * (type+1), tileStep * type },
                new Vector2 { tileStep * type, 1 },
                new Vector2 { tileStep * (type+1), 1 }
            });
        }
    }

// index the verts
// numbers of verts / verts per quad * indexes needed per quad
var indexes = new int[(verts.Count / 4) * 6];
var index = 0;

            // setup index info for each face
            for (int vert = 0; vert < verts.Count; vert += 4)
            {
                // tri 1
                indexes[index++] = vert;
                indexes[index++] = vert + 1;
                indexes[index++] = vert + 2;

                // tri 2
                indexes[index++] = vert;
                indexes[index++] = vert + 2;
                indexes[index++] = vert + 3;
            }

// create the mesh
var mesh = new Mesh {
   vertices = verts.ToArray(),
   uv = uv.ToArray()
};

mesh.SetTriangles(indexes.ToArray(), 0);

/// and finally ... draw!
GetComponent<Renderer> ().material.mainTexture = someTileAtlas;
GetComponent<MeshFilter>().sharedMesh = mesh;
\$\endgroup\$
  • \$\begingroup\$ i will be reading your code and learning what it does for next week :DD \$\endgroup\$ – user2351722 Mar 18 '15 at 23:36
  • \$\begingroup\$ yeh sorry about the bad ass brain dump ... essentially it generates a map which it puts in an array, then generates a "tile" (quad made up of 2 triangles) for each map entry. You will need to get your head round texture atlases but the idea is that the tiles will pick a different texture from your atlas based on the map data so if map[x,y] = 0 you get the first texture in the atlas rendered on that tile. it then repeats for all other tiles. There may be some logical errors in there its a raw brain dump. \$\endgroup\$ – War Mar 18 '15 at 23:42

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.