随着其他许多已被我们的现代技术Swift取代的事物,似乎通用的卷尺可能会成为下一步。 在这个分为两部分的系列教程中,我们将学习如何在iOS设备上使用增强现实和相机来创建一个应用程序,该应用程序将报告两个点之间的距离。

处理水龙头

这是本教程最大的部分之一:处理用户何时点击其世界以使球体准确显示在其点击的位置。 稍后,我们将计算这些球体之间的距离,以最终向用户显示其距离。

点击手势识别器

检查轻击的第一步是在应用启动时创建轻击手势识别器。 为此,请如下创建一个tap处理程序:

// Creates a tap handler and then sets it to a constant
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))

第一行创建UITapGestureRecognizer()类的实例,并在初始化时传入两个参数:目标和操作。 目标是该识别器发送的通知的接收者,并且我们希望ViewController类成为目标。 动作只是一种方法,每次点击都会调用该方法。

要设置抽头数,请添加以下内容:

// Sets the amount of taps needed to trigger the handler
tapRecognizer.numberOfTapsRequired = 1

接下来,我们之前创建的类的实例需要知道激活识别器实际上需要多少次轻击。 在我们的情况下,我们只需要单击一下,但是在其他应用中,某些情况下可能需要更多(例如双击)。

将处理程序添加到场景视图中,如下所示:

// Adds the handler to the scene view
sceneView.addGestureRecognizer(tapRecognizer)

最后,这一行代码仅将手势识别器添加到sceneView ,这是我们将要做的所有事情。 这是相机预览以及用户直接点击以便在屏幕上显示球体的地方,因此有必要将识别器添加到与用户进行交互的视图中。

手柄攻丝法

当我们创建UITapGestureRecognizer() ,您可能还记得我们为操作设置了handleTap方法。 现在,我们准备声明该方法。 为此,只需将以下内容添加到您的应用中:

@objc func handleTap(sender: UITapGestureRecognizer) {// Your code goes here
}

尽管函数声明可能是不言自明的,但您可能想知道为什么在其前面有@objc标记。 从Swift的当前版本开始,要将方法公开给Objective-C,您需要此标记。 您只需要知道#selector需要引用的方法即可用于Objective-C。 最后,method参数将使我们获得在屏幕上点击的确切位置。

位置侦测

使我们的球体出现在用户点击的位置的下一步是检测他们点击的确切位置。 现在,这并不像获取位置并放置球体那么简单,但是我相信您会很快掌握它。

首先将以下三行代码添加到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 }

如果您还记得我们在handleTap()方法中使用的参数,您可能会记得它被命名为sender ,并且类型为UITapGestureRecognizer 。 好吧,这第一行代码只是简单地获取了屏幕上水龙头的位置(相对于场景视图),并将其设置为一个恒定的命名location

接下来,我们要对SceneView本身进行点击测试。 简而言之,这就是检查场景中是否有真实物体,例如桌子,表面,墙壁,地板等。这使我们获得了深度感并获得了两点之间相当精确的测量值。 此外,我们指定了要检测的对象的类型,正如您所看到的,我们告诉它寻找featurePoints ,它们本质上是平坦的表面,这对于测量应用程序很有意义。

最后,代码行获取最准确的结果(在hitTest的情况下为最后一个结果),并检查其是否不是nil 。 如果是这样,它将忽略此方法中的其余行,但是如果确实存在结果,则会将其分配给一个名为result的常量。

矩阵

如果您回想起高中代数课,您可能会记得矩阵,这些矩阵在当时似乎并不像现在那么重要。 它们通常用于与计算机图形相关的任务,我们将在此应用程序中一窥它们。

将以下几行添加到handleTap()方法中,我们将详细介绍它们:

// 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)

在进入第一行代码之前,重要的是要了解我们之前做的命中测试返回的类型为matrix_float4x4 ,它实际上是一个四乘四的浮点值矩阵。 既然我们在   不过,我们需要将SceneKit转换为SceneKit可以理解的内容,在这种情况下,将其SCNMatrix4SCNMatrix4

然后,我们将使用此矩阵创建一个SCNVector3 ,顾名思义,它是具有三个分量的向量。 您可能已经猜到了,这些分量是x xyz ,以使我们在空间中处于一个位置。 transform.m41transform.m42transform.m43是三个分量向量的相关坐标值。

最后,让我们使用之前创建的newSphere()方法,以及我们从touch事件中解析的位置信息,来制作一个球体并将其分配给名为sphere的常量。

解决双击错误

现在,您可能已经意识到我们代码中的一个小缺陷。 如果用户不断点击,则将继续创建新的球体。 我们不希望这样做,因为这使得很难确定需要测量的球体。 而且,用户很难跟踪所有领域!

用数组求解

解决此问题的第一步是在类的顶部创建一个数组。

var spheres: [SCNNode] = []

