Unityでのメッシュのリアルタイム操作

画像






ゲーム開発のプラットフォームとしてのUnityの利点の1つは、強力な3Dエンジンです。 このチュートリアルでは、3Dオブジェクトとメッシュ操作の世界について学びます。



仮想現実および拡張現実(VR / AR)テクノロジーの成長により、ほとんどの開発者は複雑な3Dグラフィックスの概念に直面しています。 このチュートリアルを出発点にしましょう。 心配しないでください。複雑な3D数学はありません-ハート、ドローイング、矢印、たくさんの面白いものだけがあります!



注:このチュートリアルは、Unity IDEに精通しており、C#でプログラミングの経験があるユーザーを対象としています。 そのような知識がない場合は、まず、 Unity UIの 概要とUnity Scriptingの概要のチュートリアルを学習してください。



2017.3.1以降のUnityバージョンが必要です。 Unityの最新バージョンはここからダウンロードできます 。 このチュートリアルでは、カスタムエディターを使用します。Unityエディターの拡張チュートリアルから、それらについて詳しく学ぶことができます。


仕事を始める



開始するには、3Dグラフィックスの基本用語を理解してください。これにより、チュートリアルをよりよく理解できます。



3Dグラフィックスの基本的な技術用語:





3Dオブジェクトの構造は、そのメッシュから始まります。 このメッシュの作成は上部から始まります。 これらの頂点を結ぶ目に見えない線は、オブジェクトの基本的な形状を定義する三角形を形成します。









次に、法線とUVデータがシェーディング、色、およびテクスチャを指定します。 メッシュデータはメッシュフィルターに保存され、メッシュレンダラーはこのデータを使用してシーンにオブジェクトを描画します。



つまり、3Dモデルを作成するための擬似コードは次のようになります。





基本を理解した後、 プロジェクトをダウンロードし、ファイルを解凍し、ドラフトプロジェクトをUnityで実行します。 プロジェクトウィンドウでフォルダー構造を確認します。









フォルダーの説明:





次のセクションでは、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");
      
      





コードの説明:



  1. MeshFilter



    コンポーネントから元のoMesh



    メッシュを取得します。
  2. cMesh



    を新しいメッシュcMesh



    コピーしcMesh



  3. コピーしたメッシュメッシュフィルターを再度割り当てます。
  4. ローカル変数を更新します。


ファイルを保存してUnityに戻ります。 デバッグコンソールに「Init&Cloned」というメッセージが表示されます。 階層で GameObject Cube



を選択し、 インスペクターでそのプロパティを確認します。 Mesh Filtercloneというメッシュアセットを表示する必要があります。 いいね! これは、メッシュのクローン作成に成功したことを意味します。









Editorフォルダーで、 MeshInspector.csに移動しますOnInspectorGUI()



、コードの2行目の後に、次を追加します。



 if (GUILayout.Button("Reset")) //1 { mesh.Reset(); //2 }
      
      





コードの説明:



  1. このコードは、 Inspectorに Resetボタンを描画します。
  2. 押されると、 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; }
      
      





コードの説明:



  1. ソースとクローンメッシュの存在を確認します。
  2. cMesh



    を元のメッシュにリセットします。
  3. cMesh



    oMeshFilter



    割り当て。
  4. ローカル変数の更新。


ファイルを保存して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); }
      
      





コードの説明:



  1. handleTransform



    は、 mesh



    から変換値を取得します。
  2. handleRotation



    は、現在のジョイントの回転モードを取得します。
  3. メッシュの上部を移動し、 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);
      
      





コードの説明:



  1. Handles



    ヘルパークラスを使用して、ポイントの色、サイズ、および位置を設定します。
  2. Handles.FreeMoveHandle()



    は、ドラッグアンドドロップ操作を簡素化する無制限のモーションマニピュレーターを作成します。これは、次のセクションで役立ちます。


ファイルを保存してUnityに戻ります。 インスペクターでキューブプロパティを確認し、[ 頂点ポイント移動 ]オプションが有効になっていることを確認します。 これで、画面上のメッシュがいくつかの青い点でマークされていることがわかります。 これがキューブメッシュの頂点です! 他の3Dオブジェクトでこれを実行して、結果を観察してください。









単一の頂点を移動する



メッシュを操作する最も単純なステップから始めましょう-単一の頂点を移動します。



MeshInspector.csに移動しますShowPoint()



関数内で、 //drag



コメントの直後、 if



ブロックの閉じ括弧の直前に、次を追加します。



 if (GUI.changed) //1 { mesh.DoAction(index, handleTransform.InverseTransformPoint(point)); //2 }
      
      





コードの説明:



  1. GUI.changed



    は、ポイントで発生するすべての変更を追跡し、 Handles.FreeMoveHandle()



    Handles.FreeMoveHandle()



    てドラッグアンドドロップ操作を認識します。
  2. ドラッグ可能な頂点の場合、 mesh.DoAction()



    関数はインデックスと変換値をパラメーターとして受け取ります。 変換頂点値はワールド空間にあるため、 InverseTransformPoint()



    を使用してローカル空間に変換します。


スクリプトファイルを保存し、 MeshStudy.csに移動しますDoAction()



で、開き括弧の後に、次を追加します。



 PullOneVertex(index, localPos);
      
      





次に、 PullOneVertex()



関数に次を追加します。



 vertices[index] = newPos; //1 cMesh.vertices = vertices; //2 cMesh.RecalculateNormals(); //3
      
      





コードの説明:



  1. ターゲット頂点を値newPos



    で更新します。
  2. 更新された頂点値をcMesh.vertices



    ます。
  3. 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頂点。



