
ゲーム開発のプラットフォームとしてのUnityの利点の1つは、強力な3Dエンジンです。 このチュートリアルでは、3Dオブジェクトとメッシュ操作の世界について学びます。
仮想現実および拡張現実(VR / AR)テクノロジーの成長により、ほとんどの開発者は複雑な3Dグラフィックスの概念に直面しています。 このチュートリアルを出発点にしましょう。 心配しないでください。複雑な3D数学はありません-ハート、ドローイング、矢印、たくさんの面白いものだけがあります!
注:このチュートリアルは、Unity IDEに精通しており、C#でプログラミングの経験があるユーザーを対象としています。 そのような知識がない場合は、まず、 Unity UIの 概要とUnity Scriptingの概要のチュートリアルを学習してください。
2017.3.1以降のUnityバージョンが必要です。 Unityの最新バージョンはここからダウンロードできます 。 このチュートリアルでは、カスタムエディターを使用します。Unityエディターの拡張チュートリアルから、それらについて詳しく学ぶことができます。
仕事を始める
開始するには、3Dグラフィックスの基本用語を理解してください。これにより、チュートリアルをよりよく理解できます。
3Dグラフィックスの基本的な技術用語:
- 頂点 :各頂点は3D空間の点です。
- Mesh :モデルのすべての頂点、エッジ、三角形、法線、UVデータが含まれます。
- メッシュフィルター :モデルメッシュデータを保存します。
- Mesh Renderer :シーン内のメッシュデータをレンダリングします。
- 法線 :頂点またはサーフェスのベクトル。 メッシュの表面に垂直に外側に向けられます。
- 線/エッジ :頂点を互いに接続する非表示の線。
- 三角形 :3つのピークを接続して形成されます。
- UVマップ :オブジェクトにマテリアルをアタッチし、そのテクスチャと色を作成します。
3Dオブジェクトの構造は、そのメッシュから始まります。 このメッシュの作成は上部から始まります。 これらの頂点を結ぶ目に見えない線は、オブジェクトの基本的な形状を定義する三角形を形成します。

次に、法線とUVデータがシェーディング、色、およびテクスチャを指定します。 メッシュデータはメッシュフィルターに保存され、メッシュレンダラーはこのデータを使用してシーンにオブジェクトを描画します。
つまり、3Dモデルを作成するための擬似コードは次のようになります。
- 「myMesh」という新しいメッシュを作成します。
- 頂点と三角形myMeshのプロパティにデータを追加します。
- 「myMeshFilter」という新しいメッシュフィルターを作成します。
- メッシュプロパティmyMeshFilterをmyMeshに設定します。
基本を理解した後、 プロジェクトをダウンロードし、ファイルを解凍し、ドラフトプロジェクトをUnityで実行します。 プロジェクトウィンドウでフォルダー構造を確認します。

フォルダーの説明:
- Prefabs :アプリケーションの実行中に3Dメッシュを保存するために使用されるSphereプレハブが含まれています。
- シーン :このチュートリアルで使用する3つのシーンが含まれています。
- エディター :このフォルダー内のスクリプトは、エディターで開発で使用するスーパー機能を提供します。
- スクリプト :GameObjectにアタッチし、 Playをクリックすると実行されるランタイムスクリプトです。
- マテリアル :このフォルダには、メッシュのマテリアルが含まれています。
次のセクションでは、3Dメッシュの作成を視覚化するカスタムエディターを作成します。
カスタムエディターでメッシュを変更する
Scenesフォルダーにある01 Mesh Study Demoを開きます。 シーンウィンドウに、3Dキューブが表示されます。

メッシュに入る前に、カスタムエディタスクリプトを見てみましょう。
エディタースクリプトの編集
[ プロジェクト]ウィンドウで[ エディター ]フォルダーを選択します。 このフォルダー内のスクリプトは、開発中にエディター(エディター)に機能を追加するものであり、ビルドモードでは使用できません。

