Manipulación en tiempo real de mallas en la unidad

imagen

Una de las ventajas de Unity como plataforma para el desarrollo de juegos es su potente motor 3D. En este tutorial, aprenderá sobre el mundo de los objetos 3D y la manipulación de mallas.

Debido al crecimiento de las tecnologías de realidad virtual y aumentada (VR / AR), la mayoría de los desarrolladores se enfrentan a conceptos complejos de gráficos en 3D. Deje que este tutorial sea el punto de partida para ellos. No se preocupe, no habrá matemáticas 3D complicadas, ¡solo corazones, dibujos, flechas y muchas cosas interesantes!

Nota: este tutorial está dirigido a usuarios que están familiarizados con Unity IDE y tienen experiencia en programación en C #. Si no tiene ese conocimiento, primero estudie los tutoriales Introducción a la interfaz de usuario de Unity e Introducción a las secuencias de comandos de Unity .

Necesitará una versión de Unity no inferior a 2017.3.1. La última versión de Unity se puede descargar aquí . Este tutorial utiliza un editor personalizado, y puede obtener más información sobre ellos en el tutorial Extending the Unity Editor .

Llegar al trabajo


Para comenzar, familiarícese con los términos básicos de los gráficos en 3D, que le permitirán comprender mejor el tutorial.

Términos técnicos básicos de gráficos 3D:

  • Vértices : cada vértice es un punto en el espacio 3D.
  • Malla : contiene todos los vértices, bordes, triángulos, normales y datos UV del modelo.
  • Filtro de malla : almacena datos de malla del modelo.
  • Renderizador de malla: renderiza los datos de malla en la escena.
  • Normales : el vector de un vértice o superficie. Se dirige hacia afuera, perpendicular a la superficie de la malla.
  • Líneas / bordes : líneas invisibles que conectan vértices entre sí.
  • Triángulos : formados conectando tres picos.
  • Mapa UV : adjunta material a un objeto, creando una textura y color para él.

La anatomía de un objeto 3D comienza con su malla. La creación de esta malla comienza en su parte superior. Las líneas invisibles que conectan estos vértices forman triángulos que definen la forma básica del objeto.


Luego, los datos normales y UV establecen el sombreado, el color y la textura. Los datos de malla se almacenan en un filtro de malla, y el renderizador de malla usa estos datos para dibujar un objeto en la escena.

Es decir, el pseudocódigo para crear un modelo 3D se ve así:

  • Cree una nueva malla llamada "myMesh".
  • Agregue datos a las propiedades de los vértices y triángulos myMesh.
  • Cree un nuevo filtro de malla llamado "myMeshFilter".
  • Establezca la propiedad de malla myMeshFilter en myMesh.

Una vez que haya dominado los conceptos básicos, descargue el proyecto , descomprima los archivos y ejecute la pieza de trabajo del proyecto en Unity. Mire la estructura de carpetas en la ventana Proyecto :


Descripción de carpetas:

  • Prefabricados : contiene el prefabricado Esfera que se usará para guardar la malla 3D durante la ejecución de la aplicación.
  • Escenas : contiene las tres escenas que usamos en este tutorial.
  • Editor : los scripts dentro de esta carpeta nos brindan las súper características en el editor que usamos en el desarrollo.
  • Scripts : aquí están los scripts en tiempo de ejecución que se adjuntan al GameObject y se ejecutan al hacer clic en Reproducir .
  • Materiales : esta carpeta contiene el material para la malla.

En la siguiente sección, crearemos un editor personalizado para visualizar la creación de una malla 3D.

Cambiar mallas con el editor personalizado


Abra el 01 Mesh Study Demo ubicado en la carpeta Scenes . En la ventana Escena , verá un cubo 3D:


Antes de entrar en la malla, echemos un vistazo al script del editor personalizado.

Editar un guión de editor


Seleccione la carpeta Editor en la ventana Proyecto . Los scripts en esta carpeta agregan funcionalidad al editor (Editor) durante el desarrollo y no están disponibles en el modo Build.


Abra MeshInspector.cs y vea el código fuente. Todos los scripts del Editor deben implementar la clase Editor , su atributo CustomEditor le dice a la clase Editor qué tipo de objeto es. OnSceneGUI() es un método de evento que permite renderizar en la ventana de escena; OnInspectorGUI() permite agregar elementos GUI adicionales al Inspector.

En MeshInspector.cs, antes de iniciar la clase MeshInspector agregue lo siguiente:

 [CustomEditor(typeof(MeshStudy))] 

