今回はUnityでスクリプトからメッシュを生成してみようと思います。
LineRendererを使用すれば頂点を繋いだメッシュの生成が出来、

TrailRendererを使用すればゲームオブジェクトの軌跡をメッシュを作成し表示する事が出来ます。

ただ元々ある設定値を変更してメッシュを自動生成する為、自分で思い描いたようなメッシュを生成したい場合は自由度が低いです。
そこで今回はスクリプトから自分で頂点を設定し、そこからメッシュを生成してみようと思います。
スクリプトからメッシュを生成する方法はYouTubeにある動画でわかりやすく解説している方がいましたので、そちらを順に追っていくとこの記事よりもわかりやすいかも!?
メッシュを生成する前に知っておくべき情報
Unityでメッシュを生成する為には頂点、UVMap、三角形の情報が必要になります。
頂点
頂点はメッシュを構成する点の情報です。
3Dの点なのでVector3の配列やListとしてデータを作り、それをMeshクラスのverticesに設定します。
Unityでメッシュを作る時は三角形で構成しなければいけない為、四角形の面を作成する時は三角形を2つ使って四角形を構成します。
↑のような頂点座標を配列等に設定していきます。
UVMap
UVMapはメッシュに貼り付けるテクスチャの座標値を設定します。
UVMapは頂点毎にVector2の値を設定し、Meshクラスのuvに設定します。
例えば↓のようなテクスチャ画像を用意した場合は、
テクスチャの横座標がU、縦座標がVとなり0から1の数値を取ります。
U、Vというのは3DCGの座標軸でX、Y、Zを使っているので他のU、Vを使っているみたいです。
メッシュを作成した時にテクスチャを貼り付けたいと思った時、↑の画像の縦横半分だけを貼り付けたい時は、Uを0から0.5、Vを0から0.5の値を設定しメッシュを作成すればいいことになります。
先ほどの頂点に先ほどのテクスチャを割り当てるとすると、
↑のようになり、頂点(0, 0, 0)にはUV(0, 0)、頂点(1, 0, 0)にはUV(1, 0)、頂点(0, 1, 0)にはUV(0, 1)、頂点(1, 1, 0)にはUV(1, 1)を割り当てればテクスチャ全体が四角形の面に表示される事になります。
説明だけでは解り辛いですが、後でサンプルを作成するのでそこでわかると思います。
三角形
Unityでは三角形を繋げてメッシュを作成する必要がある為、その三角形を構成する3つの頂点を先ほどの頂点配列の番号を指定して設定する必要があります。
頂点配列が
(0, 0, 0)、(0, 1, 0)、(1, 0, 0)という順番で配列に格納されていた場合、この順番で三角形を構成しようとしたら、
0、1、2
という頂点配列の番号を三角形に指定する必要があります。
三角形の情報もVector3の配列やListとして登録する必要がある為、それぞれの三角形を構成する3つの頂点を設定していきますが、パッと見ただの配列情報になります。
現時点では何を言ってるかわからないかもしれませんが、サンプルを作成すると少しわかると思います。
四角形を作る時は頂点を4つ作成すれば三角形を作れますが、三角形のそれぞれの面の光の当たり方を変更したい場合は頂点を6つ作ります。
↑が頂点を共有して四角形を作る場合と頂点を共有しないで四角形を作る時のイメージです。
下の頂点を共有しない場合は頂点のVector3の値は同じですが、三角形を構成する時に別の頂点として設定します。
三角形を構成する頂点の順番について
三角形を構成する頂点の番号を配列に入れる場合に注意しなければいけないのは順番です。
配列に入れた順番で三角形を作っていきますが、その順番によってメッシュの向きが変わります。
頂点は時計回りに繋いだ面が表になります。
例えば↑の頂点を使って三角形を作る場合
(0, 0, 0)→(0, 1, 0)→(1, 0, 0)の順番で三角形を作った面は手前側がメッシュの表面になり、向こう側がメッシュの裏になります。
(1, 0, 0)→(1, 1, 0)→(0, 1, 0)の順番で三角形を作った面は向こう側がメッシュの表面になり、手前側が裏になります。
スクリプトからメッシュを生成してみる
メッシュの作り方がわかったので、実際にスクリプトからメッシュを生成してみます。
4つの頂点で四角形の面を作成
まずは4つの頂点を作り四角形を作ってみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | using UnityEngine; using System.Collections; public class CreateMeshTest1 : MonoBehaviour { // 頂点配列 public Vector3[] vertices; // UV配列 public Vector2[] uvs; // 三角形の順番配列 public int[] triangles; // メッシュ private Mesh mesh; // メッシュ表示コンポーネント private MeshRenderer meshRenderer; // メッシュに設定するマテリアル public Material material; // Use this for initialization void Start () { vertices = new Vector3[] { new Vector3 (0f, 0f, 0f), new Vector3 (0f, 1f, 0f), new Vector3 (1f, 1f, 0f), new Vector3 (1f, 0f, 0f) }; uvs = new Vector2[] { new Vector2 (0f, 0f), new Vector2 (0f, 1f), new Vector2 (1f, 1f), new Vector2 (1f, 0f) }; triangles = new int[] { 0, 1, 2, 0, 2, 3 }; gameObject.AddComponent<MeshFilter> (); meshRenderer = gameObject.AddComponent<MeshRenderer> (); mesh = GetComponent<MeshFilter> ().mesh; meshRenderer.material = material; } void Update () { CreateMesh (mesh, vertices, uvs, triangles); } void CreateMesh(Mesh mesh, Vector3[] vertices, Vector2[] uvs, int[] triangles) { // 最初にメッシュをクリアする mesh.Clear(); // 頂点の設定 mesh.vertices = vertices; // テクスチャのUV座標設定 mesh.uv = uvs; // 三角形メッシュの設定 mesh.triangles = triangles; // Boundsの再計算 mesh.RecalculateBounds (); // NormalMapの再計算 mesh.RecalculateNormals (); } } |
頂点配列verticesをVector3型、UV配列uvsをVector2型、三角形配列trianglesをint型の配列で宣言します。
Startメソッドで頂点、UV、三角形の値を設定します。
verticesの配列の順番とuvsの配列の順番が対応します。
三角形であるtrianglesではverticesの配列のデータ順で三角形を構成する頂点を指定しています。
0、1、2という順番で最初の三角形を構成するので、verticesの頂点(0, 0, 0)、(0, 1, 0)、(1, 1, 0)で作ります。
次の0、2、3ではverticesの頂点(0, 0, 0)、(1, 1, 0)、(1, 0, 0)で三角形を作ります。
三角形の項目で書いたように時計回りの面がメッシュの表面になります。
三角形は頂点が3つ必要なのでMeshに設定する時に3の倍数でない場合はエラーになります。
三角形を構成する頂点がわかりやすいように、1つの三角形の頂点毎に改行してわかりやすくしています。
メッシュを設定するにはMeshFilter、メッシュを表示するにはMeshRendererコンポーネントが必要な為、AddComponentを使ってスクリプトからコンポーネントを取り付けています。
Updateメソッドで自前のCreateMeshメソッドを引数を与えて呼び出し、メッシュの作成をします。
メッシュを一旦Clearメソッドでクリアにし、その後Meshクラスのvertices、uv、trianglesに作成した配列を設定しメッシュを作成します。
メッシュ作成後はBoundsの再計算とNormalMapの再計算をしています。
メッシュ生成後ノーマルマップ(法線(メッシュの表面の方向))の方向が別の方向になっていますので再計算をします。
ヒエラルキー上に空のゲームオブジェクトを作成しこのスクリプトを取り付け、Materialに何らかのマテリアルを作成し取り付けてます。
メッシュは空のゲームオブジェクトからの相対値の頂点位置に作成されるのでゲームオブジェクトを移動してみてください。
それでは実行してみます。
↑のようにUnityを実行するとメッシュが生成され指定したマテリアルのテクスチャがUVの情報によって貼り付けられています。
メッシュの表面をカメラの方向にしているので、回転させて裏側を見るとメッシュが表示されません。
メッシュの裏側が表示されないのはマテリアルに設定しているシェーダ―スクリプトで裏面を表示しないようにしている為表示されていません。
裏側も表示するシェーダ―を指定すれば裏側から見てもメッシュが表示されます。
テクスチャを4分割した左下だけを表示する
先ほどのスクリプトではテクスチャの前面を四角形に表示しましたが、UVに設定する値を変更し4分割の左下だけを表示するようにしてみます。
uvの設定部分だけを下のように変更してください。
1 2 3 4 5 6 7 8 | uvs = new Vector2[] { new Vector2 (0f, 0f), new Vector2 (0f, 0.5f), new Vector2 (0.5f, 0.5f), new Vector2 (0.5f, 0f) }; |
UVの座標を変更したことで、左下の部分のテクスチャだけがメッシュに割り当てられます。
↑のようにテクスチャの割り当てる部分を変更出来ます。
6つの頂点を使って四角形の面を作成
次は6つの頂点を使って四角形の面を作成してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | using UnityEngine; using System.Collections; public class CreateMeshTest2 : MonoBehaviour { public Vector3[] vertices; public Vector2[] uvs; public int[] triangles; private Mesh mesh; private MeshRenderer meshRenderer; public Material[] material; // Use this for initialization void Start () { vertices = new Vector3[] { new Vector3 (0f, 0f, 0f), new Vector3 (0f, 1f, 0f), new Vector3 (1f, 1f, 0f), new Vector3 (0f, 0f, 0f), new Vector3 (1f, 1f, 0f), new Vector3 (1f, 0f, 0f) }; uvs = new Vector2[] { new Vector2 (0f, 0f), new Vector2 (0f, 1f), new Vector2 (1f, 1f), new Vector2 (1f, 0f), new Vector2 (0f, 0f), new Vector2 (0f, 1f) }; triangles = new int[] { 0, 1, 2, 3, 4, 5}; gameObject.AddComponent<MeshFilter> (); meshRenderer = gameObject.AddComponent<MeshRenderer> (); mesh = GetComponent<MeshFilter> ().mesh; meshRenderer.materials = material; // meshRenderer.materials[0] = material[0]; // meshRenderer.materials[0] = material[1]; } void Update () { CreateMesh (mesh, vertices, uvs, triangles); } void CreateMesh(Mesh mesh, Vector3[] vertices, Vector2[] uvs, int[] triangles) { // 最初にメッシュをクリアする mesh.Clear(); // 頂点の設定 mesh.vertices = vertices; // テクスチャのUV座標設定 mesh.uv = uvs; // 三角形メッシュの設定 mesh.triangles = triangles; // Boundsの再計算 mesh.RecalculateBounds (); // NormalMapの再計算 mesh.RecalculateNormals (); } } |
verticesには同じVector3の頂点を別に登録し、6つの頂点を使ってtrianglesに登録しています。
6つの頂点で作成した面は光の当たりぐあい等が変わってきます。
UVは適当に設定しています。
頂点の共有ありなしで光の当たり具合の変化
次に頂点の共有で作ったメッシュと頂点を共有しないで作ったメッシュの光が当たった時の違いを見ていきます。
頂点、UV、三角形の設定値を変えるだけなので、そこの部分だけ記載します。
まずは頂点を共有する場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | vertices = new Vector3[] { new Vector3 (0f, 0f, 0f), new Vector3 (0f, 1f, 0f), new Vector3 (1f, 1f, 0f), new Vector3 (1f, 0f, 0f), new Vector3 (1f, 0f, 1f), new Vector3 (1f, 1f, 1f) }; uvs = new Vector2[] { new Vector2 (0f, 0f), new Vector2 (0f, 1f), new Vector2 (1f, 1f), new Vector2 (1f, 0f), new Vector2 (0f, 0f), new Vector2 (1f, 0f), }; triangles = new int[] { 0, 1, 2, 0, 2, 3, 3, 2, 4, 4, 2, 5 }; |
頂点を共有しない場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | vertices = new Vector3[] { new Vector3 (0f, 0f, 0f), new Vector3 (0f, 1f, 0f), new Vector3 (1f, 1f, 0f), new Vector3 (0f, 0f, 0f), new Vector3 (1f, 1f, 0f), new Vector3 (1f, 0f, 0f), new Vector3 (1f, 0f, 0f), new Vector3 (1f, 1f, 0f), new Vector3 (1f, 0f, 1f), new Vector3 (1f, 0f, 1f), new Vector3 (1f, 1f, 0f), new Vector3 (1f, 1f, 1f) }; uvs = new Vector2[] { new Vector2 (0f, 0f), new Vector2 (0f, 1f), new Vector2 (1f, 1f), new Vector2 (1f, 0f), new Vector2 (0f, 0f), new Vector2 (0f, 1f), new Vector2 (0f, 0f), new Vector2 (0f, 1f), new Vector2 (1f, 1f), new Vector2 (1f, 0f), new Vector2 (0f, 1f), new Vector2 (1f, 1f) }; triangles = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; |
それぞれのスクリプトを別の空のゲームオブジェクトに設定し実行します。
Unity実行後にMeshRendererのCast ShadowsをOff、Receive Shadowsのチェックを外します。
左側が頂点を共有したメッシュ、右が共有しないメッシュです。
Directional Lightの角度を変え光の当たり具合を確認します。
Directional LightはTransformの位置に関係なく角度だけが光の当たり具合に影響します。
共有した方はメッシュ全体が光の影響を受けている感じで、共有していない方は当たる角度によって暗いままになっています。
終わりに
スクリプトからメッシュを作成出来るようになると自動で地面を作成したり、ダンジョンを作成したりといった事まで出来ますね(^^)/
メッシュの構成を考えると結構頭が痛くなりそうですけどね・・・・(^_^;)
Cubeのような簡単なメッシュをスクリプトから作るのも大変そうです・・・・(-_-)
スクリプトからメッシュを作成する事が出来たので、次回以降にキャラクターが剣を振った時の剣の軌跡をスクリプトから作ってみたいと思います。