Dynamic mesh not filling dynamic plane.,Dynamic mesh is not filling the entire plane

I’m trying to create a dynamically sized plane with a mesh the code below. When the size of of each quad is 1 then it works perfectly. When I reduce the size of the quads to 1/2, then it brakes, and fails to fill the entire plane. It does define the frame at the correct size, but the quads start appearing overlapping each other instead of filling the space. I know its something stupid, but I can’t spot it. Code:

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

[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class FogOfWarManager : MonoBehaviour {
    [SerializeField] [Range(1,10)] int quadPerTile = 1;
    [SerializeField] TilemapManager tilemapManager;

    private TilemapController tilemapController;
    private MeshFilter meshFilter;
    
    private Mesh mesh;

    private float quadPerTitleFloat;
    private Vector3 origin;

    private void Awake() {
        tilemapController = FindObjectOfType<TilemapController>();
        meshFilter = GetComponent<MeshFilter>();
        quadPerTitleFloat = (float)quadPerTile;
    }

    // Start is called before the first frame update
    private void Start()
    {
        origin = tilemapManager.worldBounds.min;
        float quadSize = tilemapController.tileSize / quadPerTitleFloat;
        Debug.Log((int)((tilemapManager.worldBounds.max.x - origin.x) * quadPerTitleFloat));
        Debug.Log((int)((tilemapManager.worldBounds.max.y - origin.y) * quadPerTitleFloat));
    }

    // Update is called once per frame
    private void Update()
    {
        setMesh();
    }

    int lastColumns = -1;
    int lastRows = -1;
    private void setMesh() {
        if(lastColumns == columns && lastRows == rows) {
            return;
        }
        lastColumns = columns;
        lastRows = rows;

        float quadSize = tilemapController.tileSize / quadPerTitleFloat;
        
        columns = (int)((tilemapManager.worldBounds.max.x - origin.x) * quadPerTitleFloat);
        rows = (int)((tilemapManager.worldBounds.max.y - origin.y) * quadPerTitleFloat);
        
        mesh = new Mesh();

        Vector3[] vertices = new Vector3[4 * (columns * rows)];
        Vector2[] uv = new Vector2[vertices.Length];
        int[] triangles = new int[6 * (columns * rows)];

        int index = 0;
        for (int c = 0; c < columns; c++) {
            for (int r = 0; r < rows; r++) {
                vertices[index * 4]     = new Vector3(origin.x + quadSize * c       , origin.y + quadSize * r);
                vertices[index * 4 + 1] = new Vector3(origin.x + quadSize * c       , origin.y + quadSize * (r + 1));
                vertices[index * 4 + 2] = new Vector3(origin.x + quadSize * (c + 1) , origin.y + quadSize * (r + 1));
                vertices[index * 4 + 3] = new Vector3(origin.x + quadSize * (c + 1) , origin.y + quadSize * r);

                triangles[index * 6]     = index * 4;
                triangles[index * 6 + 1] = index * 4 + 1;
                triangles[index * 6 + 2] = index * 4 + 2;

                triangles[index * 6 + 3] = index * 4;
                triangles[index * 6 + 4] = index * 4 + 2;
                triangles[index * 6 + 5] = index * 4 + 3;

                index++;
            }
        }

        mesh.vertices = vertices;
        mesh.uv = uv;
        mesh.triangles = triangles;

        meshFilter.mesh = mesh;
    }
}

For the purposes of this code TilemapController just contains tileSize which is equal to 1 and tilemapManager contains worldBounds which is just a Bounds that defines the size that needs to be filled with the plane.

The plane in the images below is transparent gray.

Output when quadSize is 1 (quadPerTile = 1)
202854-tile-size-1.png
Output when quadSize is 1/2 (quadPerTile = 2)
202855-tile-size-2.png

,I’m trying to create a dynamically sized plane with a mesh the code below. When the size of of each quad is 1 then it works perfectly. When I reduce the size of the quads to 1/2, then it brakes, and fails to fill the entire plane. It does define the frame at the correct size, but the quads start appearing overlapping each other instead of filling the space. I know its something stupid, but I can’t spot it. Code:

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

[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class FogOfWarManager : MonoBehaviour {
    [SerializeField] [Range(1,10)] int quadPerTile = 1;
    [SerializeField] TilemapManager tilemapManager;

    private TilemapController tilemapController;
    private MeshFilter meshFilter;
    
    private Mesh mesh;

    private float quadPerTitleFloat;
    private Vector3 origin;

    private void Awake() {
        tilemapController = FindObjectOfType<TilemapController>();
        meshFilter = GetComponent<MeshFilter>();
        quadPerTitleFloat = (float)quadPerTile;
    }

    // Start is called before the first frame update
    private void Start()
    {
        origin = tilemapManager.worldBounds.min;
        float quadSize = tilemapController.tileSize / quadPerTitleFloat;
        Debug.Log((int)((tilemapManager.worldBounds.max.x - origin.x) * quadPerTitleFloat));
        Debug.Log((int)((tilemapManager.worldBounds.max.y - origin.y) * quadPerTitleFloat));
    }

    // Update is called once per frame
    private void Update()
    {
        setMesh();
    }

    int lastColumns = -1;
    int lastRows = -1;
    private void setMesh() {
        if(lastColumns == columns && lastRows == rows) {
            return;
        }
        lastColumns = columns;
        lastRows = rows;

        float quadSize = tilemapController.tileSize / quadPerTitleFloat;
        
        columns = (int)((tilemapManager.worldBounds.max.x - origin.x) * quadPerTitleFloat);
        rows = (int)((tilemapManager.worldBounds.max.y - origin.y) * quadPerTitleFloat);
        
        mesh = new Mesh();

        Vector3[] vertices = new Vector3[4 * (columns * rows)];
        Vector2[] uv = new Vector2[vertices.Length];
        int[] triangles = new int[6 * (columns * rows)];

        int index = 0;
        for (int c = 0; c < columns; c++) {
            for (int r = 0; r < rows; r++) {
                vertices[index * 4]     = new Vector3(origin.x + quadSize * c       , origin.y + quadSize * r);
                vertices[index * 4 + 1] = new Vector3(origin.x + quadSize * c       , origin.y + quadSize * (r + 1));
                vertices[index * 4 + 2] = new Vector3(origin.x + quadSize * (c + 1) , origin.y + quadSize * (r + 1));
                vertices[index * 4 + 3] = new Vector3(origin.x + quadSize * (c + 1) , origin.y + quadSize * r);

                triangles[index * 6]     = index * 4;
                triangles[index * 6 + 1] = index * 4 + 1;
                triangles[index * 6 + 2] = index * 4 + 2;

                triangles[index * 6 + 3] = index * 4;
                triangles[index * 6 + 4] = index * 4 + 2;
                triangles[index * 6 + 5] = index * 4 + 3;

                index++;
            }
        }

        mesh.vertices = vertices;
        mesh.uv = uv;
        mesh.triangles = triangles;

        meshFilter.mesh = mesh;
    }
}

For the purposes of this code TilemapController just contains tileSize which is equal to 1 and tilemapManager contains worldBounds which is just a Bounds that defines the size that needs to be filled with the plane.

In the code, you are calculating the size of each quad by dividing the tile size by the number of quads per tile. However, when you set the number of quads per tile to be less than 1, the size of each quad becomes larger than the tile size, causing the quads to overlap. To fix this, you can simply check if the number of quads per tile is greater than 0 and set it to 1 if it is not. You can do this by adding an if statement in the setMesh method:

if (quadPerTile <= 0) {
quadPerTile = 1; }

You can also add a check in the Start method to prevent quadPerTile from being set to a negative value. You can do this by adding an if statement:

if (quadPerTile <= 0) {
quadPerTile = 1; }

Alternatively, you can clamp the value of quadPerTile to a minimum of 1 using the Mathf.Max method:

quadPerTile = Mathf.Max(quadPerTile,
1);

You can also use the Mathf.Clamp method to achieve the same result:

quadPerTile = Mathf.Clamp(quadPerTile,
1, 10);

Hope this helps! Let me know if you have any other questions.

I realized after I posted this that the tutorial I was following created 4ish copies of each vertex, likely to make it easier to follow. Once I realized that I updated the code to store only 1 copy of each vertex, and was able to get the plane to work with 4 quads per unit (four 1/2 by 1/2 quads). That said it now appears that the issue is something to do with Unity not supporting the number of either vertices over ~65,000 or triangles over ~395,000 which fair enough, if I decided I need more granularity in the future I’ll just split it into multiple planes. In case anyone is curious the final code looked like this:

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

[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class FogOfWarInit : MonoBehaviour {
    [SerializeField] [Range(1,10)] int quadPerTile = 1;
    [SerializeField] TilemapManager tilemapManager;

    private TilemapController tilemapController;
    public MeshFilter meshFilter { get; private set; }

    private float quadPerTitleFloat;
    private Vector3 origin;

    public delegate void OnInitComplete(FogOfWarInit me);
    public event OnInitComplete onInitComplete;

    private void Awake() {
        tilemapController = FindObjectOfType<TilemapController>();
        meshFilter = GetComponent<MeshFilter>();
        quadPerTitleFloat = (float)quadPerTile;
    }

    // Start is called before the first frame update
    private void Start()
    {
        origin = tilemapManager.worldBounds.min;
        float quadSize = tilemapController.tileSize / quadPerTitleFloat;

        setMesh();

        onInitComplete?.Invoke(this);
    }

    private void Update() {
        
    }

    public void setMesh() {
        float quadSize = tilemapController.tileSize / quadPerTitleFloat;
        
        int columns = (int)((tilemapManager.worldBounds.max.x - origin.x) * quadPerTitleFloat);
        int rows = (int)((tilemapManager.worldBounds.max.y - origin.y) * quadPerTitleFloat);

        Mesh mesh = new Mesh();

        Vector3[] vertices = new Vector3[(columns * rows)];
        int[] triangles = new int[6 * (columns * rows)];

        int index = 0;
        for (int r = 0; r < rows; r++) {
            bool edge = r + 1 >= rows;

            for (int c = 0; c < columns; c++) {
                if (edge || c + 1 >= columns)
                    edge = true;

                vertices[index] = new Vector3(origin.x + quadSize * c, origin.y + quadSize * r);

                if (!edge) {
                    triangles[index * 6]     = c + ( r * columns );
                    triangles[index * 6 + 1] = c + (r * columns) + columns;
                    triangles[index * 6 + 2] = c + (r * columns) + columns + 1;

                    triangles[index * 6 + 3] = c + (r * columns);
                    triangles[index * 6 + 4] = c + (r * columns) + columns + 1;
                    triangles[index * 6 + 5] = c + (r * columns) + 1;
                }
                index++;
            }
        }

        mesh.vertices = vertices;
        mesh.triangles = triangles;

        mesh.RecalculateBounds();
        mesh.RecalculateNormals();
        meshFilter.mesh = mesh;
    }
}