IndexOutOfRangeException

IndexOutOfRangeException: Index was outside the bounds of the array. MeshData.AddTriangle(System.int32 a, System.int32 b, System.int32 c)

public static class MeshGenerator
{
    public static MeshData GenerateTerrainMesh (float[,] heightMap)
    {
        int width = heightMap.GetLength(0);
        int height = heightMap.GetLength(1);
        float topLeftX = (width - 1) / -2f;
        float topLeftZ = (height - 1) / 2f;

        MeshData meshData = new MeshData(width, height);
        int vertexIndex = 0;

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                meshData.vertices[vertexIndex] = new Vector3(topLeftX + x, heightMap[x, y], topLeftZ - y);
                meshData.uvs[vertexIndex] = new Vector2(x / (float)width, y / (float)height);
                if (x < width - 1 && y < height - 1)
                {
                    meshData.AddTriangle(vertexIndex, vertexIndex + width + 1, vertexIndex + width);
                    meshData.AddTriangle(vertexIndex + width + 1, vertexIndex, vertexIndex + 1);

                }
                vertexIndex++;
            }
        }
        return meshData;
    }
} 

public class MeshData
{
    public Vector3[] vertices;
    public int[] triangles;
    public Vector2[] uvs;

    int triangleIndex;

    public MeshData (int meshWidth, int meshHeight)
    {
        vertices = new Vector3[meshWidth * meshHeight];
        uvs = new Vector2[meshWidth * meshHeight];
        triangles = new int[(meshWidth - 1) * (meshHeight - 1)];
    }

    public void AddTriangle (int a, int b, int c)
    {
        triangles[triangleIndex] = a;
        triangles[triangleIndex + 1] = b;
        triangles[triangleIndex + 2] = c;
        triangleIndex += 3;
    }

    public Mesh CreateMesh ()
    {
        Mesh mesh = new Mesh();
        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.uv = uvs;
        mesh.RecalculateNormals();
        return mesh;
    }
}

Ответы (1 шт):

Автор решения: Yaroslav

Ошибка в длине массива triangles. В каждом квадрате 2 полигона с 3 вершинами. (meshWidth-1)*(meshHeight-1)*6.

Если ты хочешь менять карту высот в реальном времени, то зачем каждый раз выстраивать меш с нуля, притом что uv и triangles не меняются. Да и зачем тут статика тоже не ясно, как компонент он может сразу работать с объектом.

using System;
using UnityEngine;

[DisallowMultipleComponent]
[RequireComponent(typeof(MeshFilter), typeof(Renderer))]

public class TerrainGenerator : MonoBehaviour
{
    private const string TexturePropertyName = "_MainTex";
    private float _space;
    private MeshFilter _filter;
    private Renderer _renderer;
    private Mesh _mesh;
    private Vector3[] _vertices;

    public Vector2Int Size { get; private set; }

    private void Awake ()
    {
        _filter = GetComponent<MeshFilter>();
        _renderer = GetComponent<Renderer>();
    }

    public void Setup (Vector2Int size, float space)
    {
        Size = size;
        _space = space;
        BuildMesh();
    }

    public void SetHeightMap (float[,] heightMap)
    {
        if (_mesh == null)
            throw new NullReferenceException("Setup terrain before setting height map");
        if (heightMap.GetLength(0) != Size.x || heightMap.GetLength(1) != Size.y)
            throw new ArgumentException("Wrong size of height map");

        for (int y = 0; y < Size.y; y++)
            for (int x = 0; x < Size.x; x++)
            {
                int index = GetIndex(x, y);
                Vector3 position = _vertices[index];
                position.y = heightMap[x, y];
                _vertices[index] = position;
            }

        _mesh.vertices = _vertices;
        _mesh.RecalculateNormals();
        _filter.sharedMesh = _mesh;
    }

    public void SetTexture (Texture texture)
    {
        _renderer.material.SetTexture(TexturePropertyName, texture);
    }

    private void BuildMesh ()
    {
        int pointCount = Size.x*Size.y;
        int rectCount = (Size.x-1)*(Size.y-1);
        _vertices = new Vector3[pointCount];
        Vector2[] uv = new Vector2[pointCount];
        int[] triangles = new int[rectCount*6];

        for (int y = 0; y < Size.y; y++)
            for (int x = 0; x < Size.x; x++)
            {
                int pointIndex = GetIndex(x, y);
                _vertices[pointIndex] = new Vector3(x*_space, 0, y*_space);
                uv[pointIndex] = new Vector2(x/(float)Size.x, y/(float)Size.y);
            }

        int index = 0;
        for (int y = 0; y < Size.y-1; y++)
            for (int x = 0; x < Size.x-1; x++)
            {
                int current = GetIndex(x, y);
                int currentUp = GetIndex(x, y+1);

                triangles[index++] = current+1;
                triangles[index++] = current;
                triangles[index++] = currentUp+1;

                triangles[index++] = currentUp;
                triangles[index++] = currentUp+1;
                triangles[index++] = current;
            }
        
        _mesh = new Mesh();
        _mesh.name = "TerrainMesh"+Size.x+"x"+Size.y;
        _mesh.vertices = _vertices;
        _mesh.triangles = triangles;
        _mesh.uv = uv;
        _mesh.RecalculateNormals();
        _filter.sharedMesh = _mesh;
    }

    private int GetIndex (int x, int y)
    {
        return Size.x*y+x;
    }
}

using UnityEngine;

public class Foo : MonoBehaviour
{
    [SerializeField] private Vector2Int _size = new Vector2Int(11, 11);
    [SerializeField] private float _space = 1;
    [Space]
    [SerializeField] private float _noiseScale = 0.2f;
    [SerializeField] private float _noiseHeight = 2;
    [Space]
    [SerializeField] private TerrainGenerator _terrain;

    public void Start ()
    {
        _terrain.Setup(_size, _space);

        float[,] heightMap = new float[_size.x, _size.y];
        for (int y = 0; y < _size.y; y++)
            for (int x = 0; x < _size.x; x++)
                heightMap[x, y] = Mathf.PerlinNoise(x*_noiseScale, y*_noiseScale)*_noiseHeight;
        _terrain.SetHeightMap(heightMap);
    }
}
→ Ссылка