⬆️ ⬇️

Real-time mesh manipulation on Unity

image


One of the advantages of Unity as a platform for developing games is its powerful 3D engine. In this tutorial, you will learn about the world of 3D objects and mesh manipulation.



Due to the growth of virtual and augmented reality technology (VR / AR), most developers are faced with complex concepts of 3D graphics. Let this tutorial be their starting point. Do not worry, there will be no complicated 3D mathematics here - only hearts, pictures, arrows and a lot of interesting things!



Note: this tutorial is intended for users who are familiar with Unity's IDE and who have some C # programming experience. If you do not have this knowledge, then study the tutorials Introduction to Unity UI and Introduction to Unity Scripting first .



You will need Unity version not lower than 2017.3.1. The latest version of Unity can be downloaded here . In this tutorial, the custom editor is used, for more information about them, see the tutorial Extending the Unity Editor .


Getting Started



To get started, familiarize yourself with the basic terms of 3D graphics, which will allow you to better understand the tutorial.

')

Basic technical terms for 3D graphics:





The anatomy of a 3D object begins with its mesh. The creation of this mesh starts from its vertices. The invisible lines connecting these vertices form triangles that define the basic shape of the object.





Then the normals and the UV data define shading, color, and texture. The mesh data is stored in the mesh filter, and the mesh renderer uses this data to draw the object in the scene.



That is, the pseudo-code for creating a 3D model looks like this:





Having dealt with the basics, download the project , unzip the files and run the project blank in Unity. Look at the folder structure in the Project window:





Folder Description:





In the next section, we will create a custom editor to visualize the creation of a 3D mesh.



Modifying meshes using Custom Editor



Open the 01 Mesh Study Demo , located in the Scenes folder. In the Scene window, you will see a 3D cube:





Before we start the mesh, let's take a look at the custom editor script.



Change editor script



Select the Editor folder in the Project window. The scripts in this folder add functionality to the editor (Editor) during development and are not available in Build mode.





Open MeshInspector.cs and view the source code. All Editor scripts must implement the Editor class, its CustomEditor attribute tells the Editor class what type of object it is an editor for. OnSceneGUI() is an event method that allows drawing in the Scene window; OnInspectorGUI() allows you to add additional GUI elements to the Inspector.



In MeshInspector.cs, before starting the MeshInspector class MeshInspector add the following:



 [CustomEditor(typeof(MeshStudy))] 


Code Explanation: The CustomEditor attribute tells Unity which type of object can modify the custom editor class.



In OnSceneGUI() before EditMesh() add the following:



 mesh = target as MeshStudy; Debug.Log("Custom editor is running"); 


Code Explanation: The Editor class has a standard target variable. Here, the target is a conversion to MeshStudy . Now the custom editor will draw all the GameObject and MeshStudy.cs attached to it in the Scene window. Adding debug messages allows you to verify in the console that the custom editor is actually running.



Save the file and return to Unity. Go to the Scripts folder and drag the MeshStudy.cs onto the GameObject Cube in the Hierarchy to attach it.





Now the message “Custom editor is running” should be displayed in the console, and this means that we did everything right! You can delete the debug message so that it does not interfere with us in the console.



Clone and reset a mesh



When working with a 3D mesh in Edit mode using the custom editor, be careful not to overwrite the default Unity mesh. If this happens, you have to restart Unity.



To safely clone a mesh without overwriting the original form, create a copy of the mesh from the MeshFilter.sharedmesh property and assign it to the mesh filter again.



To do this, double-click MeshStudy.cs in the Scripts folder to open the file in the code editor. This script inherits from the MonoBehaviour class, and its Start() function is not executed in Edit mode.



In MeshStudy.cs, before starting the MeshStudy class MeshStudy add the following:



 [ExecuteInEditMode] 


Explanation of the code: after adding this attribute, the Start() function will be executed in both Play mode and Edit mode. Now we can first create an instance of the mesh object and clone it.



In InitMesh() add the following code:



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


Explanation of the code:



  1. Gets the original oMesh mesh from the MeshFilter component.
  2. Copies to a new cMesh mesh cMesh .
  3. Assigns the copied mesh to the mesh filter again.
  4. Updates local variables.