Explicación del código: El atributo CustomEditor le dice a Unity qué tipo de objeto puede modificar la clase del editor personalizado.

En OnSceneGUI() antes de EditMesh() agregue lo siguiente:

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

Explicación del código: la clase Editor tiene una variable target estándar. Aquí, el target es una conversión a MeshStudy . Ahora, el editor personalizado dibujará todos los GameObjects en la ventana Scene y MeshStudy.cs adjuntos. Agregar mensajes de depuración le permite verificar en la consola que el editor personalizado se está ejecutando realmente.

Guarde el archivo y regrese a Unity. Vaya a la carpeta Scripts y arrastre MeshStudy.cs al GameObject Cube en la Jerarquía para adjuntarlo.


Ahora debería aparecer el mensaje "El editor personalizado se está ejecutando" en la consola, ¡y esto significa que hicimos todo bien! Puede eliminar el mensaje de depuración para que no nos moleste en la consola.

Clonando y tirando la malla


Cuando trabaje con una malla 3D en modo Edición usando el editor personalizado, tenga cuidado de no sobrescribir la malla predeterminada de Unity. Si esto sucede, deberá reiniciar Unity.

Para clonar de forma segura la malla sin sobrescribir el formulario original, cree una copia de la malla desde la propiedad MeshFilter.sharedmesh y asígnela nuevamente al filtro de malla.

Para hacer esto, haga doble clic en MeshStudy.cs en la carpeta Scripts para abrir el archivo en el editor de código. Este script hereda de la clase MonoBehaviour , y su función Start() no se ejecuta en modo Editar.

En MeshStudy.cs, antes de iniciar la clase MeshStudy agregue lo siguiente:

 [ExecuteInEditMode] 

Explicación del código: después de agregar este atributo, la función Start() se ejecutará tanto en modo Play como en modo Edit. Ahora podemos primero instanciar el objeto de malla y clonarlo.

En InitMesh() agregue el siguiente código:

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

Explicación del código:

  1. Obtiene la malla oMesh original del componente MeshFilter .
  2. Copia cMesh a una nueva cMesh malla.
  3. Asigna el filtro de malla de malla copiado nuevamente.
  4. Actualiza variables locales.

Guarde el archivo y regrese a Unity. El mensaje "Init & Cloned" debería mostrarse en la consola de depuración. Seleccione el Cube GameObject en la Jerarquía y verifique sus propiedades en el Inspector . El filtro de malla debería mostrar un elemento de malla llamado clon . Genial Esto significa que hemos clonado con éxito la malla.


En la carpeta Editor, navegue hasta MeshInspector.cs . En OnInspectorGUI() , después de la segunda línea de código, agregue lo siguiente:

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

Explicación del código:

  1. Este código dibuja un botón Restablecer en el Inspector .
  2. Cuando se presiona, llama a la función Reset() en MeshStudy.cs .

Guarde el archivo, abra MeshStudy.cs y agregue el siguiente código a la función 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; } 

Explicación del código:

  1. Verificación de la existencia de la fuente y la malla clonada.
  2. Restablezca cMesh a la malla original.
  3. Asignación a cMesh oMeshFilter .
  4. Actualización de variables locales.

Guarde el archivo y regrese a Unity. En el Inspector, haga clic en el botón Editar prueba para distorsionar la malla del cubo. Luego, haga clic en el botón Restablecer ; el cubo debe volver a su forma original.


Explicación de vértices y triángulos en Unity


Una malla consiste en vértices conectados por bordes en triángulos. Los triángulos definen la forma básica del objeto.

Clase de malla:

  • Los vértices se almacenan como una matriz de valores de Vector3 .
  • Los triángulos se almacenan como una matriz entera correspondiente a los índices de la matriz de vértices.

Es decir, en una malla cuádruple simple, que consta de cuatro vértices y dos triángulos, los datos de la malla se verán así:


Mapeo de vértices


Aquí queremos mostrar los vértices del cubo como puntos azules.

En MeshInspector.cs entraremos en la función EditMesh() y agregaremos lo siguiente:

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

Explicación del código:

  1. handleTransform obtiene valores de transformación de la mesh .
  2. handleRotation obtiene el modo de rotación de la unión actual.
  3. Atraviese los vértices de la malla y dibuje los puntos con ShowPoint() .

En la función ShowPoint() , inmediatamente después del comentario //draw dot , agregue lo siguiente:

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

