Unityで正多角柱（底面が正n角形の柱）をつくる方法～見た目が変にならないように～

# はじめに
　こんにちは、最近Unityを始めた初心者です。　
　Unityを勉強していてつまずいた「メッシュの作成」について記事を書こうと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729479/60b4be02-bd7a-0496-84f2-701a49837ae6.png)

　左から、正5角柱、正6角柱、正7角柱です。
- 底面における中心から各頂点までの距離：1
- 高さ：1

になっています。

# 大まかな流れ
(1) Unityを起動
(2) プロジェクト作成
(3) シーンを用意
(4) 空のゲームオブジェクトを作成（名前は"Prism"）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729479/bdc9a107-0bf0-9425-36fb-3ed020a368b5.png)

(5)空のゲームオブジェクト"Prism"に以下のコンポーネントを追加する。
- MeshFilter
- MeshRenderer
- MeshCollider
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729479/90b57cbe-4fe2-ea2c-0388-c4288a00e445.png)

(6)C#スクリプトを作成（名前は"Prism"）→　スクリプトを以下のように書き換える。

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Prism : MonoBehaviour
{
    public int n = 6;// 正n角形のn
    void Start()
    {
        MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();// メッシュフィルター
        MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();// メッシュレンダラー
        MeshCollider meshCollider = gameObject.GetComponent<MeshCollider>();// メッシュコライダー
        Mesh mesh = new Mesh();// メッシュ
        Vector3[] vertices = new Vector3[n*6];// メッシュの頂点座標
        int[] triangles = new int[(n-1)*12];// メッシュの三角形情報
        for(int i = 0; i < n; i++)
        {
            // 底面の頂点
            vertices[i]   = (Quaternion.Euler(0, 0, (360f/n)*i) * Vector3.up) + Vector3.back*0.5f;
            vertices[n+i] = vertices[i] + Vector3.forward;
        }
        for(int i = 0; i < n; i++)
        {
            // 側面の頂点
            vertices[n*2 + i*4 + 0] = vertices[0 + i];
            vertices[n*2 + i*4 + 1] = vertices[(1 + i) % n];
            vertices[n*2 + i*4 + 2] = vertices[n + (1 + i) % n];
            vertices[n*2 + i*4 + 3] = vertices[n + i];
        }
        for(int i = 0; i < n - 2; i++)
        {
            // 手前の底面
            triangles[i*3 + 0] = 0;
            triangles[i*3 + 1] = 2 + i;
            triangles[i*3 + 2] = 1 + i;
            // 奥の底面
            triangles[(n-2)*3 + i*3 + 0] = n;
            triangles[(n-2)*3 + i*3 + 1] = n + 1 + i;
            triangles[(n-2)*3 + i*3 + 2] = n + 2 + i;
        }// ~ (n-2)*6
        for(int i = 0; i < n; i++)
        {
            // i番目の側面
            triangles[(n-2)*6 + i*6 + 0] = n*2 + i*4 + 0;
            triangles[(n-2)*6 + i*6 + 1] = n*2 + i*4 + 1;
            triangles[(n-2)*6 + i*6 + 2] = n*2 + i*4 + 2;
            triangles[(n-2)*6 + i*6 + 3] = n*2 + i*4 + 2;
            triangles[(n-2)*6 + i*6 + 4] = n*2 + i*4 + 3;
            triangles[(n-2)*6 + i*6 + 5] = n*2 + i*4 + 0;
        }// ~ (n-1)*12
        mesh.SetVertices(vertices);// メッシュの頂点を設定
        mesh.SetTriangles(triangles, 0);// メッシュの三角形を設定
        mesh.RecalculateNormals();// 法線ベクトルの設定
        meshFilter.sharedMesh = mesh;// メッシュフィルターにメッシュを設定
        meshCollider.sharedMesh = mesh;// メッシュコライダーにメッシュを設定
    }
}
```

(7) 先ほど作成した空のオブジェクトにスクリプトをくっつける。
(8) Playボタンを押す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729479/fb0c7708-ab95-386a-8f13-a138884fa835.png)
　このようにピンク色に表示されるはずです。原因はマテリアルが設定されていないためです。適当なマテリアルを追加してあげると、普通の見た目になると思います。

# イメージ

　メッシュの詳細については他の記事を読んでください。ここでは簡単な説明のみにとどめたいと思います。
　イメージは以下の通りです。
- 頂点
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729479/766d1bc2-7449-ab05-070b-202da338bbc2.png)
- 三角形の作り方（底面）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729479/c69b031f-d2da-7456-dff2-238e55da2d93.png)
- 三角形の作り方（i番目の側面）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729479/a07e21c2-f921-aea9-85bb-79ca133c016c.png)

　これはあくまでイメージなので、実際にやってることとは少し異なります。実際はそれぞれの面に対してメッシュの頂点を別にしています。正多角柱の各頂点はそれぞれ3つの面に含まれているので、同じ位置に異なる3つのメッシュの頂点を定義しているわけです（理由は後述します）。

追記(2022/07/27)：Unityの回転は時計回りを正とするので、上の画像は間違っていますね、、後日修正しておきます。

# コードの解説（一部のみ）
```cs
        for(int i = 0; i < n; i++)
        {
            // 底面の頂点
            vertices[i]   = (Quaternion.Euler(0, 0, (360f/n)*i) * Vector3.up) + Vector3.back*0.5f;
            vertices[n+i] = vertices[i] + Vector3.forward;
        }