MeshInspector.csを開き、ソースコードを表示します。 すべてのEditorスクリプトは
Editor
クラスを実装する必要があり、その
CustomEditor
属性は
Editor
クラスに対して、それがエディターのオブジェクトのタイプを指示します。
OnSceneGUI()
は、Sceneウィンドウでのレンダリングを可能にするイベントメソッドです。
OnInspectorGUI()
使用すると、追加のGUI要素をインスペクターに追加できます。
MeshInspector.csで 、
MeshInspector
クラスを開始する前に、次を追加します。
[CustomEditor(typeof(MeshStudy))]
コードの説明:
CustomEditor
属性は、カスタムエディタークラスが変更できるオブジェクトのタイプをUnityに伝えます。
OnSceneGUI()
前に次を追加します。
mesh = target as MeshStudy; Debug.Log("Custom editor is running");
コードの説明:
Editor
クラスには標準の
target
変数があります。 ここで、
target
は
MeshStudy
への変換
MeshStudy
。 これで、カスタムエディターはシーンウィンドウのすべてのGameObjectとそれらにアタッチされたMeshStudy.csを描画します。 デバッグメッセージを追加すると、カスタムエディターが実際に実行されていることをコンソールで確認できます。
ファイルを保存してUnityに戻ります。 Scriptsフォルダーに移動し、 MeshStudy.csを階層内のGameObject キューブにドラッグしてアタッチします。

これで、「カスタムエディターが実行中です」というメッセージがコンソールに表示されます。これは、すべてが正しく行われたことを意味します。 デバッグメッセージを削除して、コンソールで煩わされないようにすることができます。
メッシュの複製とダンプ
カスタムエディターを使用して編集モードで3Dメッシュを操作する場合、デフォルトのUnityメッシュを上書きしないように注意してください。 この場合、Unityを再起動する必要があります。
元のフォームを上書きせずにメッシュを安全に複製するには、
MeshFilter.sharedmesh
プロパティからメッシュのコピーを作成し、メッシュフィルターに再度割り当てます。
これを行うには、 ScriptsフォルダーのMeshStudy.csをダブルクリックして、コードエディターでファイルを開きます。 このスクリプトは
MonoBehaviour
クラスを継承しており、その
Start()
関数は編集モードでは実行されません。
MeshStudy.csで 、
MeshStudy
クラスを開始する前に、次を追加します。
[ExecuteInEditMode]
コードの説明:この属性を追加すると、
Start()
関数は再生モードと編集モードの両方で実行されます。 これで、最初にメッシュオブジェクトをインスタンス化し、クローンを作成できます。
InitMesh()
、次のコードを追加します。
oMeshFilter = GetComponent<MeshFilter>(); oMesh = oMeshFilter.sharedMesh; //1 cMesh = new Mesh(); //2 cMesh.name = "clone"; cMesh.vertices = oMesh.vertices; cMesh.triangles = oMesh.triangles; cMesh.normals = oMesh.normals; cMesh.uv = oMesh.uv; oMeshFilter.mesh = cMesh; //3 vertices = cMesh.vertices; //4 triangles = cMesh.triangles; isCloned = true; Debug.Log("Init & Cloned");
コードの説明:
-
MeshFilter
コンポーネントから元のoMesh
メッシュを取得します。 -
cMesh
を新しいメッシュcMesh
コピーしcMesh
。 - コピーしたメッシュメッシュフィルターを再度割り当てます。
- ローカル変数を更新します。
ファイルを保存してUnityに戻ります。 デバッグコンソールに「Init&Cloned」というメッセージが表示されます。 階層で GameObject
Cube
を選択し、 インスペクターでそのプロパティを確認します。 Mesh Filterはcloneというメッシュアセットを表示する必要があります。 いいね! これは、メッシュのクローン作成に成功したことを意味します。

Editorフォルダーで、 MeshInspector.csに移動します 。
OnInspectorGUI()
、コードの2行目の後に、次を追加します。
if (GUILayout.Button("Reset")) //1 { mesh.Reset(); //2 }
コードの説明:
- このコードは、 Inspectorに Resetボタンを描画します。
- 押されると、 MeshStudy.csの
Reset()
関数を呼び出します。
ファイルを保存し、 MeshStudy.csを開き、次のコードを
Reset()
関数に追加します。
if (cMesh != null && oMesh != null) //1 { cMesh.vertices = oMesh.vertices; //2 cMesh.triangles = oMesh.triangles; cMesh.normals = oMesh.normals; cMesh.uv = oMesh.uv; oMeshFilter.mesh = cMesh; //3 vertices = cMesh.vertices; //4 triangles = cMesh.triangles; }
コードの説明:
- ソースとクローンメッシュの存在を確認します。
-
cMesh
を元のメッシュにリセットします。 -
cMesh
oMeshFilter
割り当て。 - ローカル変数の更新。
ファイルを保存してUnityに戻ります。 インスペクタで、 [ テスト編集 ]ボタンをクリックして、キューブメッシュをゆがめます。 次に、[ リセット ]ボタンをクリックします。 キューブは元の形式に戻る必要があります。