Explicación del código: esta línea convierte la posición local del vértice en una coordenada en el espacio mundial.

En la misma función, en el bloque if , inmediatamente después de la línea de código recién agregada, agregue lo siguiente:

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

Explicación del código:

  1. Establece el color, el tamaño y la posición de un punto utilizando la clase de ayuda Handles .
  2. Handles.FreeMoveHandle() crea un manipulador de movimiento ilimitado que simplifica la operación de arrastrar y soltar, lo cual es útil para nosotros en la siguiente sección.

Guarde el archivo y regrese a Unity. Verifique la propiedad del cubo en el Inspector y asegúrese de que la opción Mover punto de vértice esté habilitada. Ahora debería ver que la malla en la pantalla está marcada con varios puntos azules. Aquí están: ¡la parte superior de la malla del cubo! Intente hacer esto con otros objetos 3D y observe los resultados.


Mueve un solo vértice


Comencemos con el paso más simple de manipular la malla: mover un solo vértice.

Vaya a MeshInspector.cs . Dentro de la función ShowPoint() , inmediatamente después del comentario //drag y justo antes de los corchetes de cierre del bloque if , agregue lo siguiente:

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

Explicación del código:

  1. GUI.changed realiza un seguimiento de todos los cambios que ocurren con los puntos, y funciona bien junto con Handles.FreeMoveHandle() para reconocer una operación de arrastrar y soltar.
  2. Para el vértice arrastrable, la función mesh.DoAction() recibe sus valores de índice y transformación como parámetros. Dado que los valores de transformación del vértice están en el espacio mundial, los convertimos al espacio local usando InverseTransformPoint() .

Guarde el archivo de script y vaya a MeshStudy.cs . En DoAction() , después de los corchetes de apertura, agregue lo siguiente:

 PullOneVertex(index, localPos); 

Luego agregue lo siguiente a la función PullOneVertex() :

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

Explicación del código:

  1. Actualizamos el vértice objetivo con el valor newPos .
  2. cMesh.vertices valores de vértice actualizados nuevamente a cMesh.vertices .
  3. En RecalculateNormals() recalculamos y redibujamos la malla para que coincida con los cambios.

Guarde el archivo y regrese a Unity. Intenta arrastrar puntos en el cubo; viste una malla rota?


Parece que algunos de los vértices tienen la misma posición, por lo que cuando arrastramos solo uno, los vértices restantes permanecen detrás de él y la malla se rompe. En la siguiente sección, solucionaremos este problema.

Encontrar todos los vértices similares


Visualmente, una malla de cubo consta de ocho vértices, seis lados y 12 triángulos. Vamos a ver si esto es así.


Abra MeshStudy.cs , eche un vistazo al frente de la función Start() y encuentre la variable de vertices . Veremos lo siguiente:

 [HideInInspector] public Vector3[] vertices; 

Explicación del código: [HideInInspector] oculta una variable compartida de la ventana del Inspector .

Comente este atributo:

 //[HideInInspector] public Vector3[] vertices; 

Nota: ocultar valores de vértice ayuda [HideInInspector] con mallas 3D más complejas. Dado que el tamaño de la matriz de vértices puede alcanzar miles de elementos, esto puede conducir a la inhibición de Unity al intentar ver el valor de la matriz en el Inspector.

Guarde el archivo y regrese a Unity. Ve al inspector . Ahora, bajo el componente de script Mesh Study , ha aparecido la propiedad de vértices . Haga clic en el ícono de flecha al lado; entonces Vector3 matriz de elementos Vector3 .


¡Puedes ver que el tamaño de la matriz es 24, es decir, hay vértices que tienen la misma posición! Antes de continuar, asegúrese de descomentar [HideInInspector] .

¿Por qué hay 24 vértices?
Hay muchas teorías sobre este tema. Pero la respuesta más simple es: el cubo tiene seis lados, y cada lado está compuesto por cuatro vértices que forman un plano.

Por lo tanto, el cálculo es el siguiente: 6 x 4 = 24 vértices.

Puedes buscar otras respuestas. Pero por ahora, es lo suficientemente simple como para saber que algunas mallas tendrán vértices que tienen la misma posición.

En MeshStudy.cs, reemplace todo el código dentro de la función DoAction() con lo siguiente:

 PullSimilarVertices(index, localPos); 

Vayamos a la función PullSimilarVertices() y agreguemos lo siguiente:

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

