Unityでプロシージャルモデリング (平面)

Unityでメッシュをスクリプトでプロシージャルに生成する方法について何回かに分けて解説します。

* [第1回 Unityでプロシージャルモデリング \(三角形・四角形・正多角形\) \- Qiita](https://qiita.com/aa_debdeb/items/d34bd733e0584494bdbd)
* 第2回 Unityでプロシージャルモデリング (平面)
* [第3回 Unityでプロシージャルモデリング \(直方体\) \- Qiita](https://qiita.com/aa_debdeb/items/983c264879084bd40b0c)
* [第4回 Unityでプロシージャルモデリング \(円柱\) \- Qiita](https://qiita.com/aa_debdeb/items/c54b14696363873c640d)
* [第5回 Unityでプロシージャルモデリング \(球\) \- Qiita](https://qiita.com/aa_debdeb/items/cc46409b99301a5e34c0)
* [第6回 Unityでプロシージャルモデリング \(トーラス\) \- Qiita](https://qiita.com/aa_debdeb/items/efc188aa3c03a8acc5b9)


今回は平面のメッシュを生成してみます。

![plane.PNG](https://qiita-image-store.s3.amazonaws.com/0/61569/b994e0bb-915c-d1d8-1353-9d32f937cd9a.png)


```csharp:Plane.cs
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Plane : MonoBehaviour
{
    [SerializeField] private Vector2 size = new Vector2(4, 3);
    [SerializeField] private Vector2Int segment = new Vector2Int(4, 3);

    private void Awake()
    {
        Mesh mesh = new Mesh();
        GetComponent<MeshFilter>().mesh = mesh;

        Vector3[] vertices = new Vector3[(segment.x + 1) * (segment.y + 1)];
        int[] triangles = new int[segment.x * segment.y * 2 * 3];


        Vector2 halfSize = size * 0.5f;
        Vector2 sizeStep = size / segment;
        int vi = 0;
        for (int y = 0; y < segment.y + 1; y++)
        {
            for (int x = 0; x < segment.x + 1; x++)
            {
                vertices[vi++] = new Vector3(sizeStep.x * x - halfSize.x, sizeStep.y * y - halfSize.y, 0);
            }
        }
        int ti = 0;
        for (int y = 0; y < segment.y; y++)
        {
            for (int x = 0; x < segment.x; x++)
            {
                triangles[ti] = x + y * (segment.x + 1);
                triangles[ti + 1] = triangles[ti + 5] = x + (y + 1) * (segment.x + 1);
                triangles[ti + 2] = triangles[ti + 4] = (x + 1) + y * (segment.x + 1);
                triangles[ti + 3] = (x + 1) + (y + 1) * (segment.x + 1);
                ti += 6;
            }
        }

        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();
    }
}
```

今回生成する平面は大きさと分割数を`size`と`segment`でインスペクターから任意に設定できるようにしています。

メッシュは以下のように構成されています（分割数はデフォルト値）。数字は頂点のインデックス、丸で囲まれた数字は三角形を定義する順番を表しています（途中で省略）。

![plane_ex.png](https://qiita-image-store.s3.amazonaws.com/0/61569/a7542d0a-d1e1-147e-daae-4eeb15ace72a.png)

図のように頂点は`(segment.x + 1) * (segment.y + 1)`だけ必要になります。またメッシュを縦横で分割した四角形は２つの三角形で作れるので、三角形の数は`segment.x * segment.y * 2`になります。そのため、`triangles`配列の大きさは`segment.x * segment.y * 2 * 3`になります。

ソースコードの以下の箇所で頂点を定義しています。XY平面上で作成するメッシュの中心が原点に一致するように頂点を配置しています。

```csharp
Vector2 halfSize = size * 0.5f;
Vector2 sizeStep = size / segment;
int vi = 0;
for (int y = 0; y < segment.y + 1; y++)
{
    for (int x = 0; x < segment.x + 1; x++)
    {
         vertices[vi++] = new Vector3(sizeStep.x * x - halfSize.x, sizeStep.y * y - halfSize.y, 0);
    }
}
```

三角形の定義は以下の箇所で行っています。2重ループの中では2つの三角形で四角形を作成しています。２つの三角形から四角形を作成する方法については[以前の記事](https://qiita.com/aa_debdeb/items/d34bd733e0584494bdbd)を参照してください。

```csharp
int ti = 0;
for (int y = 0; y < segment.y; y++)
{
      for (int x = 0; x < segment.x; x++)
      {
          triangles[ti] = x + y * (segment.x + 1);
          triangles[ti + 1] = triangles[ti + 5] = x + (y + 1) * (segment.x + 1);
          triangles[ti + 2] = triangles[ti + 4] = (x + 1) + y * (segment.x + 1);
          triangles[ti + 3] = (x + 1) + (y + 1) * (segment.x + 1);
          ti += 6;
      }
}
```