Save the file and return to Unity. The message “Init & Cloned” should be displayed in the debug console. Select the Cube GameObject in the Hierarchy and check its properties in the Inspector . Mesh Filter should display an asset mesh called clone . Fine! This means that we have successfully cloned the mesh.





In the Editor folder, go to MeshInspector.cs . In OnInspectorGUI() , after the second line of code, add the following:



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


Explanation of the code:



  1. This code draws the Reset button in the Inspector .
  2. When clicked, it calls the Reset() function in MeshStudy.cs .


Save the file, open MeshStudy.cs and add the following code to the Reset() function:



 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; } 


Explanation of the code:



  1. Check for the existence of the original and cloned mesh.
  2. Reset cMesh to the source mesh.
  3. Assign cMesh oMeshFilter .
  4. Update local variables.


Save the file and return to Unity. In the Inspector, click on the Test Edit button to distort the cube mesh. Next, click the Reset button; the cube should return to the original form.





Explaining vertices and triangles in Unity



A mesh consists of vertices connected by edges to triangles. Triangles define the basic shape of an object.



Mesh class:



  • Vertices are stored as an array of Vector3 values.
  • Triangles are stored as an array of integer corresponding to the indices of the array of vertices.


That is, in a simple Quad mesh, consisting of four vertices and two triangles, the mesh data will look like this:





Vertex mapping



Here we want to display the vertices of the cube as blue dots.



In MeshInspector.cs, go to the EditMesh() function and add the following:



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


Explanation of the code:



  1. handleTransform gets Transform values ​​from mesh .
  2. handleRotation gets the current hinge Rotation mode.
  3. Go around the vertices of the mesh and draw the points with ShowPoint() .


In the ShowPoint() function, immediately after the //draw dot comment, add the following:



 Vector3 point = handleTransform.TransformPoint(mesh.vertices[index]); 


Explanation of the code: this string converts the local position of the vertex into a coordinate in world space.



In the same function, in the if block, immediately after the newly added line of code, add the following:



 Handles.color = Color.blue; point = Handles.FreeMoveHandle(point, handleRotation, mesh.handleSize, Vector3.zero, Handles.DotHandleCap); 


Explanation of the code:



  1. Sets the color, size, and position of a point using the Handles auxiliary class.
  2. Handles.FreeMoveHandle() creates an unrestricted motion manipulator that simplifies the drag and drop operation, which will come in handy in the next section.


Save the file and return to Unity. Check the cube property in the Inspector and make sure the Move Vertex Point option is on. You should now see that the mesh on the screen is marked with a few blue dots. Here they are - the tops of the cube mesh! Try doing this with other 3D objects and observe the results.





Move a single vertex



Let's start with the simplest step of manipulating the mesh - moving a single vertex.



Navigate to MeshInspector.cs . Inside the ShowPoint() function, immediately after the //drag comment and right before the closing brackets of the if block, add the following:



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


Explanation of the code:



  1. GUI.changed tracks all changes that occur with points, and works well with Handles.FreeMoveHandle() to recognize the drag and drop operation.
  2. For the dragged vertex, the mesh.DoAction() function receives its index and Transform values ​​as parameters. Since the Transform vertex values ​​are in world space, we transform them into local space using InverseTransformPoint() .


Save the script file and go to MeshStudy.cs . In DoAction() , after the opening parentheses add the following:



 PullOneVertex(index, localPos); 


Then add the following to the PullOneVertex() function:



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


Explanation of the code:



  1. We update the target vertex with the value of newPos .
  2. cMesh.vertices values ​​of the updated vertices back to cMesh.vertices .
  3. In RecalculateNormals() recalculate and redraw the mesh to match the changes.


Save the file and return to Unity. Try dragging points on a cube; Did you see a broken mesh?





It seems that some of the vertices have the same position, so when we drag only one, the other vertices remain behind it, and the mesh breaks. In the next section, we will fix this problem.



Finding all similar vertices



Visually, the cube mesh consists of eight vertices, six sides and 12 triangles. Let's check it out.





