太空尺.gif
太空尺原理非常簡單:通過ARKit捕捉真實世界的兩個點,然后計算出兩個點之間的距離。所以可以直接下載代碼查看,下面列出重要實現(xiàn)步驟及代碼。
1, 是否劃線-狀態(tài)控制
class ViewController
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if !isMeasuring {
// 開始測量
isMeasuring = true
targetImageView.image = UIImage(named: "GreenTarget")
vectorStart = SCNVector3()
vectorEnd = SCNVector3()
} else {
// 測量結(jié)束
isMeasuring = false
if let line = currentLine {
lines.append(line)
currentLine = nil
targetImageView.image = UIImage(named: "WhiteTarget")
}
}
}
2, 因為我們要時刻掃描劃線,所以實現(xiàn)代理方法:func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval)
。
class ViewController
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
DispatchQueue.main.async {
scanWorld()
}
}
后邊所有步驟及代碼幾乎都在scanWorld方法內(nèi)為其服務(wù)
3, 這里捕捉點,就通過屏幕的中心點捕捉吧!
class ViewController.scanWorld()
let point = view.center
let results = sceneView.hitTest(point, types: [.featurePoint])
guard let result = results.first else {
return
}
let transform = result.worldTransform
let worldPosition = SCNVector3Make(transform.columns.3.x, transform.columns.3.y, transform.columns.3.z)
4, 控制劃線狀態(tài)與劃線的邏輯:
- 在剛開啟劃線狀態(tài)時,通過步驟3中掃描的點作為線的終點,如果起點都還沒有確定,那么該點還是線的起點。
- 在確定起點時,創(chuàng)建Line對象
- 在確定終點時,更新線。因為劃線狀態(tài)沒有被切換時,終點一直在變,也就是前面創(chuàng)建的展示線的節(jié)點一直在變
class ViewController.scanWorld()
if isMeasuring {
// 確定線的起點,創(chuàng)建線條
if vectorStart == vectorZero {
vectorStart = worldPosition
currentLine = Line(sceneView: sceneView, startVector: vectorStart, unit: unit)
}
// 更新線條
currentLine?.update(to: worldPosition)
infoLabel.text = currentLine?.distance ?? "update line..."
}
extension SCNVector3: Equatable {
public static func == (lhs: SCNVector3, rhs: SCNVector3) -> Bool {
return (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z)
}
}
5, 上邊代碼創(chuàng)建Line不是關(guān)鍵,而是Line的更新:currentLine?.update(to: worldPosition)
- 此時worldPosition就表示currentLine的終點
- 由于線的終點變化,所以需要移除掉之前的線節(jié)點
- 通過起點與新的終點,創(chuàng)建并展示新的線節(jié)點
class Line:
var distance: String? {
let distanceX = startVector.x - endVector.x
let distanceY = startVector.y - endVector.y
let distanceZ = startVector.z - endVector.z
let value = sqrt((distanceX * distanceX) + (distanceY * distanceY) + (distanceZ * distanceZ))
return String(format:"%0.2f %@", value*unit.factor, unit.name)
}
func update(to vector: SCNVector3) {
lineNode?.removeFromParentNode()
endVector = vector
lineNode = startVector.line(to: endVector, color: color)
sceneView.scene.rootNode.addChildNode(lineNode!)
// 文字節(jié)點
text.string = distance // text是SCNText,是文字節(jié)點的幾何形
textNode.position = SCNVector3((startVector.x + vector.x) / 2.0 , (startVector.y + vector.y) / 2.0 ,(startVector.z + vector.z) / 2.0 )
endNode.position = vector
if endNode.parent == nil {
sceneView.scene.rootNode.addChildNode(endNode)
}
}
6, 通過兩點之間在3D場景劃線:startVector.line(to: endVector, color: color)
extension SCNVector3
func line(to vector: SCNVector3, color: UIColor) -> SCNNode {
let indices: [UInt32] = [0, 1]
let source = SCNGeometrySource(vertices: [self, vector])
let element = SCNGeometryElement(indices: indices, primitiveType: .line)
let geometry = SCNGeometry(sources: [source], elements: [element])
geometry.firstMaterial?.diffuse.contents = color
let node = SCNNode(geometry: geometry)
return node
}