Explicación del código:

  1. obtenemos la posición del vértice objetivo, que se usará como argumento para el método FindRelatedVertices() .
  2. Este método devuelve una lista de índices (correspondientes a vértices) que tienen la misma posición que el vértice objetivo.
  3. El bucle atraviesa la lista completa y establece los vértices correspondientes en newPos .
  4. cMesh.vertices vertices actualizados de nuevo a cMesh.vertices . Luego llamamos a RecalculateNormals() para volver a dibujar la malla con los nuevos valores.

Guarde el archivo y regrese a Unity. Arrastra cualquiera de los vértices; ahora la malla debe conservar su forma y no colapsar.


Ahora que hemos completado el primer paso para manipular las mallas, guarde la escena y pase a la siguiente sección.

Manipulación de malla


En esta sección, aprenderá a manipular mallas en tiempo real. Hay muchas formas, pero en este tutorial veremos el tipo más simple de manipulación de malla, es decir, mover los vértices de malla creados previamente.

Recopilar índices seleccionados


Comencemos seleccionando los vértices que moveremos en tiempo real.

Abra Scene 02 Create Heart Mesh desde la carpeta Scenes . En la ventana de escena, verá una esfera roja. Seleccione Esfera en la Jerarquía y vaya al Inspector . Verá que el componente de script Heart Mesh está conectado al objeto.

Ahora necesitamos la secuencia de comandos del Editor para que este objeto muestre los vértices de la malla en la ventana Escena. Vaya a la carpeta Editor y haga doble clic en HeartMeshInspector.cs .

En la función ShowHandle() , dentro del bloque if , agregue lo siguiente:

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

Explicación del código:

  1. Establece y muestra los vértices de la malla como un tipo Handles.Button .
  2. Cuando se hace clic, agrega el índice seleccionado a la mesh.selectedIndices presionada, mesh.selectedIndices .

En OnInspectorGUI() , antes del corchete de cierre, agregue lo siguiente:

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

Explicación del código: así es como agregamos un botón Restablecer al Inspector para llamar a mesh.ClearAllData() .

Guarde el archivo y abra HeartMesh.cs desde la carpeta Scripts . En la función ClearAllData() , agregue lo siguiente:

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

Explicación del código: el código borra los valores en selectedIndices targetIndex selectedIndices y el targetIndex . También restablece targetVertex .

Guarde el archivo y regrese a Unity. Seleccione Esfera y vaya al Inspector para el componente de script HeartMesh . Expanda los Índices seleccionados haciendo clic en el icono de flecha al lado. Esto nos permitirá rastrear cada vértice agregado a la lista.

Habilite el modo de edición con la casilla de verificación junto a él. Debido a esto, los vértices de la malla se dibujarán en la ventana de escena. Al hacer clic en los puntos azules en los Índices seleccionados , los valores deberían cambiar en consecuencia. Pruebe también el botón Borrar vértices seleccionados para asegurarse de que borra todos los valores.


Nota: en el Inspector personalizado modificado, tenemos la opción de mostrar / ocultar el manipulador de transformación utilizando Mostrar controlador de transformación . ¡No se asuste si no encuentra el manipulador Transformar en otras escenas! Enciéndelo antes de salir.

Convertir una esfera en un corazón


Cambiar los vértices de malla en tiempo real consiste esencialmente en tres pasos:

  1. Copie los vértices de malla actuales (antes de la animación) en mVertices .
  2. mVertices cálculos y cambiamos los valores en mVertices .
  3. Actualice los vértices de malla actuales con mVertices cuando cambie en cada paso y permita que Unity calcule automáticamente las normales.

Abra HeartMesh.cs y las siguientes variables antes de la función Start() :

 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; 

Explicación del código:

  1. El radio del área afectada por el vértice objetivo.
  2. Fuerza de arrastre
  3. La duración de la animación.
  4. El índice actual de la lista de selectedIndices .

En la función Init() , antes del bloque if , agregue lo siguiente:

 currentIndex = 0; 

Explicación del código: al comienzo del juego, currentIndex en 0, el primer índice de la lista de currentIndex selectedIndices .

En la misma función Init() , antes del corchete de cierre del bloque else , agregue lo siguiente:

 StartDisplacement(); 

Explicación del código: ejecute la función StartDisplacement() si isEditMode es falso.

Dentro de la función StartDisplacement() , agregue lo siguiente:

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

Explicación del código:

  1. Seleccione targetVertex para comenzar la animación.
  2. Establezca la hora de inicio y cambie el valor de isAnimate a verdadero.