这是一个SCNNodes数组,因为这是我们从在本教程开始时创建的newSphere()方法返回的类型。 稍后,我们将球体放置在此阵列中,并检查有多少个球体。 基于此,我们将能够通过删除和添加数字来操纵它们的数字。

可选装订

接下来,我们将使用一系列if-else语句和for循环来确定数组中是否存在任何球体。 对于初学者,将以下可选绑定添加到您的应用程序:

if let first = spheres.first {// Your code goes here
} else {// Your code goes here
}

首先,我们要检查spheres数组中是否有任何项目,如果没有,请执行 else else子句。

审计领域

之后,将以下内容添加到if-else的第一部分( if分支)中   声明:

// 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 arrayfor sphere in spheres {// Remove all spheressphere.removeFromParentNode()}// Remove extraneous spheresspheres = [spheres[2]]
}

由于我们已经处于点击事件中,因此我们知道我们正在创建另一个领域。 因此,如果已经存在一个球体,我们需要获取距离并将其显示给用户。 您可以在球体上调用distance()方法,因为稍后,我们将创建SCNNode的扩展。

接下来,我们需要知道两个球的最大值是否已经超过最大值。 为此,我们仅使用spheres数组的count属性和if语句。 我们遍历数组中的所有球体并将其从场景中删除。 (不用担心,我们稍后会再回一些。)

最后,由于我们已经在 if语句告诉我们有两个以上的球体,则可以删除数组中的第三个球体,以确保始终保持数组中只有两个。

添加球体

最后,在else子句中,我们知道spheres数组为空,因此我们需要做的只是添加在方法调用时创建的球体。 在您的else子句中,添加以下内容:

// Add the sphere
spheres.append(sphere)

好极了! 我们刚刚将球体添加到我们的spheres数组中,并且我们的数组已准备好进行下一次点击。 现在,我们准备了应该在屏幕上显示的球体数组,所以现在,我们将它们添加到数组中。

为了迭代并添加球体,请添加以下代码:

// Iterate through spheres array
for sphere in spheres {// Add all spheres in the arrayself.sceneView.scene.rootNode.addChildNode(sphere)
}

这只是一个简单的for循环,我们将球体( SCNNode )添加为场景根节点的子级。 在SceneKit中,这是添加内容的首选方式。

完整方法

最终的handleTap()方法应如下所示:

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

计算距离

现在,如果您还记得的话,我们在球体的SCNNode上调用了distance(to:)方法,并且我确定Xcode会因为使用未声明的方法而对您大喊大叫。 现在,通过创建SCNNode类的扩展来结束这SCNNode

要创建扩展,只需在ViewController类之外执行以下操作:

extension SCNNode {// Your code goes here
}

这只是让您更改类(就像您在编辑实际的类一样)。 然后,我们将添加一个方法,该方法将计算两个节点之间的距离。

这是执行此操作的函数声明:

func distance(to destination: SCNNode) -> CGFloat {// Your code goes here
}

如果您看到的话,有一个参数是另一个SCNNode ,它返回一个CGFloat作为结果。 为了进行实际计算,请将其添加到您的distance()函数中:

let dx = destination.position.x - position.x
let dy = destination.position.y - position.y
let dz = destination.position.z - position.zlet inches: Float = 39.3701
let meters = sqrt(dx*dx + dy*dy + dz*dz)return CGFloat(meters * inches)

代码的前三行从作为参数传递的节点的坐标中减去当前SCNNode的x,y和z位置。 稍后,我们将这些值插入距离公式以获取它们的距离。 另外,因为我希望结果以英寸为单位,所以我为米和英寸之间的转换率创建了一个常数,以便以后轻松转换。

现在,要获取两个节点之间的距离,请回想一下中学数学课:您可能还记得笛卡尔平面的距离公式。 在这里,我们将其应用于三维空间中的点。

最后,我们返回该值乘以英寸转换率,以获得适当的度量单位。 如果您居住在美国以外的地方,则可以以米为单位放置它,也可以根据需要将其转换为厘米。

结论

好吧,这就是包装! 这是您最终项目的外观:

如您所见,测量结果并不完美,但它认为15英寸的计算机约为14.998英寸,所以还不错!

您现在知道了如何使用Apple的新库ARKit来测量距离。 这个应用程序可以用于很多事情,我挑战您考虑在现实世界中使用它的不同方式,并确保在下面的评论中留下您的想法。

翻译自: https://code.tutsplus.com/tutorials/code-a-measuring-app-with-arkit-placing-objects-in-the-scene--cms-30448

