Setting several textures as one texture biased off of a 2d array. aka how to use Testure2D.SetPixels().

When using a 2D grid based map system using the design within this tutorial it will help Unity only have to render one texture rather than several hundred. Hopefully this will teach you how to set individual textures into one texture.
  • Since I'm not going go into detail on how to create this map generation a great tutorial to learn how to set up grid based maps for your game can be found here.
Some code from the tutorial can be used to help get you up and running, this is below, just copy paste in to Unity. It will create a 2d array of type int named wallMap and fills it with ones and zeros. Please note this only creates an outside 'wall'. This example works using Texture2D. This code was taken from Sebastian Lague, for more information on how to use it please see the tutorial mentioned above.
   public int width, height;
    public string seed;
    public bool useRandomSeed;
    [Range(40, 70)]
    public int randomFillPercent;
    [Range(0, 10)]
    public int smoother;
    // the map
    int[,] wallMap;
    // Use this for initialization
    void Start ()
    {
        if (!go.GetComponent<SpriteRenderer>())
        {
            Debug.LogError(go.name + " does not have a SpriteRenderer!");
        }
        else
        {
            CreateMap();
            DrawMap(go, tiles, wallMap, 0);
        }
    }
    void CreateMap()
    {
        wallMap = new int[width, height];

        wallMap = RandomFillMap();

        //
        for (int i = 0; i < smoother; i++)
        {
            SmoothMap();
        }
    }
    int [,] RandomFillMap()
    {
        int [,] genericMap = new int[width, height];
        if (useRandomSeed)
        {
            seed += Time.time.ToString();
        }
        System.Random rand1 = new System.Random(seed.GetHashCode());

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                if (x == 0 || x == width - 1 || y == 0 || y == height - 1)
                {
                    genericMap[x, y] = 1;
                }
                else
                {
                    genericMap[x, y] = (rand1.Next(0, 100) > randomFillPercent) ? 1 : 0;
                }
            }
        }
        return genericMap;
    }
    void SmoothMap()
    {
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                int neighbourWallTiles = GetSurroundingWallCount(x, y);
                //
                if (neighbourWallTiles > 4)
                {
                    wallMap[x, y] = 1;
                }
                //
                else if (neighbourWallTiles < 4)
                {
                    wallMap[x, y] = 0;
                }
            }
        }
    }
    int GetSurroundingWallCount(int gridX, int gridY)
    {
        int wallCount = 0;
        //
        for (int neighbourX = gridX - 1; neighbourX <= gridX + 1; neighbourX++)
        {
            for (int neighbourY = gridY - 1; neighbourY <= gridY + 1; neighbourY++)
            {
                if (neighbourX >= 0 && neighbourX < width && neighbourY >= 0 && neighbourY < height)
                {
                    if (neighbourX != gridX || neighbourY != gridY)
                    {
                        wallCount += wallMap[neighbourX, neighbourY];
                    }
                }
                else
                {
                    wallCount++;
                }
            }
        }
        return wallCount;
    }

The code above and below can be copy pasted into a new Unity 3D or 2D project.
First off, any textures used will need to be set up properly.
  1. Select you textures.
  2. In the Inspector, for Texture Type select Advanced and Read/Write Enabled must be checked.
  3. All other setting can be set to your preferences. I do recommend setting Generate Mip Maps: off, Wrap Mode: Clamp, Filter Mode: Point(no filter), and setting the texture size closest to your textures size.
After that is set up all that is needed is to add the code below:
   // This gameobject needs to have a SpriteRenderer
    public GameObject go;
    public Texture2D[] tiles;
 
    /// <summary>
    /// go is the object you want to use to create in the scene. This must have a SpriteRenderer.
    /// variant is a Texture2D array to allow randomness to your map (these are set to 32 pixels square hard coded but can be changed to your size)
    /// map is an instance of the wallMap created. This allows for several variations of the map to be created that can be filled with different textures.
    /// depth is used for the layering of several textures.
    /// </summary>
    /// <param name="go"></param>
    /// <param name="variant"></param>
    /// <param name="map"></param>
    /// <param name="depth"></param>
    void DrawMap(GameObject go, Texture2D[] variant, int[,] map, int depth)
    {
        Texture2D mapTexture;
        GameObject theTextureGO = Instantiate(go) as GameObject;
        //rend = theTextureGO.GetComponent<SpriteRenderer>();
        mapTexture = new Texture2D(variant[0].width * width, variant[0].height * height); /// THIS ASSUMES EVERY TEXTURE IN YOUR VARIANT IS THE SAME SIZE
        // This sets all the colors of the pixles to a defualt state. This sets the alpha is to 0, this allows texture to be seethough if needed.
        Color[] clearPixels = new Color[mapTexture.width * mapTexture.height];
        mapTexture.SetPixels(clearPixels);

        int x, y;
        for (x = 0; x < width; x++)
        {
            for (y = 0; y < height; y++)
            {
                if (map[x, y] == 1)
                {
                    // Get the colors
                    Color[] pix = variant[Random.Range(0, variant.Length)].GetPixels(0, 0, 32, 32);/// CHANGE TEXTURE2D PIXEL SIZE HERE
                    // Set the colors at a position in the texture 
                    // multiplying by 32 assumes all the textures are square.
                    mapTexture.SetPixels(32 * x, 32 * y, 32, 32, pix);/// CHANGE TEXTURE2D PIXEL SIZE HERE
                }
            }
        }
        //
        mapTexture.wrapMode = TextureWrapMode.Clamp;
        mapTexture.filterMode = FilterMode.Point;
        //Apply settings
        mapTexture.Apply();
        // set the texture
        theTextureGO.GetComponent<SpriteRenderer>().sprite = MakeSprite(mapTexture);
        // adjust the position if needed
        theTextureGO.transform.position = new Vector3(0.0f, depth, 0.0f); 
        // adjust the size if needed
        theTextureGO.transform.localScale = new Vector3(3.0f, 3.0f);
    }
    // Create a sprite from a texture
    public Sprite MakeSprite(Texture2D texture)
    { 
        return Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.one * 0.5f);
    }

The code is fully commented but basically all it does is iterate through the map of ones and zeros and set the colors of the selected texture2D(labeled variant here) at the position on the mapTexture.
A successful implementation of this code should result in a randomly generated map similar to the image seen here. The project files can be found here.
  • If you want to learn more about merging layers of textures into a single texture this is a good tutorial.
Thank you for reading and if you have any question or comments please do so below and i'll be happy to respond!







Comments

Popular posts from this blog

How to create slowly revealed text in a Unity 3d game

How different data structures can be used in game coding: List, Queue, and Stack

Adding randomness to your game through code using Unity 3d