Después de la función StartDisplacement() , cree la función FixedUpdate() con el siguiente código:

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

Explicación del código:

  1. La función FixedUpdate() se ejecuta en un bucle FPS fijo.
  2. Si isAnimate es falso, omita el siguiente código.
  3. Cambiar animaciones de runtime .
  4. Si el runtime está dentro de la duration , obtenemos las coordenadas mundiales de targetVertex y DisplaceVertices() , cubriendo el vértice objetivo con los radiusofeffect pullvalue y radiusofeffect .
  5. De lo contrario, se acabó el tiempo. Agregue uno a currentIndex .
  6. Compruebe si currentIndex entre los currentIndex selectedIndices . Vaya al siguiente vértice de la lista usando StartDisplacement() .
  7. De lo contrario, al final de la lista, cambie los datos de oMesh a la malla actual y isAnimate en false para detener la animación.

En DisplaceVertices() agregue lo siguiente:

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

Explicación del código:

  1. El cuadrado del radio.
  2. Recorremos cada vértice de la malla.
  3. sqrMagnitude entre currentVertexPos y targetVertexPos .
  4. Si sqrMagnitude excede sqrRadius , vaya al siguiente vértice.
  5. De lo contrario, continúe definiendo el valor de falloff , que depende de la distance vértice actual desde el punto central del alcance.
  6. Vector3 nueva posición de Vector3 y aplique su Transformar al vértice actual.
  7. Cuando sale del bucle, asignamos los valores cambiados de mVertices a los mVertices y mVertices a Unity a recalcular las normales.

Fuente de Falloff Technology
La fórmula original se toma del archivo del paquete de activos de Ejemplos de procedimientos , que se puede descargar de forma gratuita desde la Unidad de activos de Unity.

Guarde el archivo y regrese a Unity. Seleccione Esfera , vaya al componente HeartMesh e intente agregar algunos vértices a la propiedad Índices seleccionados . Desactive el modo Is Edit y haga clic en Reproducir para ver el resultado de su trabajo.


Experimente con los valores Radiusofeffect , Pullvalue y Duration para obtener resultados diferentes. Cuando esté listo, cambie la configuración de acuerdo con la siguiente captura de pantalla.


Haz clic en Jugar . ¿Tu esfera se ha convertido en un corazón?


Felicidades En la siguiente sección, guardaremos la malla como prefabricada para uso futuro.

Guardar la malla en tiempo real


Para guardar una malla de procedimiento en forma de corazón en el modo Juego, debe preparar un prefabricado cuyo hijo será un objeto 3D y luego reemplazar su activo de malla con uno nuevo usando un script.

En la ventana Proyecto, busque CustomHeart en la carpeta Prefabs . Haga clic en el icono de flecha para expandir su contenido y seleccione Hijo . Ahora verá un objeto Esfera en la ventana de vista previa del Inspector . Este es el prefabricado que almacenará los datos para la nueva malla.


Abra HeartMeshInspector.cs . Dentro de la función OnInspectorGUI() , antes del corchete de cierre, agregue lo siguiente:

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

Explicación del código:

  1. Establece la path a la ruta al objeto prefabricado CustomHeart.
  2. Crea dos objetos a partir del prefabricado CustomHeart, uno para crear una instancia como GameObject ( pfObj ) y el segundo como enlaces ( pfRef ).
  3. Crea una instancia del pfMesh malla pfMesh . Si no se encuentra, crea una nueva malla, de lo contrario, limpia los datos existentes.
  4. pfMesh con nuevos datos de malla y luego los agrega como un activo a CustomHeart .
  5. Rellena un activo de malla en gameObj valor pfMesh .
  6. Reemplaza CustomHeart con gameObj coincidir las conexiones preexistentes.
  7. Destruye instantáneamente gameObj .

Guarde el archivo y vaya a HeartMesh.cs . En el SaveMesh() general SaveMesh() , después de crear la instancia nMesh agregue lo siguiente:

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

Explicación del código: devuelve un activo de malla con valores de una malla en forma de corazón.

Guarde el archivo y regrese a Unity. Haz clic en Reproducir . Una vez completada la animación, aparecerá el botón Guardar malla en el Inspector . Haga clic en el botón para guardar la nueva malla y luego detenga el reproductor.

Vaya a la carpeta Prefabs y mire el prefab CustomHeart. Debería ver que ahora en el objeto prefabricado CustomHeart hay una malla completamente nueva en forma de corazón.


