Chapter 7: Building a Portal
大綱
骰子遊戲已經在上一章為最終篇了,接下來的四章會另外一個新的app為範例開始進行學習。Portal app可以是用來教育方面的AR App, 例如介紹宇宙中的太陽系之類。
摘要
The portal app
我們會做一個裝潢好的房子的虛擬門口,可以走進去這個虛擬房間並進行參觀。
Getting started
基本上UI目前只有3樣東西
messageLabel: 用來告訴使用者接下來要做哪些事情。
sessionStateLabel: 用來了解目前session的狀況,是否有中斷現象,例如走到一個很暗的地方。
sceneView: 用來繪製3D物件。
Note: ARKit只是用來camera中sensor data, 並不包含任何3D物件繪製動作。這些繪製動作是交給其他Framewrok處理,如SceneKit或SpiritKit。ARSCNView可以讓我們輕鬆整合ARKit的data到SceneKit中。
Setting up ARKit
這段內容之前就有講過,就是建立config加到session中。
func runSession() {
let configuration = ARWorldTrackingConfiguration.init()
configuration.planeDetection = .horizontal
configuration.isLightEstimationEnabled = true
sceneView?.session.run(configuration)
#if DEBUG
sceneView?.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
#endif
}
Plane detection and rendering
透過ARSCNViewDelegate可以獲得camera偵測到真實環境的資料
第一步先偵測平面
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// 所有delegate mathod都是在background thread執行,所以需要丟到Main thread進行UI更新
DispatchQueue.main.async {
if let planeAnchor = anchor as? ARPlaneAnchor {
#if DEBUG
let debugPlaneNode = createPlaneNode(center: planeAnchor.center, extent: planeAnchor.extent)
node.addChildNode(debugPlaneNode)
#endif
self.messageLabel?.text = "Tap on the detected horizontal plane to place the portal"
}
}
}
再過來就是隨著camera的真實環境的資料更新,我們也要同步更新3D物件,例如大小,位置等。
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
DispatchQueue.main.async {
if let planeAnchor = anchor as? ARPlaneAnchor, node.childNodes.count > 0 {
updatePlaneNode(node.childNodes[0], center: planeAnchor.center, extent: planeAnchor.extent)
}
}
}
接下是一些helper的內容,建立3D平面,更新平面。
func createPlaneNode(center: vector_float3, extent: vector_float3) -> SCNNode {
let plane = SCNPlane(width: CGFloat(extent.x), height: CGFloat(extent.z))
let planeMaterial = SCNMaterial()
planeMaterial.diffuse.contents = UIColor.yellow.withAlphaComponent(0.4)
plane.materials = [planeMaterial]
let planeNode = SCNNode(geometry: plane)
planeNode.position = SCNVector3Make(center.x, 0, center.z)
// SceneKit的defult是vertical,因此要轉90度變成平面
planeNode.transform = SCNMatrix4MakeRotation(-Float.pi / 2, 1, 0, 0)
return planeNode
}
func updatePlaneNode(_ node: SCNNode, center: vector_float3, extent: vector_float3) {
// 檢查是否為SCNPlane
let gemorty = node.geometry as? SCNPlane
// 根據傳進來anchor的extent訊息來更新gemorty大小
gemorty?.width = CGFloat(extent.x)
gemorty?.height = CGFloat(extent.z)
// 根據傳進來anchor的extent訊息來更新gemorty位置
node.position = SCNVector3Make(center.x, 0, center.z)
}
Last updated
Was this helpful?