Open MeshStudy.cs , take a look at the place in front of the Start() function and find the variable vertices . We will see the following:



 [HideInInspector] public Vector3[] vertices; 


Code Explanation: [HideInInspector] hides the shared variable from the Inspector window.



Comment out this attribute:



 //[HideInInspector] public Vector3[] vertices; 


Note: Hiding vertex values ​​helps [HideInInspector] in the case of more complex 3D meshes. Since the size of the array of vertices can reach thousands of elements, this can lead to Unity inhibition when trying to view the value of the array in the Inspector.


Save the file and return to Unity. Go to the Inspector . Now the vertices property has appeared under the Mesh Study script component. Click the arrow icon next to it; this will Vector3 array of Vector3 elements.





You can see that the size of the array is 24, that is, there are vertices that have the same position! Before you continue, do not forget to uncomment [HideInInspector] .



Why is vertex 24?
There are many theories about this. But the simplest answer is this: the cube has six sides, and each side is composed of four vertices that form a plane.



Therefore, the calculation is as follows: 6 x 4 = 24 vertices.



You can search for other answers. But for now, it’s enough just to know that some meshes will have vertices that have the same position.


In MeshStudy.cs, replace all the code inside the DoAction() function with the following:



 PullSimilarVertices(index, localPos); 


Go to the PullSimilarVertices() function and add the following:



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


Explanation of the code:



  1. get the position of the target vertex, which will be used as an argument to the FindRelatedVertices() method.
  2. This method returns a list of indexes (corresponding to vertices) that have the same position as the target vertex.
  3. The loop traverses the entire list and sets the corresponding vertices to the value of newPos .
  4. cMesh.vertices updated vertices back to cMesh.vertices . Then call RecalculateNormals() to redraw the mesh with the new values.


Save the file and return to Unity. Drag any of the vertices; now the mesh must retain its shape and not collapse.





Now that we have completed the first step in manipulating the meshes, save the scene and move on to the next section.



Manipulating Meshes



In this section, you will learn about real-time mesh manipulation. There are many ways, but in this tutorial we will look at the most simple type of manipulation of meshes, namely the movement of previously created mesh vertices.



Collect selected indexes



We start by selecting the vertices that we will move in real time.



Open Scene 02 Create Heart Mesh from the Scenes folder. In the Scene window, you will see a red sphere. Select Sphere in the Hierarchy and go to the Inspector . You'll see that the Heart Mesh script component is attached to the object.



Now we need the Editor script for this object to display the vertices of the mesh in the Scene window. Go to the Editor folder and double-click on HeartMeshInspector.cs .



In the ShowHandle() function, add the following inside the if block:



 Handles.color = Color.blue; if (Handles.Button(point, handleRotation, mesh.pickSize, mesh.pickSize, Handles.DotHandleCap)) //1 { mesh.selectedIndices.Add(index); //2 } 


Explanation of the code:



  1. Sets and displays the vertices of the mesh as the type Handles.Button .
  2. When clicked, it adds the selected index to the list of pressed, mesh.selectedIndices .


In OnInspectorGUI() , before the closing bracket, add the following:



 if (GUILayout.Button("Clear Selected Vertices")) { mesh.ClearAllData(); } 


Explanation of the code: this is how we add the Reset button in the Inspector to call mesh.ClearAllData() .



Save the file and open HeartMesh.cs from the Scripts folder. In the ClearAllData() function, add the following:



 selectedIndices = new List<int>(); targetIndex = 0; targetVertex = Vector3.zero; 


Code Explanation: The code clears the values ​​in selectedIndices and targetIndex . It also resets targetVertex .



Save the file and return to Unity. Select Sphere and go to the Inspector component of the HeartMesh script. Expand Selected Indices by clicking on the arrow icon next to it. This will allow us to track every vertex added to the list.



Enable Is Edit Mode using the checkbox next to it. Due to this, the mesh vertices will be drawn in the Scene window. When clicking on the blue dots in Selected Indices , the values ​​should change accordingly. Also test the Clear Selected Vertices button to make sure it clears all values.





Note: in the modified custom Inspector we have the option to show / hide the transform manipulator using the Show Transform Handle . So do not panic if you do not find the Transform manipulator in other scenes! Turn it on before quitting.