他の回答を検索できます。 ただし、現時点では、一部のメッシュに同じ位置の頂点があることを知るのは簡単です。


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();
      
      





コードの説明:



  1. FindRelatedVertices()



    メソッドへの引数として使用されるターゲット頂点の位置を取得します。
  2. このメソッドは、ターゲット頂点と同じ位置を持つインデックス(頂点に対応する)のリストを返します。
  3. ループはリスト全体を走査し、対応する頂点をnewPos



    設定しnewPos



  4. 更新された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 }
      
      





コードの説明:



  1. メッシュの頂点をタイプHandles.Button



    として設定および表示します。
  2. クリックすると、選択したインデックスを、押された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つのステップがあります。



  1. (アニメーションの前に)現在のメッシュの頂点をmVertices



    ます。
  2. 計算mVertices



    mVertices



    の値を変更します。
  3. 各ステップで変更するときに、現在のメッシュ頂点を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;
      
      





コードの説明:



  1. ターゲット頂点の影響を受ける領域の半径。
  2. ドラッグフォース。
  3. アニメーションの継続時間。
  4. 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;
      
      





コードの説明:



  1. targetVertex



    を選択して、アニメーションを開始します。
  2. 開始時間を設定し、 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; } } }
      
      





コードの説明:



  1. FixedUpdate()



    関数は、固定FPSのサイクルで実行されます。
  2. isAnimate



    がfalseの場合、次のコードをスキップします。
  3. runtime



    アニメーションを変更しruntime



  4. runtime



    duration



    内であるduration



    targetVertex



    およびDisplaceVertices()



    のワールド座標を取得し、 pullvalue



    およびradiusofeffect



    ターゲット頂点をカバーしradiusofeffect



  5. それ以外の場合は、時間切れです。 currentIndex



    に1を追加します。
  6. currentIndex



    selectedIndices



    含まselectedIndices



    いるかどうかを確認します。 StartDisplacement()



    を使用して、リスト内の次の頂点に移動します。
  7. それ以外の場合は、リストの最後で、 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();
      
      





コードの説明:



  1. 半径の二乗。
  2. メッシュの各頂点をループします。
  3. currentVertexPos



    sqrMagnitude



    間のcurrentVertexPos



    targetVertexPos



    ます。
  4. sqrMagnitude



    sqrMagnitude



    を超える場合、次の頂点に移動します。
  5. それ以外の場合は、スコープの中心点から現在の頂点までのdistance



    応じて、 falloff



    オフ値を定義して続行します。
  6. 新しいVector3



    位置をVector3



    そのTransformを現在の頂点に適用します。
  7. ループを終了すると、変更されたmVertices



    値をmVertices



    に割り当て、Unityに法線を再計算させます。


フォールオフテクノロジーのソース

元の式は、 Procedural Examplesアセットパッケージファイルから取得され、Unity Asset Storeから無料でダウンロードできます。


ファイルを保存してUnityに戻ります。 Sphereを選択し、 HeartMeshコンポーネントに移動して、 Selected Indicesプロパティにいくつかの頂点を追加してみてください。 編集モードを無効にし、[ 再生 ]をクリックして、作業の結果を確認します。









RadiusofeffectPullvalue、および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 } }
      
      





コードの説明:



  1. CustomHeart プレハブオブジェクトへのパスにpath



    を設定します。
  2. CustomHeart プレハブから2つのオブジェクトを作成します。1つはGameObjectとしてインスタンスを作成するため( pfObj



    )、もう1つはリンクとして作成します( pfRef



    )。
  3. CustomHeart pfMesh



    メッシュpfMesh



    インスタンスを作成ます。 見つからない場合は新しいメッシュを作成し、見つからない場合は既存のデータをクリアします。
  4. pfMesh



    に新しいメッシュデータを入力し、それをアセットとしてCustomHeartに追加します

  5. gameObj



    メッシュアセットを値pfMesh



    ます。
  6. 既存の接続を照合することgameObj



    CustomHeartgameObj



    置き換えます。
  7. 即座に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
      
      





コードの説明:



  1. 単純な曲線は3つのポイントで構成されます。最初の曲線のポイントを設定します。
  2. ヘルプを使用して最初の曲線を生成し、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
      
      





コードの説明:



  1. 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]);
      
      





コードの説明:



  1. 私たちは、与えられた曲線上の位置を取得distance



    し、その値を乗算y



    force



    取得しますincrement



  2. Vector3



    現在の頂点の新しい位置を保存する新しいデータ型作成し、それに応じてその変換を適用します。


ファイルを保存してUnityに戻ります。コンポーネントのプロパティを確認CustomHeartのゲームオブジェクトの子をカーブタイプを選択できるドロップダウンリストが表示されます[ タイプ編集]ドロップダウンリストから、[ インデックスの追加]または[ インデックスの削除]を選択して、頂点のリストを更新し、さまざまな設定で実験します。









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









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















以上です![ 選択した頂点クリア ]をクリックして、選択したインデックスをリセットし、独自のパターンを試すことができますただし、メッシュの最終結果に影響する他の要因があることを忘れないでください。





次はどこへ行きますか?



完成したプロジェクトのファイルは、チュートリアルプロジェクトのアーカイブにあります



停止しないでください!Unity Procedural Maze Generationチュートリアルで使用されている、より洗練されたテクニックを試してください



このチュートリアルを楽しんで、情報がお役に立てば幸いです。私が表現する特別な感謝ジャスパーフリックコーディングのCatlike私を助けた彼の優れたチュートリアルのためには、私のプロジェクトのためのデモを組み立てます。










All Articles