Welcome to the world of 3D objects and ! One of the benefits of using as your game development platform is its powerful 3D Engine. Along with its ability to use custom editors, it makes the development of 3D games and apps so much easier.

With the growth of virtual reality and augmented reality (VR/AR) technology, most developers will inadvertently find themselves wrestling with the gritty bits of 3D concepts. So let this tutorial be your starting point. Don’t worry, there will be no complicated 3D math here — just lots of hearts, drawings, arrows and loads of fun!

Note: This tutorial is intended for users who are familiar with Unity’s integrated development environment (IDE) and have some experience with C# programming. Otherwise, do check out Introduction to Unity UI and Introduction to Unity Scripting first.

You need to have at least Unity 2017.3.1 installed. If not, please download the latest version of Unity here. This tutorial utilises a custom editor, you can find out more about it at Extending the Unity Editor.

## Getting Started

First, familiarize yourself with these basic 3D technical terms to help you better understand this tutorial.

Basic 3D Technical Terms:

• Vertices: Each vertex is a point in 3D space.
• Mesh: Holds all the vertices, edges, triangles, normals and UV data of the model.
• Mesh Filter: Stores the mesh data of the model.
• Mesh Renderer: Renders the mesh data in the scene.
• Normals: The directional vector of a vertex or a surface. This characteristically points outward, perpendicular to the mesh surface.
• Lines/Edges: The invisible lines that connect vertices to one another.
• Triangles: Formed when three vertices are connected by edges.
• UV Map: Maps a material to an object to give it texture and color.

The anatomy of a 3D object starts with its mesh. The construction of this mesh starts with its vertices. The invisible lines that connect these vertices form triangles, which define the basic shape of the object.

Normals and UV data then provide shading, color and texture. This mesh data is stored in the mesh filter, and the mesh renderer uses this data to draw the object in the scene.

Hence, to create a 3D model in pseudocode:

• Create a new mesh named “myMesh”.
• Add data to myMesh’s vertices and triangle properties.
• Create a new mesh filter named “myMeshFilter”.
• Assign myMesh to myMeshFilter’s mesh property.

Now that you have the basics covered, download the project using the Download Materials button at the top or bottom of this page, unpack the files and open the starter project in Unity. Check out the folder structure in the Project window:

Folders explained:

• Prefabs: This contains a Sphere prefab, to be used for saving your 3D mesh at .
• Scenes: This contains three scenes, which will be used in this tutorial.
• Editor: The Scripts inside this folder give you special powers in the editor during development.
• Scripts: This includes runtime scripts which, when attached to a GameObject, execute when you click Play.
• Materials: This folder contains the Material for the mesh.

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

## Poking and Prodding Meshes With a Custom Editor

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

Before diving into the mesh, let’s look at a custom editor script.

### Customizing Editor Script

Select the Editor folder in the Project window. Scripts in this folder add functionality to the Editor during development and are not available in Build mode.

Open up MeshInspector.cs and view the source code. All Editor script needs to implement the `Editor` class, its attribute `CustomEditor` tells the `Editor` class which object type it’s an editor for. The `OnSceneGUI()` is an event method that lets you draw stuff in the Scene view; `OnInspectorGUI()` lets you customise the Inspector with extra GUI elements.

In MeshInspector.cs, before the start of the `MeshInspector` class, add the following:

```
[CustomEditor(typeof(MeshStudy))]
```

Code explained:
Here, the `CustomEditor` attribute tells Unity which object type the custom editor class can edit.

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

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

Code explained:
The `Editor` class has a default `target` variable. Here, the `target` is cast to `MeshStudy`. Now, the custom editor will draw any GameObject with MeshStudy.cs attached to it in the Scene view. Adding a debug log allows you to see in the console that the custom editor is indeed running.

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

The console should be logging the message “Custom editor is running” now, which means the setup was successful! Feel free to remove the debug log at this time so that it doesn’t flood your console.

### Cloning and Reseting Mesh

In handling a 3D mesh with a custom editor in Edit mode, be careful not to overwrite Unity’s default mesh. If that happens, you will need to restart Unity.