Unityの頂点と三角形の説明
メッシュは、三角形のエッジで接続された頂点で構成されます。 三角形は、オブジェクトの基本的な形状を定義します。
メッシュクラス:
- 頂点は
Vector3
値の配列として保存されます。- 三角形は、頂点配列のインデックスに対応する整数配列として保存されます。
つまり、4つの頂点と2つの三角形で構成される単純なクアッドメッシュでは、メッシュデータは次のようになります。
頂点マッピング
ここでは、立方体の頂点を青い点として表示します。
MeshInspector.csで、
EditMesh()
関数に入り、以下を追加します。
handleTransform = mesh.transform; //1 handleRotation = Tools.pivotRotation == PivotRotation.Local ? handleTransform.rotation : Quaternion.identity; //2 for (int i = 0; i < mesh.vertices.Length; i++) //3 { ShowPoint(i); }
コードの説明:
-
handleTransform
は、mesh
から変換値を取得します。 -
handleRotation
は、現在のジョイントの回転モードを取得します。 - メッシュの上部を移動し、
ShowPoint()
を使用してポイントを描画します。
ShowPoint()
関数で、
//draw dot
コメントの直後に、以下を追加します。
Vector3 point = handleTransform.TransformPoint(mesh.vertices[index]);
コードの説明:この行は、頂点のローカル位置をワールド空間の座標に変換します。
同じ関数の
if
ブロックで、追加したばかりのコード行の直後に、次を追加します。
Handles.color = Color.blue; point = Handles.FreeMoveHandle(point, handleRotation, mesh.handleSize, Vector3.zero, Handles.DotHandleCap);
コードの説明:
-
Handles
ヘルパークラスを使用して、ポイントの色、サイズ、および位置を設定します。 -
Handles.FreeMoveHandle()
は、ドラッグアンドドロップ操作を簡素化する無制限のモーションマニピュレーターを作成します。これは、次のセクションで役立ちます。
ファイルを保存してUnityに戻ります。 インスペクターでキューブプロパティを確認し、[ 頂点ポイントの移動 ]オプションが有効になっていることを確認します。 これで、画面上のメッシュがいくつかの青い点でマークされていることがわかります。 これがキューブメッシュの頂点です! 他の3Dオブジェクトでこれを実行して、結果を観察してください。

単一の頂点を移動する
メッシュを操作する最も単純なステップから始めましょう-単一の頂点を移動します。
MeshInspector.csに移動します 。
ShowPoint()
関数内で、
//drag
コメントの直後、
if
ブロックの閉じ括弧の直前に、次を追加します。
if (GUI.changed) //1 { mesh.DoAction(index, handleTransform.InverseTransformPoint(point)); //2 }
コードの説明:
-
GUI.changed
は、ポイントで発生するすべての変更を追跡し、Handles.FreeMoveHandle()
とHandles.FreeMoveHandle()
てドラッグアンドドロップ操作を認識します。 - ドラッグ可能な頂点の場合、
mesh.DoAction()
関数はインデックスと変換値をパラメーターとして受け取ります。 変換頂点値はワールド空間にあるため、InverseTransformPoint()
を使用してローカル空間に変換します。
スクリプトファイルを保存し、 MeshStudy.csに移動します 。
DoAction()
で、開き括弧の後に、次を追加します。
PullOneVertex(index, localPos);
次に、
PullOneVertex()
関数に次を追加します。
vertices[index] = newPos; //1 cMesh.vertices = vertices; //2 cMesh.RecalculateNormals(); //3
コードの説明:
- ターゲット頂点を値
newPos
で更新します。 - 更新された頂点値を
cMesh.vertices
ます。 -
RecalculateNormals()
、変更に一致するようにメッシュRecalculateNormals()
再計算して再描画します。
ファイルを保存してUnityに戻ります。 キューブ上のポイントをドラッグしてみてください。 壊れたメッシュを見ましたか?