```
これは手前の底面と奥の頂点を設定しています。
```cs
        (Quaternion.Euler(0, 0, (360f/n)*i) * Vector3.up)
```
の部分は、ベクトル(0, 1, 0)をz軸を中心に(360/n)度ずつ回転させています。
```cs
        + Vector3.back*0.5f
```
の部分は、回転された点を手前(0, 0, -1)方向に0.5だけずらしています。
```cs
        for(int i = 0; i < n; i++)
        {
            // 側面の頂点
            vertices[n*2 + i*4 + 0] = vertices[0 + i];
            vertices[n*2 + i*4 + 1] = vertices[(1 + i) % n];
            vertices[n*2 + i*4 + 2] = vertices[n + (1 + i) % n];
            vertices[n*2 + i*4 + 3] = vertices[n + i];
        }
```
の部分はi番目の側面の頂点を設定しています。位置は先ほど設定したものと同じです。
```cs
        for(int i = 0; i < n - 2; i++)
        {
            // 手前の底面
            triangles[i*3 + 0] = 0;
            triangles[i*3 + 1] = 2 + i;
            triangles[i*3 + 2] = 1 + i;
            // 奥の底面
            triangles[(n-2)*3 + i*3 + 0] = n;
            triangles[(n-2)*3 + i*3 + 1] = n + 1 + i;
            triangles[(n-2)*3 + i*3 + 2] = n + 2 + i;
        }// ~ (n-2)*6
```
の部分は、底面の三角形を張っています。
```cs
        for(int i = 0; i < n; i++)
        {
            // i番目の側面
            triangles[(n-2)*6 + i*6 + 0] = n*2 + i*4 + 0;
            triangles[(n-2)*6 + i*6 + 1] = n*2 + i*4 + 1;
            triangles[(n-2)*6 + i*6 + 2] = n*2 + i*4 + 2;
            triangles[(n-2)*6 + i*6 + 3] = n*2 + i*4 + 2;
            triangles[(n-2)*6 + i*6 + 4] = n*2 + i*4 + 3;
            triangles[(n-2)*6 + i*6 + 5] = n*2 + i*4 + 0;
        }// ~ (n-1)*12
```
の部分は、側面の三角形を張っています。
```cs
        mesh.RecalculateNormals();// 法線ベクトルの設定
```
の部分は、各頂点に法線ベクトルを設定しています。これをやらないと、見た目（光の反射や影）がおかしくなるので注意してください。
　ちなみに、頂点を重複させて設定する理由もやはり見た目に関するもので、頂点を面によって分けないと各面に対して法線ベクトルがうまく機能しなくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729479/bedebc8d-a149-b39c-c2d2-3d9338241f59.png)

# 注意

- メッシュの三角形の情報は、時計回りに設定すると「表」、半時計周りに設定すると「裏」になります。裏からはメッシュが見えなくなってしまうため、三角形の設定の順番には注意しましょう。
- trianglesにはverticesのインデックスを値として持たせます。0, 1, 2番目 / 3, 4, 5番目 / 6, 7, 8番目がそれぞれ一つの三角形になるということにも注意しましょう。

# 応用
　Inspectorから、Nの値を変えてから実行することで、他の正n多角柱をつくることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729479/dafe18bd-4050-686f-bba9-fdd574894f62.png)

- 左から、n = 3, 8, 9, 30, 100の場合。n=100だとほぼ円柱です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729479/45827098-d5e9-8376-e332-bce7d06aa018.png)

# まとめ
　この内容を応用すれば、自分の作りたい形を作ることができ、表現の幅が広がると思います。実際に正n角錐や[正多面体](https://qiita.com/fruit_ole/items/151f854cf21ec1e12314)をつくって練習してみましょう。
　メッシュを紹介する記事で、見た目がおかしくなるのを誤魔化すかのように単色シェーダーの設定方法を解説しているものがありました。こういうのはやめていただきたいですね。
　解決方法のキーワードは、「メッシュの頂点を面によって分けること」、「法線ベクトルを設定すること」でした。
　それでは、よいUnityライフをお過ごしください。