Tiling quad texture with UVs script

I’m not sure which forum to post this in, but I thought someone might find it useful.

I made this script to save on draw calls when tiling textures on quads for a a 2D game. I searched around quite a bit and couldn’t find any examples of doing something like this in Unity.

The usual method for tiling would be to use a separate material for each quad size, setting the material textureScale for tiling. This script tiles the texture by creating additional vertices and triangles in the quad’s mesh at runtime, with texture coordinates to match. This saves draw calls as you can reuse the same material no matter what the size of the quad or scale of the tile.

Since the script increases the number of vertices per quad, I recommend you do your own testing if you are unsure of whether this method will increase performance in your case.

I’d appreciate feedback from experienced users on whether saving draw calls this way is generally worth the cost of additional vertices.

Screenshot in use:

C# code:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class TiledQuad : MonoBehaviour {
    [SerializeField] private float textureWidthToUnits = 1;
    [SerializeField] private float textureHeightToUnits = 1;

    void Start() {
        UpdateTiling();
    }

    public void UpdateTiling() {
        float myWidth = transform.localScale.x;
        float myHeight = transform.localScale.y;

        int quadColumns = (int)Mathf.Ceil(myWidth / textureWidthToUnits);
        int quadRows = (int)Mathf.Ceil(myHeight / textureHeightToUnits);

        float regularColumnWidth, regularRowHeight, endColumnWidth, topRowHeight, endColumnU, topRowV;

        if (quadColumns == 1) {
            endColumnWidth = 1;
            regularColumnWidth = 0;
            endColumnU = myWidth / textureWidthToUnits;
        }
        else {
            float leftoverWidth = myWidth % textureWidthToUnits;
            if (leftoverWidth == 0) {
                regularColumnWidth = (myWidth / quadColumns) / myWidth;
                endColumnWidth = regularColumnWidth;
                endColumnU = 1;
            }
            else {
                regularColumnWidth = ((myWidth - leftoverWidth) / (quadColumns - 1)) / myWidth;
                endColumnWidth = leftoverWidth / myWidth;
                endColumnU = endColumnWidth / regularColumnWidth;
            }
        }

        if (quadRows == 1) {
            topRowHeight = 1;
            regularRowHeight = 0;
            topRowV = myHeight / textureHeightToUnits;
        }
        else {
            float leftoverHeight = myHeight % textureHeightToUnits;
            if (leftoverHeight == 0) {
                regularRowHeight = (myHeight / quadRows) / myHeight;
                topRowHeight = regularRowHeight;
                topRowV = 1;
            }
            else {
                regularRowHeight = ((myHeight - leftoverHeight) / (quadRows - 1)) / myHeight;
                topRowHeight = leftoverHeight / myHeight;
                topRowV = topRowHeight / regularRowHeight;
            }
        }

        List<Vector3> vertices = new List<Vector3>();
        List<int> triangles = new List<int>();
        List<Vector2> uvs = new List<Vector2>();

        for (int y = 0; y < quadRows; y++) {
            float quadHeight = (y == quadRows - 1) ? topRowHeight : regularRowHeight;
            float v = (y == quadRows - 1) ? topRowV : 1;

            for (int x = 0; x < quadColumns; x++) {
                float quadWidth = (x == quadColumns - 1) ? endColumnWidth : regularColumnWidth;
                float u = (x == quadColumns - 1) ? endColumnU : 1;

                vertices.Add(new Vector3(x * regularColumnWidth - 0.5f, y * regularRowHeight - 0.5f, 0));
                vertices.Add(new Vector3(x * regularColumnWidth - 0.5f, y * regularRowHeight + quadHeight - 0.5f, 0));
                vertices.Add(new Vector3(x * regularColumnWidth + quadWidth - 0.5f, y * regularRowHeight - 0.5f, 0));
                vertices.Add(new Vector3(x * regularColumnWidth + quadWidth - 0.5f, y * regularRowHeight + quadHeight - 0.5f, 0));

                // texture coordinates
                uvs.Add(new Vector2(0, 0));
                uvs.Add(new Vector2(0, v));
                uvs.Add(new Vector2(u, 0));
                uvs.Add(new Vector2(u, v));

                // triangle indices should match up with vertex indices (the order which vertices are added matters)
                int bottomLeftVertexIndex = (y * quadColumns * 4) + (x * 4);

                // top left triangle
                triangles.Add(bottomLeftVertexIndex);
                triangles.Add(bottomLeftVertexIndex + 1);
                triangles.Add(bottomLeftVertexIndex + 3);
                
                // bottom right triangle
                triangles.Add(bottomLeftVertexIndex + 3);
                triangles.Add(bottomLeftVertexIndex + 2);
                triangles.Add(bottomLeftVertexIndex);
            }
        }

        Mesh m = new Mesh();

        m.vertices = vertices.ToArray();
        m.triangles = triangles.ToArray();
        m.uv = uvs.ToArray();

        m.RecalculateNormals();

        GetComponent<MeshFilter>().mesh = m;
    }
}

Scipting Section please!

Sorry. Doesn’t seem like I can delete or move the post. Hopefully an admin can move it.