Turning a sphere into a heart



Changing the vertices of the mesh in real time essentially consists of three stages:



  1. Copy the current mesh vertices (before the animation) into mVertices .
  2. mVertices calculations and change values ​​in mVertices .
  3. Update the current mesh vertices with mVertices as they change at each stage and let Unity automatically calculate the normals.


Open HeartMesh.cs and the following variables before the Start() function:



 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; 


Explanation of the code:



  1. The radius of the area affected by the target vertex.
  2. Dragging Force.
  3. The duration of the animation.
  4. The current index of the selectedIndices list.


In the Init() function, before the if block, add the following:



 currentIndex = 0; 


The explanation of the code: at the beginning of the game currentIndex assigned the value 0 - the first index of the selectedIndices list.



In the same Init() function, before the closing bracket of the else block, add the following:



 StartDisplacement(); 


Explanation of code: run the StartDisplacement() function if isEditMode is false.



Inside the StartDisplacement() function, add the following:



 targetVertex = oVertices[selectedIndices[currentIndex]]; //1 starttime = Time.time; //2 isAnimate = true; 


Explanation of the code:



  1. Select the targetVertex to start the animation.
  2. Set the start time and change the isAnimate value to true.


After the StartDisplacement() function, we will create a FixedUpdate() function with the following code:



 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; } } } 


Explanation of the code:



  1. The FixedUpdate() function is executed in a loop with a fixed FPS.
  2. If isAnimate is false, then skip the following code.
  3. Change the runtime animation.
  4. If the runtime is within the duration , then we get the world coordinates of targetVertex and DisplaceVertices() , covering the target vertex with the parameters pullvalue and radiusofeffect .
  5. Otherwise, time is over. Add to currentIndex unit.
  6. Check if currentIndex among selectedIndices . Go to the next vertex in the list using StartDisplacement() .
  7. Otherwise, at the end of the list, change the oMesh data to the current mesh and isAnimate to false to stop the animation.


Add the following to 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(); 


Explanation of the code:



  1. Square radius.
  2. We loop around each vertex of the mesh.
  3. sqrMagnitude between currentVertexPos and targetVertexPos .
  4. If sqrMagnitude exceeds sqrRadius , then go to the next vertex.
  5. Otherwise, we continue by determining the falloff value, depending on the distance current vertex from the central point of the area of ​​action.
  6. Vector3 new position of Vector3 and apply its Transform to the current vertex.
  7. When exiting the loop, assign the changed mVertices values ​​to the mVertices , and force Unity to recalculate the normals.


Falloff source

The original formula is taken from the Procedural Examples asset package file, which can be downloaded for free from the Unity Asset Store.


Save the file and return to Unity. Select Sphere , go to the HeartMesh component and try adding a few vertices to the Selected Indices property. Disable Is Edit mode and click Play to view the result of your work.





Experiment with the Radiusofeffect , Pullvalue, and Duration values ​​to get different results. When you are ready, change the settings according to the screenshot below.





Click on Play . Has your sphere turned into a heart?





Congratulations! In the next section, we will save the mesh to a prefab for future use.



Saving the mesh in real time



To save the procedural mesh in the shape of a heart in Play mode, you need to prepare a prefab, a child of which will be a 3D object, and then replace it with a new mesh asset using a script.



In the Project window , find the CustomHeart in the Prefabs folder. Click the arrow icon to expand its contents and select Child . Now you see the Sphere object in the Inspector preview window. This is the prefab that will store the new mesh data.





Open HeartMeshInspector.cs . Inside the OnInspectorGUI() function, add the following before the closing bracket:



 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 } } 


Explanation of the code:



  1. Sets the path value of the CustomHeart prefab object path .
  2. Creates two objects from the CustomHeart prefab , one to create an instance as a GameObject ( pfObj ), the second as links ( pfRef ).
  3. Creates an instance of the pfMesh mesh from pfMesh . If not found, creates a new mesh, otherwise clears the existing data.
  4. Fills pfMesh with new mesh data, and then adds it as an asset to CustomHeart .

  5. Fills the mesh asset in gameObj with the gameObj value.
  6. Replaces CustomHeart with gameObj matching pre-existing connections.
  7. Instantly destroy gameObj .