Gran trabajo!

Poniendo todo junto


En la escena anterior, la función DisplaceVertices() utilizó la fórmula Falloff para determinar la fuerza de arrastre que se aplicó a cada vértice dentro de un radio determinado. El punto de "caída", en el que la fuerza de arrastre comienza a disminuir, depende del tipo de caída utilizada: lineal, gaussiana o aguja. Cada tipo produce resultados diferentes en la malla.


En esta sección, veremos otra forma de manipular vértices: usando una curva dada. Tomando la regla de que la velocidad es igual a la distancia dividida por el tiempo (d = (v / t)), podemos determinar la posición del vector, refiriéndonos a su distancia dividida por el tiempo.


Usando el Método de Curva


Guarde la escena actual y abra 03 Personalizar Heart Mesh desde la carpeta Escenas . Verá una instancia de jerarquía del prefabricado CustomHeart. Haga clic en el icono de flecha al lado para expandir su contenido y seleccione Hijo .

Ver sus propiedades en el Inspector .Verá el componente Filtro de malla con el activo de malla de corazón . Adjunte un script de corazón personalizado a Child como componente . Ahora el activo debería cambiar de HeartMesh a clonar .


A continuación, abra CustomHeart.cs desde la carpeta Scripts . Antes de la función, Start()agregue lo siguiente:

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

Explicación del código: aquí se crea una enumeración general bajo el nombre CurveType, después de lo cual se pone a disposición del Inspector .

Vaya CurveType1()y agregue lo siguiente:

 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 

Explicación del código:

  1. Una curva simple consta de tres puntos. Establezca los puntos para la primera curva.
  2. Generamos la primera curva con la ayuda Curve()y asignamos sus valores curve. La curva dibujada se puede mostrar en la vista previa si especifica true como último parámetro.

Vaya CurveType2()y agregue lo siguiente:

 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 

Explicación del código:

  1. Establezca los puntos para la segunda curva.
  2. Generamos la segunda curva con Curve()y asignamos sus valores curve. La curva dibujada se puede mostrar en la vista previa si especifica true como último parámetro.

B StartDisplacement(), antes del corchete de cierre, agregue lo siguiente:

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

Explicación del código: aquí verificamos la opción seleccionada por el usuario curveTypey la generamos en consecuencia curve.

B DisplaceVertices(), dentro de la instrucción de bucle forantes de los corchetes de cierre, agregue lo siguiente:

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

Explicación del código:

  1. Obtenemos la posición de la curva en la dada distancey multiplicamos su valor ypor forcepara obtener increment.
  2. Cree un nuevo tipo de datos Vector3para almacenar la nueva posición del vértice actual y aplique su Transformar en consecuencia.

Guarde el archivo y regrese a Unity. Compruebe las propiedades del componente CustomHeart objeto de juego para niños . Verá una lista desplegable donde puede seleccionar Tipo de curva . En la lista desplegable Editar tipo , seleccione Agregar índices o Eliminar índices para actualizar la lista de vértices y experimentar con diferentes configuraciones.


Para ver resultados detallados para diferentes tipos de curvas, ingrese los valores de acuerdo con la captura de pantalla:


Para la lista Tipo de curva , seleccione Curva1 , asegúrese de que Ninguno esté seleccionado para Editar tipo y haga clic en Reproducir . Debería ver que la malla diverge en el patrón. Gire el modelo para verlo en una vista lateral y compare los resultados para ambos tipos de curvas. Aquí puede ver cómo el Tipo de curva seleccionado afecta el desplazamiento de la malla.



Eso es todo!Puede hacer clic en Borrar vértices seleccionados para restablecer los índices seleccionados y experimentar con sus propios patrones. Pero no olvide que hay otros factores que afectarán el resultado final de la malla, a saber:

  • El valor del radio.
  • La distribución de vértices en el área.
  • La posición del patrón de los vértices seleccionados.
  • El método seleccionado para el desplazamiento.

¿A dónde ir después?


Los archivos del proyecto terminado están en el archivo del proyecto tutorial.

¡No te detengas ahí! Pruebe las técnicas más sofisticadas que se utilizan en el tutorial Unity Procedural Maze Generation .

Espero que hayan disfrutado este tutorial y hayan encontrado útil la información. Un agradecimiento especial que expreso Jasper Flick de Catlike Codificación por sus excelentes tutoriales que me ayudaron a armar un demo para mi proyecto.

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


All Articles