To safely clone the mesh without overwriting its original form, make a copy of the mesh from the `MeshFilter.sharedmesh` property and assign it back to the mesh filter.

To do this, double-click on MeshStudy.cs in the Scripts folder to open the file with your favorite code editing software. This script inherits from the `MonoBehaviour` class and its `Start()`function will not run in Edit mode.

In MeshStudy.cs, before the start of the `MeshStudy` class, add in the following:

```
[ExecuteInEditMode]
```

Code explained:
By adding this attribute, the `Start()` function will now fire in both Play mode and Edit mode. Now you can instantiate and clone your mesh object first.

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

Code explained:

1. Grabs original mesh `oMesh` from the `MeshFilter` component.
2. Copies to a new mesh instance `cMesh`.
3. Assigns the copied mesh back to the mesh filter.

Save the file and return to Unity. The debug console should show the message “Init & Cloned.” Select the `Cube` GameObject in the Hierarchy and check its properties in the Inspector. The Mesh Filter should show a mesh asset named clone. Great! This means you have cloned the mesh successfully.

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

Code explained:

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

Save the file, open MeshStudy.cs, and add the following into 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;
}
```

Code explained:

1. Checks that both original and clone mesh exists.
2. Resets `cMesh` to the original mesh.
3. Assigns `cMesh` back to `oMeshFilter`.

Save the file and return to Unity. In the Inspector, click on the Test Edit button to mess with the cube’s mesh. Next, press the Reset button; the cube should return to its original form.

### Understanding Vertices and Triangles With Unity

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

The Mesh Class:

• Vertices are stored as an array of `Vector3`s.
• Triangles are stored as an array of integers that correspond to the indices of its array of vertices.

So in a simple Quad mesh that consists of four vertices and two triangles, its mesh data would be as follows:

### Presenting Vertices

Here, you want to present the vertices on the cube with blue colored dots.

In MeshInspector.cs, look for 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);
}
```

Code explained:

1. The `handleTransform` gets Transform values from `mesh`.
2. The `handleRotation` gets the current pivot Rotation mode.
3. Loops through the mesh vertices and draws dots with `ShowPoint()`.

In the `ShowPoint()` function, right after the `//draw dot` comment, add:

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

Code explained:
This line converts the vertex local position into world space.

Within the same function, in the `if statement` block, right after the previously added line of code, add the following:

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

Code explained:

1. Sets the color, size and position of the dot, with the `Handles` utility class.
2. `Handles.FreeMoveHandle()` makes an unconstrained movement handle to facilitate the dragging action, which you will need in the next section.

Save the file and return to Unity. Check out the cube’s property in the Inspector and be sure that the Move Vertex Point option is on. You should now see the mesh marked with some blue dots on screen. There you go ~ the vertices of the cube mesh! Try it with other 3D objects and see the results.

### Moving a Single Vertex

Start with the most basic step of mesh manipulation by moving a single vertex first.

Go to MeshInspector.cs. Inside the `ShowPoint()` function, right after the `//drag` comment, just before the closing braces in the `if statement` block, add the following:

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

Code explained:

1. `GUI.changed` monitors any changes made to the dots, which works nicely with `Handles.FreeMoveHandle()` to detect a dragging action.
2. On drag vertex, the `mesh.DoAction()` function will receive its index and Transform values as params. Since the vertex Transform values are in world space, you convert it to local space with `InverseTransformPoint()`.