使用ARKit编写测量应用程序代码:交互和测量相关推荐

  1. 编写并运行php程序,上传所编写的PHP程序代码,并上传运行后的效果截图

    上传所编写的PHP程序代码,并上传运行后的效果截图 更多相关问题 [多选] 对税务机关的下列行政行为,纳税人可以申请行政复议的有(). [多选] 纳税人收到税务机关的行政处罚决定书之后,在法定期限内可 ...

  2. 计算器的程序代码java_求用JAVA编写的计算器程序代码

    展开全部 import java.awt.*;//引入包java.awt中所有的类 import java.awt.event.*;//引入包java.awt.event中所有的类. public c ...

  3. python血压测量程序代码_血压测量程序及评价标准

    操作 准备 10 分 1 .护士准备.着装整洁,洗手 2 .准备用物.血压计.听诊器.记录本和笔 5 5 评估 10 分 1 .了解评估患者的身体活动情况 2 .告之患者量血压的目的,取得患者配合 5 ...

  4. arkit与现实世界距离比_如何使用ARKit和Pusher构建实时增强现实测量应用程序

    arkit与现实世界距离比 by Esteban Herrera 由Esteban Herrera 如何使用ARKit和Pusher构建实时增强现实测量应用程序 (How to Build a Rea ...

  5. 《编写高质量代码:改善c程序代码的125个建议》——第1章 数据,程序设计之根本建议1:认识ANSI C...

    本节书摘来自华章计算机<编写高质量代码:改善c程序代码的125个建议>一书中的第1章,建议1,作者:马 伟 更多章节内容可以访问云栖社区"华章计算机"公众号查看. 第1 ...

  6. java 代码重用需要注意的事项_程序员笔记|编写高性能的Java代码需要注意的4个问题...

    一.并发 无法创建新的本机线程...... 问题1:Java的中创建一个线程消耗多少内存? 每个线程有独自的栈内存,共享堆内存 问题2:一台机器可以创建多少线程? CPU,内存,操作系统,JVM,应用 ...

  7. 《编写高质量代码:改善c程序代码的125个建议》——建议3-5:避免使用浮点数作为循环计数器...

    本节书摘来自华章计算机<编写高质量代码:改善c程序代码的125个建议>一书中的第1章,建议3-5,作者:马 伟 更多章节内容可以访问云栖社区"华章计算机"公众号查看. ...

  8. 《Java和Android开发实战详解》——2.5节良好的Java程序代码编写风格

    本节书摘来自异步社区<Java和Android开发实战详解>一书中的第2章,第2.5节良好的Java程序代码编写风格,作者 陈会安,更多章节内容可以访问云栖社区"异步社区&quo ...

  9. 《编写高质量代码:改善c程序代码的125个建议》——建议2-6:防止无符号整数回绕...

    本节书摘来自华章计算机<编写高质量代码:改善c程序代码的125个建议>一书中的第1章,建议2-6,作者:马 伟 更多章节内容可以访问云栖社区"华章计算机"公众号查看. ...

  10. java jmf 视屏监控的核心代码_Java中利用JMF编写摄像头拍照程序_java

    我把程序分为两种,有趣的和无趣的,最近做了几个有趣的项目,其中一个,应当就算是摄像头拍照程序了.用于现场拍照,生成照片,主要用到java Media Framework(JMF). 首先到SUN下载最 ...

最新文章

  1. 1091 线段的重叠
  2. 搬家Testing.
  3. 中国医药品市场供需状况与竞争前景研究报告2022年
  4. javaScript第六天(1)
  5. 出口同比中国经济三大怪状折射出啥危机?
  6. 工信部副部长怀进鹏:信息产业呈现四大发展特点
  7. (150)System Verilog仿真结束机制
  8. android关机充电流程、充电画面显示
  9. Spring MVC中基于自定义Editor的表单数据处理技巧
  10. 品尝阿里云容器服务:食用注意事项
  11. R语言安装包下载和安装
  12. 一维导热方程c语言,一维热传导方程的推导.doc
  13. TcpClient Class
  14. 【AES】基于FPGA的128位AES加解密系统设计实现
  15. Android生日礼物(含拼图游戏,背景音乐,自动拨号等功能实现)--根据代码规范修改注释以及定义
  16. HTML基础之 HTML5新增视频和音频标签
  17. OpenCV:Scalar数据类型理解
  18. 微信小程序网络请求之设置合法域名后不显示oss、cos的图片问题
  19. 人工智能定价算法的发展,使得默示共谋更为普遍
  20. 移动电影院和民族电影达成战略合作,传递正能量

热门文章

  1. Python输入若干整数求和
  2. 关于快速幂与快速积取模实现的尝试
  3. 阿里云短信接口PHP
  4. 东软实训告诉你:职场上不该说的13种话
  5. 手机怎么下载python呢_安卓手机端怎么安装Python?
  6. iPhone - 不能读取文件 iTunes.Library.itl 因为它是由更高级别的 iTunes 所创建
  7. A4纸网页打印中对应像素的设定和换算
  8. matlab 教案,matlab第七讲教案.doc
  9. 我的硬盘居然被win10安装工具_科研工具 | SPSS 25中文版软件下载和安装教程|兼容WIN10...
  10. 厦门大学继续教育计算机科学毕业难吗,厦门大学工资待遇