一部の頂点は同じ位置にあるため、1つだけをドラッグすると、残りの頂点がその後ろに残り、メッシュが壊れます。 次のセクションでは、この問題を修正します。
すべての類似した頂点を見つける
視覚的には、キューブメッシュは8つの頂点、6つの辺、および12の三角形で構成されています。 これがそうであるかどうかを確認しましょう。

MeshStudy.csを開き、
Start()
関数の前を見て、
vertices
変数を見つけます。 以下が表示されます。
[HideInInspector] public Vector3[] vertices;
コードの説明:
[HideInInspector]
は、 Inspectorウィンドウから共有変数を非表示にします。
この属性をコメントアウトします。
//[HideInInspector] public Vector3[] vertices;
注:頂点値を非表示にすると、[HideInInspector]
がより複雑な3Dメッシュに役立ちます。 頂点配列のサイズは数千の要素に達する可能性があるため、インスペクターで配列値を表示しようとすると、Unityが抑制される可能性があります。
ファイルを保存してUnityに戻ります。 Inspectorに移動します。 これで、 Mesh Studyスクリプトのコンポーネントの下に、 verticesプロパティが表示されました。 その横の矢印アイコンをクリックします。 そのため、
Vector3
要素
Vector3
配列を
Vector3
ます。

配列のサイズが24であることがわかります。つまり、同じ位置の頂点があります。 続行する前に、必ず
[HideInInspector]
コメントを
[HideInInspector]
。
なぜ24個の頂点があるのですか?
このテーマには多くの理論があります。 しかし、最も簡単な答えは、立方体には6つの側面があり、各側面は平面を形成する4つの頂点で構成されているということです。
したがって、計算は次のようになります:6 x 4 = 24頂点。
他の回答を検索できます。 ただし、現時点では、一部のメッシュに同じ位置の頂点があることを知るのは簡単です。
したがって、計算は次のようになります:6 x 4 = 24頂点。
他の回答を検索できます。 ただし、現時点では、一部のメッシュに同じ位置の頂点があることを知るのは簡単です。
MeshStudy.csで 、
DoAction()
関数内のすべてのコードを次のコードに置き換えます。
PullSimilarVertices(index, localPos);
PullSimilarVertices()
関数に
PullSimilarVertices()
して、次を追加しましょう。
Vector3 targetVertexPos = vertices[index]; //1 List<int> relatedVertices = FindRelatedVertices(targetVertexPos, false); //2 foreach (int i in relatedVertices) //3 { vertices[i] = newPos; } cMesh.vertices = vertices; //4 cMesh.RecalculateNormals();
コードの説明:
-
FindRelatedVertices()
メソッドへの引数として使用されるターゲット頂点の位置を取得します。 - このメソッドは、ターゲット頂点と同じ位置を持つインデックス(頂点に対応する)のリストを返します。
- ループはリスト全体を走査し、対応する頂点を
newPos
設定しnewPos
。 - 更新された
vertices
をcMesh.vertices
ます。 次に、RecalculateNormals()
を呼び出して、新しい値でメッシュを再描画します。
ファイルを保存してUnityに戻ります。 頂点のいずれかをドラッグします。 これで、メッシュはその形状を保持し、崩壊しないはずです。