Save the script file and go to MeshStudy.cs. In `DoAction()`, after the opening braces 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
```

Code explained:

1. You upate the target vertex with `newPos`.
2. You assign the updated vertices back to `cMesh.vertices`.
3. You re-calculate and re-draw the mesh to reflect the change with `RecalculateNormals()`.

Save the file and return to Unity. Try dragging one of the dots on the cube; did you see a broken mesh?

It seems like some of the vertices share the same position, so when you pull only one, the other vertices stay behind, and your mesh breaks. In the next section, you will fix that problem. :]

### Finding All Similar Vertices

Visually, a cube mesh is made up of eight vertices, six sides and 12 triangles. Let’s see if that is true.

Go to MeshStudy.cs, before the `Start()` function, and look for a variable named `vertices`. You will see this:

```
[HideInInspector]
public Vector3[] vertices;
```

Code explained:
`[HideInInspector]` hides a public variable from the Inspector window.

Now comment out that attribute:

```
//[HideInInspector]
public Vector3[] vertices;
```

Note: Hiding the vertices value with `[HideInInspector]` helps with more complicated 3D meshes. As the array size of vertices can go into the thousands, this will cause Unity to freeze up if you try to view the array value in the Inspector.

Save the file and return to Unity. Go to the Inspector. You can now see the vertices property under the Mesh Study script component. Click on the arrow icon beside it; it should expand to an array of `Vector3` elements.

You can see that the array size is 24, which means that there are vertices sharing the same position! Remember to uncomment `[HideInInspector]` before you proceed.

[spoiler title = “Why 24 Vertices?”]
There are many theories to this. But the simplest answer is that:
A cube has six sides, and each side is made up of four vertices to form a plane.

So, the calculation: 6 x 4 = 24 vertices.

Feel free to explore the other answers. For now, just know that certain meshes will have vertices sharing the same position.

[/spoiler]

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

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

Code explained:

1. Gets the target vertex position, to be used as an argument to the `FindRelatedVertices()` method.
2. This method returns a list of indices (that correspond to vertices) that share the same position as the target vertex.
3. Loops through that list and update the related vertices with `newPos`.
4. Assigns the updated `vertices` back to `cMesh.vertices`. Then `RecalculateNormals()` to re-draw the mesh with the new values.

Save the file and return to Unity. Click and drag any of the vertices; the mesh should now retain its form without breaking.

Now that you have covered the first step in mesh manipulation, save the scene and move on to the next segment.

## Manipulating Meshes

In this segment, you will learn about manipulating meshes in real time. While there are many ways to do that, this tutorial will feature the most basic form of mesh manipulation, which is simply the pushing and pulling of predefined vertices of the mesh.

No math…

### Collecting the Selected Indices

Start by selecting the vertices to be displaced in real time.

Open up the 02 Create Heart Mesh scene inside the Scenes folder. You will see a red colored sphere in the Scene view. Select the Sphere in the Hierarchy and go to the Inspector. You will see the Heart Mesh script component attached.

Now, you want an Editor script for this object to display the mesh vertices in the Scene view. Go to the Editor folder and double-click on HeartMeshInspector.cs.

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

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

Code explained:

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

In `OnInspectorGUI()`, before the closing braces, add the following:

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

Code explained:
This adds a custom Reset button in the Inspector to invoke `mesh.ClearAllData()`.

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

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

Code explained:
This clears the values in `selectedIndices` and `targetIndex`. It also resets `targetVertex` to zero.

Save the file and return to Unity. Select the Sphere and go to the HeartMesh script component in the Inspector. Expand Selected Indices by clicking on the arrow icon beside it. This helps you monitor every vertex that you add into the list.

Toggle on Is Edit Mode with the Checkbox icon beside it. This will draw the mesh’s vertices in the Scene view. When you click on the blue dots, the values should be updated in Selected Indices accordingly. Also test the Clear Selected Vertices button to make sure it clears all values correctly.

Note: You have the option to show/hide the transform handle with Show Transform Handle in the custom Inspector. Just remember not to panic when you find the Transform handle missing from your other scenes! Be sure to switch it back on before you exit.

### Deforming the Sphere Into a Heart Shape

Updating mesh vertices in real time basically involves three steps:

1. Copy the current mesh vertices (before animation) to `mVertices`.
2. Do calculations and update values on `mVertices`.
3. Update the current mesh vertices with `mVertices` on every step change and get Unity to auto calculate the normals.

Go to HeartMesh.cs, and before the `Start()` function, declare the following variables:

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

Code explained:

1. Radius of area affected by the targeted vertex.
2. The strength of the pull.
3. How long the animation will run.
4. Current index of the `selectedIndices` list.

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

```
currentIndex = 0;
```

Code explained:
When the player starts, `currentIndex` is set to 0 — the first index of the `selectedIndices` list.

Still in the `Init()` function, before the closing braces of the `else statement` block, add:

```
StartDisplacement();
```

Code explained:
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;
```

