Spanish (Español) translation by Jean Perez (you can also view the original English article)
Junto con muchas otras cosas que han sido reemplazadas rápidamente por la tecnología moderna, parece como si la cinta métrica común puede ser el siguiente en ir. En esta serie de tutoriales de dos partes, estamos aprendiendo cómo usar la realidad aumentada y la cámara en tu dispositivo iOS para crear una aplicación que informará de la distancia entre dos puntos.
En el primer post, creó el proyecto de app y codificados los elementos de la interfaz principal. En este post, nos va acabar por medición entre dos puntos en la escena de la AR. Si no lo has hecho todavía, seguir junto con el primer post para conseguir su proyecto ARKit establecido.
Manipulación de Grifos
Esta es una de las partes más importantes de este tutorial: manejo cuando el usuario golpecitos en su mundo para conseguir una esfera para aparecer exactamente donde aprovechado. Después, calculamos la distancia entre estas esferas finalmente mostrar al usuario su distancia.
Reconocedor de Gesto de Toque
El primer paso en la comprobación de grifos es crear un reconocedor de gesto de toque cuando se inicia la aplicación. Para ello, cree un controlador de grifo como sigue:
// Creates a tap handler and then sets it to a constant let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
La primera línea crea una instancia de la clase UITapGestureRecognizer()
y pasa dos parámetros en la inicialización: el objetivo y la acción. El objetivo es el destinatario de las notificaciones que envía este reconocedor, y queremos que nuestra clase ViewController
a ser objetivo. La acción es simplemente un método que se debe llamar cada vez que hay un grifo.
Para establecer el número de grifos, añadir esto:
// Sets the amount of taps needed to trigger the handler tapRecognizer.numberOfTapsRequired = 1
A continuación, la instancia de la clase que creamos anteriormente necesita saber cuántos grifos son realmente necesarias para activar el reconocedor. En nuestro caso, basta un toque, pero en otras aplicaciones, puede que necesite tener más (como un doble tap) para algunos casos.
Agregue el controlador a la vista de escena como esta:
// Adds the handler to the scene view sceneView.addGestureRecognizer(tapRecognizer)
Por último, esta sola línea de código sólo añade el reconocedor del gesto a la sceneView
, que es donde vamos a estar haciendo todo. Esto es donde la vista previa de la cámara así como de lo que el usuario se golpee directamente para conseguir una esfera para aparecer en la pantalla, así que tiene sentido agregar el reconocedor a la vista con el que interactuará el usuario.
Método Handle Tap
Cuando creamos el UITapGestureRecognizer()
, recordarás que fijar un método handleTap
a la acción. Ahora, estamos listos para declarar ese método. Para ello, simplemente añada lo siguiente a su aplicación:
@objc func handleTap(sender: UITapGestureRecognizer) { // Your code goes here }
Aunque la declaración de la función es bastante explica por sí mismo, usted puede preguntarse por qué hay una etiqueta de @objc
delante de ella. A partir de la versión actual de Swift, para exponer métodos para Objective-C, necesita esta etiqueta. Todo lo que necesitas saber es que #selector
necesita el método referido para Objective-C. Por último, el parámetro de método nos conseguirá la ubicación exacta que fue aprovechada en la pantalla.
Detección de la Ubicación
El siguiente paso en conseguir nuestras esferas para aparecer donde el usuario aprovechado es detectar la posición exacta que aprovechado. Ahora, esto no es tan sencillo como conseguir la ubicación y colocación de una esfera, pero estoy seguro que usted a dominar en ningún momento.
Comience agregando las tres líneas siguientes de código al método handleTap()
:
// Gets the location of the tap and assigns it to a constant let location = sender.location(in: sceneView) // Searches for real world objects such as surfaces and filters out flat surfaces let hitTest = sceneView.hitTest(location, types: [ARHitTestResult.ResultType.featurePoint]) // Assigns the most accurate result to a constant if it is non-nil guard let result = hitTest.last else { return }
Si recuerdas el parámetro que tomamos en el método handleTap()
, puede recordar que se llamaba sender
y era del tipo UITapGestureRecognizer
. Bien, esta primera línea de código simplemente toma el lugar de la llave en la pantalla (en relación a la vista de escena) y establece en una ubicación con nombre location
.
A continuación, estamos haciendo algo que se llama una prueba en el SceneView
sí mismo. Lo que hace esto, en términos simples, consiste en comprobar la escena de objetos reales, tales como tablas, superficies, paredes, pisos, etcetera. Esto nos permite obtener una sensación de profundidad y las medidas muy exactas entre dos puntos. Además, estamos especificando los tipos de objetos para detectar, y como se puede ver, estamos diciendo que busque featurePoints
, que son superficies esencialmente planas, que tiene sentido para una aplicación de medición.
Por último, la línea de código toma el resultado más preciso, que en el caso de hitTest
es el último resultado y comprueba si no es nil
. Si es así, ignora el resto de las líneas en este método, pero si en efecto es un resultado, se asignará a una constante llamada result
.
Matrices
Si usted piensa volver a su clase de álgebra de la high School secundaria, puede recordar matrices, que no podrían haber parecido tan importantes entonces como ahora. Comúnmente se utilizan en la computadora gráficos relacionados con las tareas, y estará recibiendo un atisbo de ellos en esta aplicación.
Añadir las siguientes líneas a su método handleTap()
, y vamos a ir sobre ellos en detalle:
// Converts the matrix_float4x4 to an SCNMatrix4 to be used with SceneKit let transform = SCNMatrix4.init(result.worldTransform) // Creates an SCNVector3 with certain indexes in the matrix let vector = SCNVector3Make(transform.m41, transform.m42, transform.m43) // Makes a new sphere with the created method let sphere = newSphere(at: vector)
Antes de entrar en la primera línea de código, es importante entender que la prueba que hicimos anterior devuelve un tipo de matrix_float4x4
, que es esencialmente una matriz de cuatro de valores flotantes. Ya que estamos en SceneKit, sin embargo, tendremos que convertirlo en algo que SceneKit puede entender, en este caso, a un SCNMatrix4
.
Entonces, vamos a usar esta matriz para crear una SCNVector3
, que, como su nombre lo indica, es un vector con tres componentes. Como puede haber adivinado, esos componentes son x
, y
y z
, para darnos una posición en el espacio. transform.M41
, transform.m42
y transform.m43
son los valores de coordenadas pertinentes para los vectores de tres componentes.
Por último, vamos a usar el método newSphere()
que creamos anteriormente, junto con la información de ubicación que analiza desde el evento de toque, para hacer una esfera y asignarlo a una constante llamada sphere
.
Solucionar el Bug de Doble-Tap
Ahora, usted puede han dado cuenta de un leve defecto en nuestro código; Si el usuario sigue golpeando, una nueva esfera mantener que crearía. Nosotros no queremos porque hace difícil determinar qué esferas deben medirse. Además, es difícil para el usuario hacer un seguimiento de todos los ámbitos!
Para Resolver Con Matrices
El primer paso para solucionar esto es crear una matriz en la parte superior de la clase.
var spheres: [SCNNode] = []
Se trata de una matriz de SCNNodes
porque ese es el tipo que nos devuelve nuestro método newSphere()
que hemos creado hacia el principio de este tutorial. Más tarde nosotros poner las esferas en este conjunto y compruebe cuántas existen. Basándonos en esto, podremos manipular sus números, quitando y los añadiendo.
Enlace Opcional
A continuación, vamos a usar una serie de sentencias if-else y bucles para averiguar si hay cualquier ámbitos de la matriz o no. Para empezar, agregar el siguiente enlace opcional a su aplicación:
if let first = spheres.first { // Your code goes here } else { // Your code goes here }
En primer lugar, estamos comprobando si hay algún elemento de la matriz de las spheres
y si no, ejecutar el código en la cláusula else
.
Auditoría de las Esferas
Después de eso, agregar lo siguiente a la primera parte (el if
rama) de la instrucción if-else:
// Adds a second sphere to the array spheres.append(sphere) print(sphere.distance(to: first)) // If more that two are present... if spheres.count > 2 { // Iterate through spheres array for sphere in spheres { // Remove all spheres sphere.removeFromParentNode() } // Remove extraneous spheres spheres = [spheres[2]] }
Ya que estamos ya en un evento de grifo, sabemos que estamos creando otra esfera. Así que si ya existe una esfera, necesitamos obtener la distancia y mostrar al usuario. Usted puede llamar al método distance()
sobre la esfera, porque más adelante, vamos a crear una extensión de SCNNode
.
A continuación, necesitamos saber si ya hay más de un máximo de dos esferas. Para hacer esto, simplemente estamos utilizando la propiedad count de nuestra gama de spheres
y un if
declaración. Todas las esferas de la matriz en iteración y eliminarlos de la escena. (No te preocupes, más adelante veremos algunos de ellos hacia atrás).
Por último, puesto que estamos ya en el if
declaración en la que nos dice que hay más de dos esferas, podemos eliminar la tercera parte de la matriz de modo que nos aseguramos de que sólo dos quedan en la matriz en todo momento.
Añadir las Esferas
Por último, en la cláusula else
, sabemos que la matriz de spheres
está vacía, así que lo que tenemos que hacer es simplemente añadir la esfera que hemos creado en el momento de la llamada al método. Dentro de la cláusula else
, añadir esto:
// Add the sphere spheres.append(sphere)
¡Yay! Hemos añadido la esfera a nuestra gama de spheres
, y nuestra gama está listo para la siguiente toma. Ahora hemos preparado nuestra matriz con las esferas que debe ser en la pantalla, así que ahora, vamos a sólo añadir estos a la matriz.
Para recorrer y añadir las esferas, agregue este código:
// Iterate through spheres array for sphere in spheres { // Add all spheres in the array self.sceneView.scene.rootNode.addChildNode(sphere) }
Este es sólo un simple bucle for
, y estamos añadiendo las esferas (SCNNode
) como hijo del nodo raíz de la escena. En SceneKit, esta es la forma preferida de agregar cosas.
Método Completo
Esto es lo que debería ser el método de handleTap()
final:
@objc func handleTap(sender: UITapGestureRecognizer) { let location = sender.location(in: sceneView) let hitTest = sceneView.hitTest(location, types: [ARHitTestResult.ResultType.featurePoint]) guard let result = hitTest.last else { return } let transform = SCNMatrix4.init(result.worldTransform) let vector = SCNVector3Make(transform.m41, transform.m42, transform.m43) let sphere = newSphere(at: vector) if let first = spheres.first { spheres.append(sphere) print(sphere.distance(to: first)) if spheres.count > 2 { for sphere in spheres { sphere.removeFromParentNode() } spheres = [spheres[2]] } } else { spheres.append(sphere) } for sphere in spheres { self.sceneView.scene.rootNode.addChildNode(sphere) } }
Cálculo de Distancias
Ahora, si te acordarás, llamamos a un método de distance(to:)
en nuestra SCNNode
, la esfera, y estoy seguro que Xcode es gritarle a usted utilizando un método ilegal. Vamos a acabar ya, mediante la creación de una extensión de la clase SCNNode
.
Para crear una extensión, justo lo siguiente fuera su clase ViewController
:
extension SCNNode { // Your code goes here }
Esto simplemente le permite alterar la clase (es como si estaba editando la clase real). Entonces, vamos a añadir un método que se calcula la distancia entre dos nodos.
Aquí está la declaración de función para hacer eso:
func distance(to destination: SCNNode) -> CGFloat { // Your code goes here }
Verás, hay un parámetro que es otro SCNNode
, y devuelve un CGFloat
como resultado. Para el cálculo real, añadir esto a la función distance()
:
let dx = destination.position.x - position.x let dy = destination.position.y - position.y let dz = destination.position.z - position.z let inches: Float = 39.3701 let meters = sqrt(dx*dx + dy*dy + dz*dz) return CGFloat(meters * inches)
Las tres primeras líneas de código restan la x, y y posición z de la SCNNode
actual de las coordenadas del nodo pasado como parámetro. Más tarde a llevar estos valores en la fórmula de distancia para obtener la distancia. También, porque quiero que el resultado en pulgadas, he creado una constante para el tipo de conversión entre metros y pulgadas para la fácil conversión más adelante.
Ahora, para obtener la distancia entre los dos nodos, a pensar la clase de matemáticas de secundaria: usted puede recordar la fórmula de distancia para el plano cartesiano. Aquí, nosotros estamos aplicando a puntos en el espacio tridimensional.
Finalmente, retornamos el valor multiplicado por el ratio de conversión de pulgadas a la unidad de medida apropiada. Si vives fuera de Estados Unidos, puede dejarlo en metros o convertir a centímetros, si lo desea.
Conclusión
Bueno, eso es Bolero! Esto es lo que debería ser su proyecto final:

Como se puede ver, las mediciones no son perfectas, pero piensa que un ordenador de 15 pulgadas es de alrededor de 14,998 pulgadas, por lo que no es malo!
Ahora sabes cómo medir distancias utilizando la biblioteca nueva de Apple, ARKit
. Esta aplicación se puede utilizar para muchas cosas, y desafiarte a pensar de diferentes maneras que esto puede ser utilizado en el mundo real y no olvide dejar sus pensamientos en los comentarios.
También, asegúrese de comprobar hacia fuera el repositorio de GitHub para esta aplicación. Y mientras esté todavía aquí, comprobar hacia fuera nuestros otros iOS desarrollo tutoriales aquí en Envato Tuts+!
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post