メッシュを操作する最初のステップが完了したので、シーンを保存して次のセクションに進みます。
メッシュ操作
このセクションでは、メッシュをリアルタイムで操作する方法について学習します。 多くの方法がありますが、このチュートリアルでは、メッシュを操作する最も簡単な方法、つまり事前に作成されたメッシュの頂点を移動する方法を見ていきます。
選択したインデックスを収集する
リアルタイムで移動する頂点を選択することから始めましょう。
シーンを開く02 Scenesフォルダーからハートメッシュを作成します。 [シーン]ウィンドウに、赤い球が表示されます。 Hierarchyで Sphereを選択し、 Inspectorに進みます。 Heart Meshスクリプトコンポーネントがオブジェクトにアタッチされていることがわかります。
ここで、Sceneウィンドウにメッシュの頂点を表示するには、このオブジェクトのEditorスクリプトが必要です。 Editorフォルダーに移動し、 HeartMeshInspector.csをダブルクリックします 。
ShowHandle()
関数で、
if
ブロック内に次を追加します。
Handles.color = Color.blue; if (Handles.Button(point, handleRotation, mesh.pickSize, mesh.pickSize, Handles.DotHandleCap)) //1 { mesh.selectedIndices.Add(index); //2 }
コードの説明:
- メッシュの頂点をタイプ
Handles.Button
として設定および表示します。 - クリックすると、選択したインデックスを、押された
mesh.selectedIndices
にmesh.selectedIndices
。
OnInspectorGUI()
で、閉じ括弧の前に、次を追加します。
if (GUILayout.Button("Clear Selected Vertices")) { mesh.ClearAllData(); }
コードの説明:これは、 Inspectorにリセットボタンを追加して
mesh.ClearAllData()
を呼び出す
mesh.ClearAllData()
です。
ファイルを保存し、 ScriptsフォルダーからHeartMesh.csを開きます。
ClearAllData()
関数で、次を追加します。
selectedIndices = new List<int>(); targetIndex = 0; targetVertex = Vector3.zero;
コードの説明:コードは
selectedIndices
および
targetIndex
値をクリアします。 また、
targetVertex
もリセットします。
ファイルを保存してUnityに戻ります。 Sphereを選択し、 HeartMeshスクリプトコンポーネントの インスペクターに移動します 。 横にある矢印アイコンをクリックして、 選択したインデックスを展開します。 これにより、リストに追加されたすべての頂点を追跡できます。
横にあるチェックボックスを使用して、 編集モードを有効にします。 これにより、メッシュの頂点がシーンウィンドウに描画されます。 選択されたインデックスの青い点をクリックすると、それに応じて値が変更されます。 また、[ 選択した頂点をクリア ]ボタンをテストして、すべての値がクリアされることを確認します。