Code explained:

1. Single out the `targetVertex` to start the animation.
2. Set the start time and change `isAnimate` to true.

After the `StartDisplacement()` function, create the `FixedUpdate()` function with the following:

```
void FixedUpdate() //1
{
if (!isAnimate) //2
{
return;
}

runtime = Time.time - starttime; //3

if (runtime < duration)  //4
{
Vector3 targetVertexPos = oFilter.transform.InverseTransformPoint(targetVertex);
}
else //5
{
currentIndex++;
if (currentIndex < selectedIndices.Count) //6
{
StartDisplacement();
}
else //7
{
oMesh = GetComponent<MeshFilter>().mesh;
isAnimate = false;
}
}
}
```

Code explained:

1. The `FixedUpdate()`function runs on a fixed FPS loop.
2. If `isAnimate` is false, skip the code below.
3. Updates the `runtime` of the animation.
4. If `runtime` is within the `duration` limit, get the world space coordinates of `targetVertex` and `DisplaceVertices()` surrounding the target vertex with the `pullvalue` and `radiusofeffect` as params.
5. Otherwise, time is up. Add one to `currentIndex`.
6. Checks if `currentIndex` is within the number of `selectedIndices`. Move on to the next vertex in the list with `StartDisplacement()`.
7. Otherwise, at the end of the list, update `oMesh` data with the current mesh and set `isAnimate` to false to stop the animation.

In `DisplaceVertices()`, add the following:

```
Vector3 currentVertexPos = Vector3.zero;

for (int i = 0; i < mVertices.Length; i++) //2
{
currentVertexPos = mVertices[i];
float sqrMagnitute = (currentVertexPos - targetVertexPos).sqrMagnitude; //3
{
continue; //4
}
float distance = Mathf.Sqrt(sqrMagnitute); //5
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();
```

Code explained:

1. The square of the radius.
2. Loops through each vertex in the mesh.
3. Gets `sqrMagnitude` between `currentVertexPos` and `targetVertexPos`.
4. If `sqrMagnitude` exceeds `sqrRadius`, continue to the next vertex.
5. Otherwise, proceed on to determine the `falloff` value, based on the current vertex `distance` from the center point of area of effect.
6. Sums up the new `Vector3` position and applies its Transform to the current vertex.
7. On exiting the loop, assign the updated `mVertices` to `oMesh` data, and have Unity adjust the normals.

Original Source of the Falloff Technique

Save your file and return to Unity. Select the Sphere, go to the HeartMesh component, and try adding some vertices into your Selected Indices property. Turn off Is Edit mode and press Play to preview your work.

Play around with the Radiusofeffect, Pullvalue and Duration settings to see different results. When you are ready, update the settings per the screenshot below.

Press Play. Did your sphere balloon into a heart shape?

Congratulations! In the next section, you will save the mesh into a prefab for further use.

### Saving Your Mesh in Real Time

To save the procedural heart-shaped mesh in Play mode, you need to prepare a prefab that has a 3D object as its child, then replace its mesh asset with a new one via a script.

In the Project view, find the CustomHeart in the Prefabs folder. Click on the Arrow icon to expand its contents and select Child. You will see a Sphere object in the Inspector preview screen. This is the prefab that will hold the new mesh data.

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

```
{
string path = "Assets/Prefabs/CustomHeart.prefab"; //1

if (GUILayout.Button("Save Mesh"))
{
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

gameObj.GetComponentInChildren<MeshFilter>().mesh = pfMesh; //5
PrefabUtility.ReplacePrefab(gameObj, pfRef, ReplacePrefabOptions.Default); //6
Object.DestroyImmediate(gameObj); //7
}
}
```

Code explained:

1. Sets `path` to the CustomHeart prefab object.
2. Creates two objects from the CustomHeart prefab, one to be instantiated as a GameObject (`pfObj`), the other one as a reference (`pfRef`).
3. Creates an instance of the mesh asset `pfMesh` from CustomHeart. If not found, create a new mesh, otherwise clear existing data.
4. Updates `pfMesh` with new mesh data, and adds it as an asset to CustomHeart.
5. Updates the mesh asset in `gameObj` with `pfMesh`.
6. Replaces CustomHeart with `gameObj` by matching pre-existing connections.
7. Destroys `gameObj` immediately.

Save your file and go to HeartMesh.cs. In the public method `SaveMesh()`, after `nMesh` instantiates, add in the following:

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

Code explained:
Returns a mesh asset with values from the heart-shaped mesh.

Save the file and return to Unity. Press Play. When the animation ends, a Save Mesh button will appear in the Inspector. Click on the button to save your new mesh, then stop the player.

Go to the Prefabs folder and check out the CustomHeart prefab. You should now see a spanking new heart-shaped mesh sitting nicely in your CustomHeart prefab object.

Good Job!

## Putting It All Together

In the previous scene, the `DisplaceVertices()` function uses the Falloff formula to determine the pull strength to be applied to each vertex within the defined radius. The ‘fall off’ point — where the pull strength starts decaying — depends on the type of Falloff used: Linear, Gaussian or Needle. Each type produces different results on the mesh.

Like cupcake toppings…

In this section, you will look at another option to manipulate vertices with the use of a pre-fixed curve. Based on the principle that velocity equals distance divided by time (d=(v/t)), you can determine the vector’s position by referencing its distance divided by its time factor.

### Using the Curve Method

Save your current scene and open up 03 Customize Heart Mesh from the Scenes folder. You will see an instance of the CustomHeart prefab in the Hierarchy. Click on the Arrow icon beside 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. The asset should now change from HeartMesh to clone.

Next, open up CustomHeart.cs from the Scripts folder. Before the `Start()` function, add in the following:

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

Code explained:
This creates a public enum named `CurveType` and makes it available in theInspector.

Go to `CurveType1()` and add in 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
```

Code explained:

1. The basic curve consists of three points. Set and plot the points for the first curve.
2. Generate the 1st curve with `Curve()` and assign its values to `curve`. The curve drawn can be a preview if you set the last parameter to true.

Go to `CurveType2()` and add in 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
```

Code explained:

1. Set and plot the points for the second curve.
2. Generate the second curve with `Curve()` and assign its values to `curve`. The curve drawn can be a preview if you set the last parameter to “True.”

In `StartDisplacement()`, before the closing braces, add the following:

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

Code explained:
Here, you check the `curveType` option the user had selected and you generate the `curve` accordingly.

In `DisplaceVertices()`, inside the `for-loop statement`, before the closing braces, 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]);
```

Code explained:

1. Get the curve’s position at the given `distance` and multiply its `y` value by `force` to get `increment`.
2. Create a new `Vector3` data type to store the new position for the current vertex and apply its Transform accordingly.

Save the file and return to Unity. Check out the properties in the CustomHeart component on the Child GameObject. You will see the drop-down option to select a Curve Type. In the Edit Type drop-down menu, select Add Indices or Remove Indices to update your list of vertices and experiment with different settings.

To see detailed results of the different curve types, enter the values per the screenshot:

Set Curve Type option to Curve1, check that Edit Type is set to None and press Play. You should see the mesh fans out into a pattern. Move the model around until you see its side-view and compare the results of both curve types. Here you can see how the Curve Type selected affects the displacement of the mesh.

That’s it! Feel free to press Clear Selected Vertices to reset the Selected Indices and experiment with your own patterns. But remember that there are other factors that will affect the end result of the mesh, these are:

• The size of the radius.
• The spread of vertices within the area.
• The pattern position of the selected vertices.
• The method that you choose for displacement.

## Where to Go From Here?

Files to the final project are included in the “Download Materials” link at the top and bottom of this tutorial.

Don’t stop here! Try out more advanced techniques with Procedural Maze Generation.

I hope you have enjoyed this tutorial and found the information useful. Special credits to Jasper Flick from Catlike Coding for his great tutorials that helped me put together the demos for this project.

Feel free to join the discussion forum below for any questions or comments!