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.
  4. Updates local variables.

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.
  4. Updates local variables.

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 Vector3s.
  • 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
{
    mesh.selectedIndices.Add(index); //2
}

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);
        DisplaceVertices(targetVertexPos, pullvalue, radiusofeffect);
    }
    else //5
    {
        currentIndex++;
        if (currentIndex < selectedIndices.Count) //6
        {
            StartDisplacement();
        }
        else //7
        {
            oMesh = GetComponent<MeshFilter>().mesh;
            isAnimate = false;
            isMeshReady = true;
        }
    }
}

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

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
The original formula is from the Procedural Examples asset package file, downloadable for free from the Unity Asset Store.

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:


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

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!



Source link https://www.raywenderlich.com/5128-runtime-mesh-manipulation-with-unity

LEAVE A REPLY

Please enter your comment!
Please enter your name here