注:変更されたカスタムインスペクターには、 Show Transform Handleを使用して変換マニピュレーターを表示 /非表示にするオプションがあります。 そのため、他のシーンでTransformマニピュレーターを見つけられなくてもパニックに陥らないでください! 終了する前にオンにしてください。
球体を心に変える
メッシュの頂点をリアルタイムで変更するには、基本的に3つのステップがあります。
- (アニメーションの前に)現在のメッシュの頂点を
mVertices
ます。 - 計算
mVertices
、mVertices
の値を変更します。 - 各ステップで変更するときに、現在のメッシュ頂点を
mVertices
更新し、Unityに法線を自動的に計算させます。
Start()
関数の前にHeartMesh.csと次の変数を開きます。
public float radiusofeffect = 0.3f; //1 public float pullvalue = 0.3f; //2 public float duration = 1.2f; //3 int currentIndex = 0; //4 bool isAnimate = false; float starttime = 0f; float runtime = 0f;
コードの説明:
- ターゲット頂点の影響を受ける領域の半径。
- ドラッグフォース。
- アニメーションの継続時間。
-
selectedIndices
リストの現在のインデックス。
Init()
関数で、
if
ブロックの前に、次を追加します。
currentIndex = 0;
コードの説明:ゲームの開始時に、
currentIndex
selectedIndices
リストの最初のインデックスである0に
currentIndex
selectedIndices
ます。
同じ
Init()
関数で、
else
ブロックの閉じ括弧の前に、次を追加します。
StartDisplacement();
コードの説明:
isEditMode
がfalseの場合、
StartDisplacement()
関数を開始します。
StartDisplacement()
関数内に、次を追加します。
targetVertex = oVertices[selectedIndices[currentIndex]]; //1 starttime = Time.time; //2 isAnimate = true;
コードの説明:
-
targetVertex
を選択して、アニメーションを開始します。 - 開始時間を設定し、
isAnimate
の値をtrueに変更します。
StartDisplacement()
関数の後、次のコードで
FixedUpdate()
関数を作成します。
void FixedUpdate() //1 { if (!isAnimate) //2 { return; } runtime = Time.time - starttime; //3 if (runtime < duration) //4 { Vector3 targetVertexPos = oFilter.transform.InverseTransformPoint(targetVertex); DisplaceVertices(targetVertexPos, pullvalue, radiusofeffect); } else //5 { currentIndex++; if (currentIndex < selectedIndices.Count) //6 { StartDisplacement(); } else //7 { oMesh = GetComponent<MeshFilter>().mesh; isAnimate = false; isMeshReady = true; } } }
コードの説明:
-
FixedUpdate()
関数は、固定FPSのサイクルで実行されます。 -
isAnimate
がfalseの場合、次のコードをスキップします。 -
runtime
アニメーションを変更しruntime
。 -
runtime
がduration
内であるduration
、targetVertex
およびDisplaceVertices()
のワールド座標を取得し、pullvalue
およびradiusofeffect
ターゲット頂点をカバーしradiusofeffect
。 - それ以外の場合は、時間切れです。
currentIndex
に1を追加します。 -
currentIndex
selectedIndices
含まselectedIndices
いるかどうかを確認します。StartDisplacement()
を使用して、リスト内の次の頂点に移動します。 - それ以外の場合は、リストの最後で、
oMesh
データを現在のメッシュに変更し、isAnimate
をfalseにisAnimate
てアニメーションを停止します。
DisplaceVertices()
、次を追加します。
Vector3 currentVertexPos = Vector3.zero; float sqrRadius = radius * radius; //1 for (int i = 0; i < mVertices.Length; i++) //2 { currentVertexPos = mVertices[i]; float sqrMagnitute = (currentVertexPos - targetVertexPos).sqrMagnitude; //3 if (sqrMagnitute > sqrRadius) { continue; //4 } float distance = Mathf.Sqrt(sqrMagnitute); //5 float falloff = GaussFalloff(distance, radius); Vector3 translate = (currentVertexPos * force) * falloff; //6 translate.z = 0f; Quaternion rotation = Quaternion.Euler(translate); Matrix4x4 m = Matrix4x4.TRS(translate, rotation, Vector3.one); mVertices[i] = m.MultiplyPoint3x4(currentVertexPos); } oMesh.vertices = mVertices; //7 oMesh.RecalculateNormals();
コードの説明:
- 半径の二乗。
- メッシュの各頂点をループします。
-
currentVertexPos
とsqrMagnitude
間のcurrentVertexPos
をtargetVertexPos
ます。 -
sqrMagnitude
がsqrMagnitude
を超える場合、次の頂点に移動します。 - それ以外の場合は、スコープの中心点から現在の頂点までの
distance
応じて、falloff
オフ値を定義して続行します。 - 新しい
Vector3
位置をVector3
そのTransformを現在の頂点に適用します。 - ループを終了すると、変更された
mVertices
値をmVertices
に割り当て、Unityに法線を再計算させます。
フォールオフテクノロジーのソース
元の式は、 Procedural Examplesアセットパッケージファイルから取得され、Unity Asset Storeから無料でダウンロードできます。
ファイルを保存してUnityに戻ります。 Sphereを選択し、 HeartMeshコンポーネントに移動して、 Selected Indicesプロパティにいくつかの頂点を追加してみてください。 編集モードを無効にし、[ 再生 ]をクリックして、作業の結果を確認します。

Radiusofeffect 、 Pullvalue、およびDurationの値を試して、異なる結果を取得します。 準備ができたら、下のスクリーンショットに従って設定を変更します。

[ 再生]をクリックします 。 あなたの球は心に変わりましたか?

おめでとうございます! 次のセクションでは、メッシュを将来の使用に備えてプレハブとして保存します。
メッシュをリアルタイムで保存する
プレイモードでハート型の手続き型メッシュを保存するには、子が3Dオブジェクトになるプレハブを準備し、スクリプトを使用してそのメッシュアセットを新しいものに置き換える必要があります。
プロジェクトウィンドウで、 PrefabsフォルダーのCustomHeartを見つけます。 矢印アイコンをクリックして内容を展開し、「 子」を選択します。 これで、 InspectorプレビューウィンドウにSphereオブジェクトが表示されます。 これは、新しいメッシュのデータを保存するプレハブです。