Save the file and go to HeartMesh.cs . In the general SaveMesh() method, after creating an instance of nMesh add the following:



 nMesh.name = "HeartMesh"; nMesh.vertices = oMesh.vertices; nMesh.triangles = oMesh.triangles; nMesh.normals = oMesh.normals; 


Explanation of the code: returns an asset of the mesh with values ​​from the heart-shaped mesh.



Save the file and return to Unity. Click play . When the animation is complete, the Save Mesh button will appear in the Inspector . Click the button to save the new mesh, and then stop the player.



Go to the Prefabs folder and look at the CustomHeart prefab . You should see that now in the CustomHeart prefab object there is a completely new heart-shaped mesh.





Great job!



We put everything together



In the previous scene, the DisplaceVertices() function used the Falloff formula to determine the drag force that was applied to each vertex within a given radius. The “fall off” point at which the drag force begins to decrease depends on the type of falloff used: Linear, Gaussian, or Needle. Each type creates different results in the mesh.





In this section, we will look at another way of manipulating vertices: using a given curve. Taking the rule that the speed is equal to the distance divided by time (d = (v / t)), we can determine the position of the vector, referring to its distance divided by time.





Using the curve method



Save the current scene and open 03 Customize Heart Mesh from the Scenes folder. You will see in the Hierarchy an instance of the CustomHeart prefab . Click the arrow icon next to it to expand its contents and select Child .



View its properties in the Inspector .You will see the Mesh Filter component with the Heart Mesh asset . Attach the Custom Heart script to Child as a component . Now the asset should change from HeartMesh to clone .





Next, open CustomHeart.cs from the Scripts folder . Start()Add the following before the function :



 public enum CurveType { Curve1, Curve2 } public CurveType curveType; Curve curve; 


Explanation of the code: a generic enum (enum) is created here CurveType, after which it is made available from the Inspector .



Go to CurveType1()and add the following:



 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 


Explanation of the code:



  1. A simple curve consists of three points. Set the points for the first curve.
  2. We generate the first curve with the help Curve()and we assign its values curve. The drawn curve can be displayed in the preview, if the last parameter is true.


Go to CurveType2()and add the following:



 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 


Explanation of the code:



  1. Set the points for the second curve.
  2. Generate the second curve with Curve()and assign its values curve. The drawn curve can be displayed in the preview, if the last parameter is true.


B StartDisplacement(), before the closing bracket add the following:



 if (curveType == CurveType.Curve1) { CurveType1(); } else if (curveType == CurveType.Curve2) { CurveType2(); } 


Explanation of the code: here we check the option selected by the user curveTypeand generate it accordingly curve.



B DisplaceVertices(), inside the cycle operator, forbefore the closing brackets add the following:



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


Explanation of the code:



  1. We get the position of the curve on the given one distanceand multiply its value yby forceto get increment.
  2. Create a new data type Vector3to store the new position of the current vertex and apply its Transform accordingly.


Save the file and return to Unity. Check the properties of the component CustomHeart game object Child . You will see a drop-down list where you can select the Curve Type . In the Edit Type drop-down list, select Add Indices or Remove Indices to update the list of vertices and experiment with different settings.





To see detailed results for different types of curves, enter values ​​according to the screenshot:





For the Curve Type list, select the Curve1 value , make sure that None is selected for the Edit Type and click Play . You should see the mesh diverge into the pattern. Twist the model to see it in the side view, and compare the results for both types of curves. Here you can see how the selected Curve Type affects the mesh offset.







That's all!You can click on Clear Selected Vertices to reset the Selected Indices and experiment with your own patterns. But do not forget that there are other factors that will affect the final result of the mesh, namely:





Where to go next?



Files of the finished project are in the archive of the project tutorial.



Do not stop at this! Try to use more complex techniques used in the tutorial "Procedural generation of mazes in Unity" .



I hope you liked this tutorial, and the information was useful. Special thanks go to Jasper Flick from Catlike Coding for his great tutorials that helped me build a demo for my project.



Source: https://habr.com/ru/post/428796/



All Articles