HeartMeshInspector.csを開きます 。
OnInspectorGUI()
関数内で、閉じ括弧の前に、次を追加します。
if (!mesh.isEditMode && mesh.isMeshReady) { string path = "Assets/Prefabs/CustomHeart.prefab"; //1 if (GUILayout.Button("Save Mesh")) { mesh.isMeshReady = false; Object pfObj = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)); //2 Object pfRef = AssetDatabase.LoadAssetAtPath (path, typeof(GameObject)); GameObject gameObj = (GameObject)PrefabUtility.InstantiatePrefab(pfObj); Mesh pfMesh = (Mesh)AssetDatabase.LoadAssetAtPath(path, typeof(Mesh)); //3 if (!pfMesh) { pfMesh = new Mesh(); } else { pfMesh.Clear(); } pfMesh = mesh.SaveMesh(); //4 AssetDatabase.AddObjectToAsset(pfMesh, path); gameObj.GetComponentInChildren<MeshFilter>().mesh = pfMesh; //5 PrefabUtility.ReplacePrefab(gameObj, pfRef, ReplacePrefabOptions.Default); //6 Object.DestroyImmediate(gameObj); //7 } }
コードの説明:
- CustomHeart プレハブオブジェクトへのパスに
path
を設定します。 - CustomHeart プレハブから2つのオブジェクトを作成します。1つはGameObjectとしてインスタンスを作成するため(
pfObj
)、もう1つはリンクとして作成します(pfRef
)。 - CustomHeart
pfMesh
メッシュpfMesh
インスタンスを作成します。 見つからない場合は新しいメッシュを作成し、見つからない場合は既存のデータをクリアします。 -
pfMesh
に新しいメッシュデータを入力し、それをアセットとしてCustomHeartに追加します 。
-
gameObj
メッシュアセットを値pfMesh
ます。 - 既存の接続を照合すること
gameObj
CustomHeartをgameObj
置き換えます。 - 即座に
gameObj
破壊しgameObj
。
ファイルを保存して、 HeartMesh.csに移動します 。 一般的な
SaveMesh()
メソッドで、
nMesh
インスタンスを作成した後、次を追加します。
nMesh.name = "HeartMesh"; nMesh.vertices = oMesh.vertices; nMesh.triangles = oMesh.triangles; nMesh.normals = oMesh.normals;
コードの説明:メッシュのアセットをハート型メッシュの値で返します。
ファイルを保存してUnityに戻ります。 再生をクリックします 。 アニメーションが完了すると、 メッシュの保存ボタンがインスペクターに表示されます。 ボタンをクリックして新しいメッシュを保存し、プレーヤーを停止します。
Prefabsフォルダーに移動し、CustomHeart プレハブを確認します。 これで、CustomHeart プレハブオブジェクトに、まったく新しいハート型のメッシュがあることがわかります。

素晴らしい仕事です!
すべてをまとめる
前のシーンでは、
DisplaceVertices()
関数はFalloff式を使用して、指定された半径内の各頂点に適用されたドラッグ力を決定しました。 抗力が減少し始める「減衰」のポイントは、使用される減衰の種類(線形、ガウス、または針)によって異なります。 各タイプは、メッシュで異なる結果を生成します。

このセクションでは、頂点を操作する別の方法、つまり特定の曲線を使用する方法について説明します。 速度は距離を時間で割った値に等しいというルールを採用し(d =(v / t))、時間で割った距離を参照して、ベクトルの位置を決定できます。

曲線法を使用する
現在のシーンを保存し、 Scenesフォルダから03 Customize Heart Meshを開きます。 HierarchyにCustomHeartのインスタンスが表示されます。 その横にある矢印アイコンをクリックして内容を展開し、「 子」を選択します。
Inspectorでプロパティを表示します。 Heart Mesh Assetを含むMesh Filterコンポーネントが表示されます。カスタムハートスクリプトをコンポーネントとしてChildに添付します。これで、アセットがHeartMeshからcloneに変更されるはずです。

次に、ScriptsフォルダーからCustomHeart.csを開きます。関数の前に、次を追加します。
Start()
public enum CurveType { Curve1, Curve2 } public CurveType curveType; Curve curve;
コードの説明:ここでは、名前の下に一般的な列挙型が作成され
CurveType
、その後Inspectorから使用可能になります。
以下に移動し
CurveType1()
て追加します。
Vector3[] curvepoints = new Vector3[3]; //1 curvepoints[0] = new Vector3(0, 1, 0); curvepoints[1] = new Vector3(0.5f, 0.5f, 0); curvepoints[2] = new Vector3(1, 0, 0); curve = new Curve(curvepoints[0], curvepoints[1], curvepoints[2], false); //2
コードの説明:
- 単純な曲線は3つのポイントで構成されます。最初の曲線のポイントを設定します。
- ヘルプを使用して最初の曲線を生成し、
Curve()
その値を割り当てますcurve
。最後のパラメーターとしてtrueを指定すると、描画された曲線をプレビューに表示できます。
CurveType2()
次に移動して追加します。
Vector3[] curvepoints = new Vector3[3]; //1 curvepoints[0] = new Vector3(0, 0, 0); curvepoints[1] = new Vector3(0.5f, 1, 0); curvepoints[2] = new Vector3(1, 0, 0); curve = new Curve(curvepoints[0], curvepoints[1], curvepoints[2], false); //2
コードの説明:
- 2番目の曲線のポイントを設定します。
- で2番目の曲線を生成し、
Curve()
その値を割り当てますcurve
。最後のパラメーターとしてtrueを指定すると、描画された曲線をプレビューに表示できます。
B
StartDisplacement()
、閉じ括弧の前に、次を追加します。
if (curveType == CurveType.Curve1) { CurveType1(); } else if (curveType == CurveType.Curve2) { CurveType2(); }
コードの説明:ここでは、ユーザーが選択したオプションを確認し、
curveType
それに応じて生成し
curve
ます。
B
DisplaceVertices()
、
for
閉じ括弧の前のループステートメント内に、次を追加します。
float increment = curve.GetPoint(distance).y * force; //1 Vector3 translate = (vert * increment) * Time.deltaTime; //2 Quaternion rotation = Quaternion.Euler(translate); Matrix4x4 m = Matrix4x4.TRS(translate, rotation, Vector3.one); mVertices[i] = m.MultiplyPoint3x4(mVertices[i]);
コードの説明:
- 私たちは、与えられた曲線上の位置を取得
distance
し、その値を乗算y
にforce
取得しますincrement
。 Vector3
現在の頂点の新しい位置を保存する新しいデータ型を作成し、それに応じてその変換を適用します。
ファイルを保存してUnityに戻ります。コンポーネントのプロパティを確認CustomHeartのゲームオブジェクトの子を。カーブタイプを選択できるドロップダウンリストが表示されます。[ タイプの編集]ドロップダウンリストから、[ インデックスの追加]または[ インデックスの削除]を選択して、頂点のリストを更新し、さまざまな設定で実験します。

さまざまな種類の曲線の詳細な結果を表示するには、スクリーンショットに従って値を入力します。

[ カーブタイプ]リストで[ Curve1 ]を選択し、[ タイプの編集 ]で[ なし]が選択されていることを確認して、[ 再生 ]をクリックします。メッシュがパターンに分岐するのが見えるはずです。モデルを回転させて側面図で表示し、両方のタイプの曲線の結果を比較します。ここでは、選択したカーブタイプがメッシュオフセットにどのように影響するかを確認できます。


以上です![ 選択した頂点をクリア ]をクリックして、選択したインデックスをリセットし、独自のパターンを試すことができます。ただし、メッシュの最終結果に影響する他の要因があることを忘れないでください。
- 半径の値。
- エリア内の頂点の分布。
- 選択した頂点のパターン位置。
- オフセットに選択されたメソッド。
次はどこへ行きますか?
完成したプロジェクトのファイルは、チュートリアルプロジェクトのアーカイブにあります。
停止しないでください!Unity Procedural Maze Generationチュートリアルで使用されている、より洗練されたテクニックを試してください。
このチュートリアルを楽しんで、情報がお役に立てば幸いです。私が表現する特別な感謝ジャスパーフリックのコーディングのCatlike私を助けた彼の優れたチュートリアルのためには、私のプロジェクトのためのデモを組み立てます。
